Docker 使用“-p <port>:<port>”时忽略 iptable 规则

Docker ignores iptable rules when using "-p <port>:<port>"

几天前才意识到 Docker 似乎绕过了我的 iptable 规则。我对 Docker 和 iptables 的经验并不丰富。最后几天尝试了很多不同的东西。还看到最近的 docker 版本有很大的变化,有一个特殊的 DOCKER-chain 应该允许我这样做。但是不确定我做错了什么,但它从来没有像我期望的那样。

所以我想要的很简单。我希望它表现得像预期的那样。如果我有一个 ACCEPT-Rule 可以通过,否则它会被阻止。

我的 iptable 原来是这样的(所以在我多次尝试失败之前):

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [779:162776]
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -s 1.2.3.4 -p tcp -m tcp --dport 123 -j ACCEPT
-A INPUT -j DROP
COMMIT

希望它完全符合我的要求。只允许访问端口 22 和 80,还允许来自 ip 1.2.3.4 的端口 123。但是,如果我使用“-p 123:123”创建容器,每个人都可以访问它。任何人都可以帮助我并告诉我如何更改上述文件吗?

谢谢!

Docker-版本:1.6.2

编辑:

离开最初我的不同尝试不使问题过于复杂。但是,至少添加其中一个可能会有所帮助。

*nat
:PREROUTING ACCEPT [319:17164]
:INPUT ACCEPT [8:436]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [16:960]
:DOCKER - [0:0]
COMMIT


