数据包过滤和防火墙

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

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

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

注意

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

iptables规则是为ipvlan,macvlanhost联网。

Docker 和 iptables 链

filtertable 中,Docker 会将默认策略设置为DROP,并创建 遵循自定义iptables链:

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

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

nattable, Docker 创建链DOCKER并添加要实施的规则 伪装和端口映射。

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

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

匹配请求的原始 IP 和端口

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

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

$ 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 run -p 8080:80 [...]创建映射 在 Docker 主机上任何地址的端口 8080 与容器的 端口 80。来自容器的传出连接将使用 Docker 主机的 IP 地址。

限制与容器的外部连接

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

要仅允许特定 IP 或网络访问容器,请插入 negated 规则位于DOCKER-USERfilter 链。例如, 以下规则丢弃来自所有 IP 地址的数据包,但192.0.2.2:

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

您将需要更改ext_if以对应您的 host 的实际外部接口。您可以改为允许来自 源子网。以下规则仅允许从子网访问192.0.2.0/24:

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

最后,您可以使用--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

您可以组合-s--src-range-d--dst-range控制两者 源和目标。例如,如果 Docker 主机具有地址2001:db8:1111::22001:db8:2222::2中,您可以将规则设为特定于2001:db8:1111::2然后离开2001:db8:2222::2打开。

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

直接路由

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

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

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

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

默认值为nat,为每个 NAT 和伪装规则设置 发布 Container Port。带模式routed、无 NAT 或伪装规则 设置,但iptables仍会设置,以便仅发布容器 端口是可访问的。

routedmode 中,一个-p--publishport mapping 为 not used,主机地址仅用于决定是否申请 到 IPv4 或 IPv6 的映射。因此,当映射仅适用于routed模式,仅地址0.0.0.0::1,并且 Host 端口 不得给予。

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

创建适合 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,使用routed模式中,端口 80 将在容器的 IP 上打开 地址。端口 8080 不会在主机的 IP 地址上打开,并且 传出数据包将使用容器的 IP 地址。
  • 对于 IPv4,使用默认的nat模式,则容器的端口 80 将为 可通过主机 IP 地址上的端口 8080 访问,也可以直接访问。 源自容器的连接将使用 host 的 IP 地址。

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

$ 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 主机访问。为此,您可以 将守护程序配置为使用环回地址 (127.0.0.1) 代替。

警告

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

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

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

注意

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

默认网桥

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

{
  "ip": "127.0.0.1"
}

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

路由器上的 Docker

Docker 为FORWARDchain 到DROP.这将阻止 您的 Docker 主机充当路由器。

如果您希望您的系统充当路由器,则必须添加显式ACCEPTrules 添加到DOCKER-USER链。例如:

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

阻止 Docker作 iptables

可以设置iptablesip6tableskeys 到falsedaemon 配置中,但 此选项不适用于大多数用户。它可能会破裂 Docker Engine 的容器联网。

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

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

与 firewalld 集成

如果您使用iptables选项设置为true,并且 firewalld 在您的系统上启用了 Docker 自动创建一个firewalld区域调用docker、目标ACCEPT.

Docker 创建的所有网络接口(例如docker0) 插入 到docker区。

Docker 还会创建一个名为docker-forwarding这允许 转发自ANYzone 设置为docker区。

Docker 和 ufw

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

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