使用 iptables 限制与 docker 容器的外部连接的步骤?

使用 iptables 限制与 docker 容器的外部连接的步骤?

我的目标是将对 docker 容器的访问限制为仅几个公共 IP 地址。是否有一个简单、可重复的过程来实现我的目标?在使用 Docker 的默认选项时仅了解 iptables 的基础知识,我发现这非常困难。

我想运行一个容器,使其对公共互联网可见,但只允许来自选定主机的连接。我希望设置默认的 INPUT 策略 REJECT,然后只允许来自我的主机的连接。但是 Docker 的 NAT 规则和链会妨碍我,导致我的 INPUT 规则被忽略。

有人可以提供一个根据以下假设如何实现我的目标的例子吗?

  • eth0 上的主机公网 IP 80.80.80.80
  • eth1 上的主机私有 IP 192.168.1.10
  • docker run -d -p 3306:3306 mysql
  • 阻止除主机 4.4.4.4 和 8.8.8.8 之外的所有到主机/容器 3306 的连接

我很乐意将容器绑定到仅本地 IP 地址,但需要有关如何正确设置 iptables 转发规则的说明,这些规则在 docker 进程和主机重启后仍然有效。

谢谢!

答案1

使用 Docker 的防火墙规则时需要记住两件事:

  1. 为了避免你的规则被docker破坏,请使用DOCKER-USER
  2. PREROUTINGDocker 在表的链中进行端口映射nat。这发生在filter规则之前,因此--dest--dport将看到容器的内部 IP 和端口。要访问原始目标,您可以使用-m conntrack --ctorigdstport

例如:

iptables -A DOCKER-USER -i eth0 -s 8.8.8.8 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i eth0 -s 4.4.4.4 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j DROP

注意:如果没有--ctdir ORIGINAL,这也会匹配从容器到其他服务器的 3306 端口的连接返回的回复数据包,这几乎肯定不是您想要的!如果您的第一条规则和我一样是 ,那么您就不需要这个,-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT因为它将处理所有回复数据包,但无论如何,仍然使用会更安全--ctdir ORIGINAL

答案2

Docker v.17.06 中有一个名为 DOCKER-USER 的新 iptables 链。这个是用于您的自定义规则:https://docs.docker.com/network/iptables/

与链式 DOCKER 不同,它不会在构建/启动容器时重置。因此,您可以在安装 docker 和启动容器之前将这些行添加到 iptables 配置/脚本中,以配置服务器:

-N DOCKER
-N DOCKER-ISOLATION
-N DOCKER-USER
-A DOCKER-ISOLATION -j RETURN
-A DOCKER-USER -i eth0 -p tcp -m tcp --dport 3306 -j DROP
-A DOCKER-USER -j RETURN

现在,即使 docker 向全世界开放了该端口,MySQL 的端口也被阻止从外部访问(eth0)。(这些规则假设您的外部接口是 eth0。)

最后,如果你像我一样试图锁定端口而弄得太乱,那么你必须先清理 iptables 并重新启动 docker 服务。

答案3

更新:虽然该解决方案在 2015 年有效,但它已不再是正确的方法。

答案似乎在 Docker 的文档中https://docs.docker.com/articles/networking/#the-world

Docker 的转发规则默认允许所有外部源 IP。若要仅允许特定 IP 或网络访问容器,请在 DOCKER 过滤器链的顶部插入否定规则。例如,若要限制外部访问,以便只有源 IP 8.8.8.8 可以访问容器,可以添加以下规则:iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP

我最终做的是:

iptables -I DOCKER -i eth0 -s 8.8.8.8 -p tcp --dport 3306 -j ACCEPT
iptables -I DOCKER -i eth0 -s 4.4.4.4 -p tcp --dport 3306 -j ACCEPT
iptables -I DOCKER 3 -i eth0 -p tcp --dport 3306 -j DROP

我没有触碰--iptables--icc选项。

答案4

基于@SystemParadox 的出色回答,我想防止全部流量(TCP 和 UDP)来自全部外部主机的已发布端口全部运行容器,因为我将反向代理到我实际想要从外部访问的容器端口。

对我来说,实现这一目标的规则是:

iptables -I DOCKER-USER -i eth0 -m conntrack --ctdir ORIGINAL -j DROP

这会将规则插入到DOCKER-USER链的顶部。请根据外部网络接口的实际名称或链中的所需位置进行相应调整(使用-I DOCKER-USER <number>)。

模块conntrackctdir这里似乎很重要,因为我遇到了一些问题,docker-composed 容器在使用没有它们的变体时无法访问外部世界。例如:iptables -I DOCKER-USER -i eth0 ! -s 127.0.0.1 -j DROP,由 Jeff Geerling 在博客文章从 2020 年开始,其本身来源于官方文档,阻止我的容器与外部 IP 通信。

相关内容