数据包过滤和防火墙
在 Linux 上,Docker 会创建iptables
和ip6tables
实现网络的规则
隔离、端口发布和筛选。
因为这些规则是 Docker bridge 正常运行所必需的 网络,则不应修改 Docker 创建的规则。
但是,如果您在公开到 Internet 的主机上运行 Docker,您将 可能想要添加 iptables 策略来防止对 容器或其他服务。本页介绍了如何作 为了实现这一目标,以及您需要注意的注意事项。
注意
Docker 创建
iptables
桥接网络的规则。不
iptables
规则是为ipvlan
,macvlan
或host
联网。
Docker 和 iptables 链
在filter
table 中,Docker 会将默认策略设置为DROP
,并创建
遵循自定义iptables
链:
DOCKER-USER
- 将在规则之前处理的用户定义规则的占位符
在
DOCKER
链。
- 将在规则之前处理的用户定义规则的占位符
在
DOCKER
- 确定不属于已建立的 应根据端口转发配置接受连接 正在运行的容器。
DOCKER-ISOLATION-STAGE-1
和DOCKER-ISOLATION-STAGE-2
- 将 Docker 网络彼此隔离的规则。
在FORWARD
chain,Docker 会添加传递不相关数据包的规则
与这些自定义链建立连接,以及接受的规则
属于已建立连接的数据包。
在nat
table, Docker 创建链DOCKER
并添加要实施的规则
伪装和端口映射。
在 Docker 的规则之前添加 iptables 策略
被这些自定义链中的规则接受或拒绝的数据包将不会
被附加到FORWARD
链。所以,要添加
其他规则来筛选这些数据包,请使用DOCKER-USER
链。
匹配请求的原始 IP 和端口
当数据包到达DOCKER-USER
链,他们已经通过
目标网络地址转换 (DNAT) 筛选器。这意味着iptables
您使用的标志只能匹配
器皿。
如果要根据网络中的原始 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 run -p 8080:80 [...]
创建映射
在 Docker 主机上任何地址的端口 8080 与容器的
端口 80。来自容器的传出连接将使用
Docker 主机的 IP 地址。
限制与容器的外部连接
默认情况下,允许所有外部源 IP 连接到具有 已发布到 Docker 主机的地址。
要仅允许特定 IP 或网络访问容器,请插入
negated 规则位于DOCKER-USER
filter 链。例如,
以下规则丢弃来自所有 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::2
和2001: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
仍会设置,以便仅发布容器
端口是可访问的。
在routed
mode 中,一个-p
或--publish
port mapping 为
not used,主机地址仅用于决定是否申请
到 IPv4 或 IPv6 的映射。因此,当映射仅适用于routed
模式,仅地址0.0.0.0
或::1
,并且 Host 端口
不得给予。
映射的容器端口, 在nat
或routed
模式,可从
任何远程地址(如果在网络中设置了路由),除非
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 为FORWARD
chain 到DROP
.这将阻止
您的 Docker 主机充当路由器。
如果您希望您的系统充当路由器,则必须添加显式ACCEPT
rules 添加到DOCKER-USER
链。例如:
$ iptables -I DOCKER-USER -i src_if -o dst_if -j ACCEPT
阻止 Docker作 iptables
可以设置iptables
或ip6tables
keys 到false
在 daemon 配置中,但
此选项不适用于大多数用户。它可能会破裂
Docker Engine 的容器联网。
所有容器的所有端口都可以从网络访问,并且没有 将从 Docker 主机 IP 地址映射。
不可能完全阻止 Docker 创建iptables
规则,而事后创建规则则非常复杂,甚至超越了
这些说明的范围。
与 firewalld 集成
如果您使用iptables
选项设置为true
,并且 firewalld 在您的系统上启用了 Docker
自动创建一个firewalld
区域调用docker
、目标ACCEPT
.
Docker 创建的所有网络接口(例如docker0
) 插入
到docker
区。
Docker 还会创建一个名为docker-forwarding
这允许
转发自ANY
zone 设置为docker
区。
Docker 和 ufw
Uncomplicated Firewall (ufw) 是 Debian 和 Ubuntu 附带的前端, 它还允许您管理防火墙规则。Docker 和 ufw 以多种方式使用 iptables 这使得它们彼此不兼容。
当您使用 Docker 发布容器的端口时,进出该端口的流量
容器在通过 UFW 防火墙设置之前被转移。
Docker 在nat
表,这意味着数据包
在到达INPUT
和OUTPUT
ufw 使用的链。
数据包在应用防火墙规则之前被路由,
有效地忽略了您的防火墙配置。