数据包过滤和防火墙

在 Linux 上,Docker 会创建规则并实施网络 隔离、端口发布和筛选。iptablesip6tables

因为这些规则是 Docker bridge 正常运行所必需的 网络,则不应修改 Docker 创建的规则。

但是,如果您在公开到 Internet 的主机上运行 Docker,您将 可能想要添加 iptables 策略来防止对 容器或其他服务。本页介绍了如何操作 为了实现这一目标,以及您需要注意的注意事项。

注意

Docker 为桥接网络创建规则。iptables

没有为 或 networking 创建规则。iptablesipvlanmacvlanhost

Docker 和 iptables 链

在表中,Docker 将默认策略设置为 ,并创建 以下定制链:filterDROPiptables

  • DOCKER-USER
    • 将在规则之前处理的用户定义规则的占位符 在链中。DOCKER
  • DOCKER
    • 确定不属于已建立的 应根据端口转发配置接受连接 正在运行的容器。
  • DOCKER-ISOLATION-STAGE-1DOCKER-ISOLATION-STAGE-2
    • 将 Docker 网络彼此隔离的规则。

在链中,Docker 添加了传递不相关数据包的规则 与这些自定义链建立连接,以及接受的规则 属于已建立连接的数据包。FORWARD

在表中,Docker 创建链并添加要实现的规则 伪装和端口映射。natDOCKER

在 Docker 的规则之前添加 iptables 策略

被这些自定义链中的规则接受或拒绝的数据包将不会 被附加到链中的用户定义的规则看到。所以,要添加 要筛选这些数据包的其他规则,请使用 Chain.FORWARDDOCKER-USER

匹配请求的原始 IP 和端口

当数据包到达链时,它们已经通过 目标网络地址转换 (DNAT) 筛选器。这意味着您使用的标志只能匹配 器皿。DOCKER-USERiptables

如果要根据网络中的原始 IP 和端口匹配流量 请求,则必须使用 Conntrack iptables 扩展。 例如:

$ sudo iptables -I DOCKER-USER -p tcp -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
$ sudo iptables -I DOCKER-USER -p tcp -m conntrack --ctorigdst 198.51.100.2 --ctorigdstport 80 -j ACCEPT

重要

使用该扩展可能会导致性能下降。conntrack

港口发布和映射

默认情况下,对于 IPv4 和 IPv6,守护程序会阻止对未阻止 已发布。已发布的容器端口将映射到主机 IP 地址。 为此,它使用 iptables 来执行网络地址转换 (NAT)、 端口地址转换 (PAT) 和伪装。

例如,创建一个映射 在 Docker 主机上任何地址的端口 8080 与容器的 端口 80。来自容器的传出连接将使用 Docker 主机的 IP 地址。docker run -p 8080:80 [...]

限制与容器的外部连接

默认情况下,允许所有外部源 IP 连接到具有 已发布到 Docker 主机的地址。

要仅允许特定 IP 或网络访问容器,请插入 在过滤器链的顶部否定规则。例如, 以下规则会丢弃来自所有 IP 地址的数据包,但 :DOCKER-USER192.0.2.2

$ iptables -I DOCKER-USER -i ext_if ! -s 192.0.2.2 -j DROP

您需要进行更改以与您的 host 的实际外部接口。您可以改为允许来自 源子网。以下规则仅允许从子网访问 :ext_if192.0.2.0/24

$ iptables -I DOCKER-USER -i ext_if ! -s 192.0.2.0/24 -j DROP

最后,您可以使用 (请记住在使用 或 时也要添加) 来接受 IP 地址范围 (请记住,在使用 or 时也要添加):--src-range-m iprange--src-range--dst-range

$ iptables -I DOCKER-USER -m iprange -i ext_if ! --src-range 192.0.2.1-192.0.2.3 -j DROP

您可以将 OR 与 OR 结合使用,以控制两者 源和目标。例如,如果 Docker 主机具有地址 和 ,则可以使规则特定于 并保持开放状态。-s--src-range-d--dst-range2001:db8:1111::22001:db8:2222::22001:db8:1111::22001:db8:2222::2

iptables很复杂。Netfilter.org HOWTO 中提供了更多信息。

直接路由

端口映射可确保已发布的端口可在主机的 网络地址,这些地址可能可路由给任何外部 客户。通常不会在主机的网络中为容器设置路由 主机中存在的地址。

但是,特别是对于 IPv6,您可能更愿意避免使用 NAT,而是使用 安排到容器地址的外部路由。

要从 Docker 主机外部访问桥接网络上的容器, 您必须通过 Docker 上的地址设置到桥接网络的路由 主机。这可以使用静态路由、边界网关协议来实现 (BGP) 或适用于您的网络的任何其他方式。

网桥网络驱动程序具有 options 和 。com.docker.network.bridge.gateway_mode_ipv6=<nat|routed>com.docker.network.bridge.gateway_mode_ipv4=<nat|routed>

默认值为 ,NAT 并为每个 NAT 设置伪装规则 发布 Container Port。使用 mode ,无 NAT 或伪装规则 已设置,但仍会进行设置,以便仅发布容器 端口是可访问的。natroutediptables

在模式下,or 端口映射中的主机端口为 not used,主机地址仅用于决定是否申请 到 IPv4 或 IPv6 的映射。因此,当映射仅适用于模式时,仅允许地址 或 主机端口 不得给予。routed-p--publishrouted0.0.0.0::1