*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [779:162776]
:DOCKER - [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A DOCKER -s 1.2.3.4 -p tcp -m tcp --dport 123 -j ACCEPT
-A DOCKER -j DROP
-A INPUT -j DROP
COMMIT

以上那种作品。然而搞定之后还有很多其他问题。例如,我是否遇到容器链接问题、DNS 不再工作等等。所以最终添加了很多额外的规则来解决这些问题,但我从来没有达到它正常运行的状态。所以我想那里有更好更简单的解决方案。

解决方案:

最终或多或少做了 larsks 所说的事情。只是没有将其添加到 FORWARD 链中,而是将其添加到 DOCKER 链中。 FORWARD 链的问题是 Docker 当它在第一个位置重新启动时会在其中添加它的东西。这导致我的规则被推低并且没有任何效果。然而,对于 DOCKER 链,似乎 Docker 仅附加了额外的规则,因此我的规则仍然有效。因此,当我保存我的规则然后重新启动服务器时,一切仍然正常。

所以现在它看起来或多或少是这样的:

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [779:162776]
:DOCKER - [0:0]
# That I can access from IP 1.2.3.4
-A DOCKER -s 1.2.3.4/32 -p tcp -m tcp --dport 123 -j ACCEPT
# That I can access from other Docker containers
-A DOCKER -o docker0 -p tcp -m tcp --dport 123 -j ACCEPT
# Does not allow it for anything else
-A DOCKER -p tcp --dport 123 -j DROP

-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -j DROP
COMMIT

您的 iptables 配置现在看起来有点损坏,好像您在某个时候清除了它而没有重新启动 Docker。例如,您在 filternat table 中都有一个 DOCKER 链可用,但没有引用它的规则,因此放置在该链中的规则将没有影响。

一般来说,如果您想实施影响您的 Docker 容器的 iptables 规则,它们需要进入 filter [的 FORWARD 链 table。每个容器都有自己的 IP 地址,这意味着您的主机只是接受数据包,然后 FORWARD 将它们发送到容器地址。

INPUT 链中的规则仅适用于最终目的地地址位于主机全局网络命名空间中的接口上的数据包。

但是,我不确定 iptables 是否真的是您的问题。

如果您尝试在容器中公开服务以便其他系统可以使用它们,则需要使用 -p 标志将这些端口发布到 docker run。你可以阅读更多相关信息 在文档的 this section 中。

如果你想用一个具体的例子来更新你的问题,我可以提供更有针对性的答案。

更新

确实,当您使用 -p 发布容器端口时,它通常可用于任何源 IP 地址。为了限制对已发布端口的访问,您需要向 FORWARD 链添加新规则。例如,如果我启动一个网络服务器:

docker run --name web -p 80:8080 larsks/mini-httpd

容器中的 Web 服务器现在可以在我的主机上的端口 8080 上使用。如果我想阻止对该端口的访问,我需要在 FORWARD 链中插入一条规则,以阻止对容器 ip 上的端口 80 的访问。所以首先我需要容器 ip 地址:

$ web_ip=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' web)
$ echo $web_ip
172.17.0.5

我在 FORWARD 链中创建的规则需要 docker 创建的规则之前,所以我需要指定一个明确的位置:

iptables -I FORWARD 1 -d $web_ip -p tcp --dport 80 \! -s 192.168.1.10 -j DROP

这将阻止来自 192.168.1.10 以外主机的所有流量。

如果您希望规则应用于所有容器,而不是特定容器,您可以将其绑定到 docker0 接口而不是特定 ip 地址:

-A FORWARD -o docker0 -p tcp --dport 80 \! -s 192.168.1.10 -j DROP

这将禁止访问 任何 容器上的端口 80。

我不是 iptables 专家,但我知道如果你 运行 带有 -p 127.0.0.1:123:123 的容器,那么端口不会在所有接口上公开,只是在环回上公开。

Ended up doing more or less exactly what larsks said. Just did not add it to the FORWARD chain, I added it to the DOCKER chain instead.

我在文档中找到了相同的内容:https://docs.docker.com/v1.5/articles/networking/#the-world

Docker will not delete or modify any pre-existing rules from the DOCKER filter chain. This allows the user to create in advance any rules required to further restrict access to the containers.

Docker's forward rules permit all external source IPs by default. To allow only a specific IP or network to access the containers, insert a negated rule at the top of the DOCKER filter chain. For example, to restrict external access such that only source IP 8.8.8.8 can access the containers, the following rule could be added:

$ iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP

给定: Debian stretch,docker 18.06,以及通过

创建的 docker 进程
docker run ... -p 5678:1234 ...

必需: 对 docker 容器的访问仅限于 多个 外部子网。

解决方案:(使用一些示例子网)

iptables -I DOCKER-USER                  -p tcp --dport 1234 -j REJECT
iptables -I DOCKER-USER -s 18.204.0.0/16 -p tcp --dport 1234 -j RETURN
iptables -I DOCKER-USER -s 34.192.0.0/16 -p tcp --dport 1234 -j RETURN
iptables -I DOCKER-USER -s 35.153.0.0/16 -p tcp --dport 1234 -j RETURN
iptables -I DOCKER-USER -s 13.56.63.0/24 -p tcp --dport 1234 -j RETURN

使用 iptables-save 保存更改的规则:

iptables-save > /etc/iptables/rules.v4

结果:

iptables -L DOCKER-USER -n -v --line-numbers

Chain DOCKER-USER (1 references)
pkts bytes target     prot opt in     out     source               destination
0     0 RETURN     tcp  --  *      *       13.56.63.0/24        0.0.0.0/0            tcp dpt:1234
0     0 RETURN     tcp  --  *      *       35.153.0.0/16        0.0.0.0/0            tcp dpt:1234
0     0 RETURN     tcp  --  *      *       34.192.0.0/16        0.0.0.0/0            tcp dpt:1234
0     0 RETURN     tcp  --  *      *       18.204.0.0/16        0.0.0.0/0            tcp dpt:1234
0     0 REJECT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:1234 reject-with icmp-port-unreachable

背景一:规则

所有规则都按照当前 docker 文档的建议添加到链 DOCKER-USER 中。

第一条规则针对 REJECT 并将作为最后一条规则结束,因为其他规则添加在顶部(没有位置编号的选项 -I 对应于在位置 1 添加规则).所有目的端口1234达到此规则的包裹都将被拒绝。

其他规则以 RETURN 为目标,即目标端口为 1234 且来自给定子网之一的源 IP 的包将返回到调用链,即 FORWARD 链。

iptables -L FORWARD -n -v
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target     prot opt in     out     source               destination
16471 4568K DOCKER-USER  all  --  *      *       0.0.0.0/0            0.0.0.0/0
16413 4565K DOCKER-ISOLATION-STAGE-1  all  --  *      *       0.0.0.0/0            0.0.0.0/0
7173 2060K ACCEPT     all  --  *      docker0  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
45  2340 DOCKER     all  --  *      docker0  0.0.0.0/0            0.0.0.0/0

来自 FORWARD 链,它将由 DOCKER 链处理,并根据需要转发到 docker 容器:

iptables -L DOCKER -n -v
Chain DOCKER (1 references)
pkts bytes target     prot opt in     out     source               destination
45  2340 ACCEPT     tcp  --  !docker0 docker0  0.0.0.0/0            172.17.0.2           tcp dpt:1234

背景二:端口值

我们不在转发规则中使用外部端口值 5678,因为目标端口是通过 docker 自动创建的规则更改的,并在执行转发链之前应用。请参阅 /etc/iptables/rules.v4:

的顶部部分
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
...
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 5678 -j DNAT --to-destination 172.17.0.2:1234

要在 docker 容器的已发布端口上使用 iptables,您需要结合使用以下内容:

  • DOCKER-USER table: docker 将此 table 用于影响容器的 iptables 规则,并专门为用户提供的规则保留docker 引擎在重新启动时不会被覆盖。
  • conntrack:端口转发可以在一个端口上发布,转发到容器中的另一个端口。您可以让多个容器全部侦听端口 80,并在主机上使用不同的已发布端口。

要使用这些,生成的 iptables 规则如下所示:

iptables -I DOCKER-USER -i eth0 -s 10.0.0.0/24 -p tcp \
  -m conntrack --ctorigdstport 8080 -j ACCEPT
iptables -I DOCKER-USER -i eth0 ! -s 10.0.0.0/24 -p tcp \
  -m conntrack --ctorigdstport 8080 -j DROP

这处理对已发布端口 8080/tcp 的请求(在主机上,容器可以侦听 80 或任何其他端口),并且只接受来自 10.0.0.0/24 子网的请求。该子网之外的所有内容都将被删除。

请注意 DOCKER-USER table 有一个默认规则立即 return,因此所有更改都应插入 table 中的默认规则之前。