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

GGG*_*rce 33 iptables docker

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

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

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

  • 在 eth0 上托管公共 IP 80.80.80.80
  • 在 eth1 上托管私有 IP 192.168.1.10
  • docker run -d -p 3306:3306 mysql
  • 阻止与主机/容器 3306 的所有连接,主机 4.4.4.4 和 8.8.8.8 除外

我很高兴将容器仅绑定到本地 ip 地址,但需要有关如何正确设置 iptables 转发规则的说明,以便在 docker 进程和主机重新启动后继续存在。

谢谢!

Sys*_*dox 39

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

  1. 为避免您的规则被 docker 破坏,请使用DOCKER-USER
  2. Docker 在PREROUTING表链中进行端口映射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
Run Code Online (Sandbox Code Playgroud)

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

  • 注意默认的`DOCKER-USER`表包含条目:`-A DOCKER-USER -j RETURN`,如果你使用`-A`,它将在上述之前运行。一种解决方案是使用“-I”以相反的顺序在头部插入规则。 (5认同)
  • @BMitch 或者[甚至更好](https://unrouted.io/2017/08/15/docker-firewall/),在新的“FILTERS”链中添加所有规则,然后“-I”插入新规则(例如你说的),跳转到它:“-I INPUT -j FILTERS”和“-I DOCKER-USER -i eth0 -j FILTERS” (2认同)

小智 8

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

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

-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
Run Code Online (Sandbox Code Playgroud)

现在 MySQL 的端口被阻止从外部访问 (eth0) 甚至认为 docker 为世界打开了端口。(这些规则假设您的外部接口是 eth0。)

最终,您将不得不先清理 iptables,然后重新启动 docker 服务,如果您像我一样试图锁定端口而把它弄得一团糟。

  • 请注意,如果您在 DOCKER-USER 中使用“--dport”,则它必须与容器服务的“内部”IP 匹配,“而不是”暴露的端口。这些通常匹配,但并不总是匹配,这很容易与其他服务发生冲突,所以我仍然认为这个 DOCKER-USER 解决方案是不成熟的。 (3认同)

GGG*_*rce 6

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

\n\n

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

\n\n
\n

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

\n
\n\n

我最终做的是:

\n\n
iptables -I DOCKER -i eth0 -s 8.8.8.8 -p tcp --dport 3306 -j ACCEPT\niptables -I DOCKER -i eth0 -s 4.4.4.4 -p tcp --dport 3306 -j ACCEPT\niptables -I DOCKER 3 -i eth0 -p tcp --dport 3306 -j DROP\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n

  • 如果您执行“iptables -vnL DOCKER”,则目标端口是容器内的所有端口。如果我理解正确,那就意味着上述规则只会影响容器内的端口“3306” - 也就是说,如果您要“-p 12345:3306”您的容器,您的规则仍然是锁定所需的规则关闭访问(即“--dport 12345”不起作用),因为 DOCKER 链的 ACCEPT 规则是 NAT 后的。 (2认同)
  • DOCKER链不应该由用户直接操作!为此使用 DOCKER-USER 链。检查已接受的答案。 (2认同)

Col*_*inM 5

更新:DOCKER-USER虽然这个答案仍然有效,但 @SystemParadox与 结合使用的答案--ctorigdstport更好。

这是一个在重新启动之间保持良好状态的解决方案,并允许您影响暴露的端口而不是内部端口。

iptables -t mangle -N DOCKER-mysql
iptables -t mangle -A DOCKER-mysql -s 22.33.44.144/32 -j RETURN
iptables -t mangle -A DOCKER-mysql -s 22.33.44.233/32 -j RETURN
iptables -t mangle -A DOCKER-mysql -j DROP
iptables -t mangle -A PREROUTING -i eth0 -p tcp -m tcp --dport 3306 -j DOCKER-mysql
Run Code Online (Sandbox Code Playgroud)

我构建了一个 Docker 映像,它使用此方法自动为您管理 iptables,使用环境变量或动态使用 etcd(或两者):

https://hub.docker.com/r/colinmollenhour/confd-firewall/