映射的容器端口 (in 或 mode) 可从 任何远程地址(如果在网络中设置了路由),除非 Docker 主机的防火墙还有其他限制。natrouted

创建适合 IPv6 直接路由的网络,并启用 NAT 对于 IPv4:

$ docker network create --ipv6 --subnet 2001:db8::/64 -o com.docker.network.bridge.gateway_mode_ipv6=routed mynet

创建具有已发布端口的容器:

$ docker run --network=mynet -p 8080:80 myimage

然后:

  • 对于 IPv4 和 IPv6,将仅打开容器端口 80。它是可访问的 从任何地方,如果有到容器地址的路由,并访问 未被主机的防火墙阻止。
  • 对于 IPv6,使用模式,端口 80 将在容器的 IP 上打开 地址。端口 8080 不会在主机的 IP 地址上打开,并且 传出数据包将使用容器的 IP 地址。routed
  • 对于 IPv4,使用默认模式时,容器的端口 80 将为 可通过主机 IP 地址上的端口 8080 访问,也可以直接访问。 源自容器的连接将使用 host 的 IP 地址。nat

在 中,此端口映射将显示如下。请注意, IPv6 没有,因为它正在使用模式:docker inspectHostPortrouted

$ docker container inspect <id> --format "{{json .NetworkSettings.Ports}}"
{"80/tcp":[{"HostIp":"0.0.0.0","HostPort":"8080"},{"HostIp":"::","HostPort":""}]}

或者,要使映射仅 IPv6,请禁用对 容器的 80 端口,请使用未指定的 IPv6 地址,并且不要 包括主机端口号:[::]

$ docker run --network mynet -p '[::]::80'

设置容器的默认绑定地址

默认情况下,当容器的端口映射时没有任何特定主机 address,Docker 守护进程将已发布的容器端口绑定到所有主机 地址 ( 和 )。0.0.0.0[::]

例如,以下命令将端口 8080 发布到所有网络 主机上的接口(IPv4 和 IPv6 地址)可能 使它们对外界可用。

docker run -p 8080:80 nginx

您可以更改已发布容器端口的默认绑定地址,以便 默认情况下,它们只能由 Docker 主机访问。为此,您可以 将守护进程配置为使用 loopback address () 来代替。127.0.0.1

警告

同一 L2 Segment 中的主机(例如,连接到同一 网络交换机)可以访问发布到 localhost 的端口。 有关更多信息,请参阅 moby/moby#45610

要为用户定义的桥接网络配置此设置,请使用 驱动程序选项com.docker.network.bridge.host_binding_ipv4

$ docker network create mybridge \
  -o "com.docker.network.bridge.host_binding_ipv4=127.0.0.1"

注意

  • 将默认绑定地址设置为 表示没有主机的端口绑定 指定的地址将适用于主机上的任何 IPv6 地址。但是,是指任何 IPv4 或 IPv6 地址。::0.0.0.0
  • 更改默认绑定地址对 Swarm 服务没有任何影响。 Swarm 服务始终暴露在网络接口上。0.0.0.0

默认网桥

要为默认桥接网络设置默认绑定,请在配置文件中配置密钥:"ip"daemon.json

{
  "ip": "127.0.0.1"
}

这会将已发布容器的默认绑定地址更改为 默认桥接网络上的端口。 重新启动守护程序以使此更改生效。 或者,您可以在启动守护程序时使用该标志。127.0.0.1dockerd --ip

路由器上的 Docker

Docker 将链的策略设置为 .这将阻止 您的 Docker 主机充当路由器。FORWARDDROP

如果您希望您的系统充当路由器,则必须向链中添加显式规则。例如:ACCEPTDOCKER-USER

$ iptables -I DOCKER-USER -i src_if -o dst_if -j ACCEPT

阻止 Docker 操作 iptables

可以在 daemon 配置中将 或 键设置为 ,但是 此选项不适用于大多数用户。它可能会破裂 Docker Engine 的容器联网。iptablesip6tablesfalse

所有容器的所有端口都可以从网络访问,并且没有 将从 Docker 主机 IP 地址映射。

完全阻止 Docker 创建规则是不可能的,事后创建规则非常复杂,甚至超越 这些说明的范围。iptables

与 firewalld 集成

如果运行的 Docker 将选项设置为 ,并且系统上启用了 firewalld,则 Docker 使用 Target 自动创建名为 的区域 。iptablestruefirewallddockerACCEPT

插入 Docker 创建的所有网络接口(例如 ) 进入区域。docker0docker

Docker 还创建了一个名为 从区域转发到区域。docker-forwardingANYdocker

Docker 和 ufw

Uncomplicated Firewall (ufw) 是 Debian 和 Ubuntu 附带的前端, 它还允许您管理防火墙规则。Docker 和 ufw 以多种方式使用 iptables 这使得它们彼此不兼容。

当您使用 Docker 发布容器的端口时,进出该端口的流量 容器在通过 UFW 防火墙设置之前被转移。 Docker 在表中路由容器流量,这意味着数据包 在到达 UFW 使用的 和 链之前被转移。 数据包在应用防火墙规则之前被路由, 有效地忽略了您的防火墙配置。natINPUTOUTPUT