如何在 docker 上使用 iptables 来实现从容器连接到互联网?

如何在 docker 上使用 iptables 来实现从容器连接到互联网?

我们有一个从传统服务迁移到docker的系统。我们只想在生产环境中使用防火墙,因此我们希望iptables配置只在服务器中,而不是在docker的配置中。

我们有以下要求:

  • 服务器之间可以互相通信。
  • 您可以 ping 服务器
  • 你可以通过 SSH
  • 您可以通过某些端口访问服务器的某些网络服务。

经过几个小时/几天的阅读后,我得到了以下配置:

# Delete old entries if any
iptables -F INPUT
iptables -F DOCKER-USER
iptables -F OUTPUT

# Set firewall 
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # Allow established connections
iptables -A INPUT -i lo -j ACCEPT # Allow localhost communication
iptables -A OUTPUT -o lo -j ACCEPT # Allow output to the internet from localhost
iptables -A INPUT -p icmp --icmp-type 8 -j ACCEPT # Allow ICMP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT # Allow SSH
iptables -A INPUT -j DROP # Drop eveything else

# Docker specific, allow connections to the ports of the web services:
iptables -A DOCKER-USER -i ens192 -s 192.168.69.0/24 -p tcp -m conntrack --ctorigdstport 8080 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i ens192 -s 192.168.69.0/24 -p tcp -m conntrack --ctorigdstport 9000 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i ens192 -s 192.168.69.0/24 -p tcp -m conntrack --ctorigdstport 19900 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i docker0 -j ACCEPT # Allow input from other containers
iptables -A DOCKER-USER -i ens192 -j DROP # Drop all access to the containers through default interface

在此刻:

  • 如果我不在该子网中,我就无法访问网络服务。
  • 我可以 ping 通并且可以通过 ssh 连接。
  • 容器之间可以互相通信。因此,大多数要求都得到了满足。

但是现在当我们尝试从容器访问互联网时,它无法访问。(例如 ping 到 google 或本地网络)。但是从本地主机可以访问。

我尝试了很多选项,例如以下多种组合:

iptables -A DOCKER-USER -o docker0 -j ACCEPT
iptables -A OUTPUT -o docker0 -j ACCEPT
iptables -A DOCKER-USER -o ens192 -j ACCEPT

但它们似乎都不允许我从 docker 容器中 ping 东西。

答案1

我认为你想得太多了,因为docker它带有一些合理的默认配置,并且还会自动管理。例如,在默认设置中,容器无法通过外部网络访问,但它们可以自己连接到互联网。

因此你应该EXPOSE将容器的相关端口绑定到主机的外部 IP 上,或者绑定到主机的环回接口,然后将容器视为在主机上运行的普通服务

例如,如果您正在运行 Web 服务并将docker其绑定到相应的主机端口:

docker run -p 80:80 -p 443:443 ...

然后配置您的iptables以将所需端口和DROP其他所有内容列入白名单:

iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # Allow established connections
iptables -A INPUT -p icmp --icmp-type 8 -j ACCEPT # Allow ICMP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT # Allow SSH
iptables -A INPUT -p tcp --dport 80 -j ACCEPT # Allow HTTP
iptables -A INPUT -p tcp --dport 443 -j ACCEPT # Allow HTTPS
iptables -A INPUT -j DROP # Drop eveything else

关于内部网络设置、允许/阻止来自/到容器的连接以及转发暴露端口的所有其他事情都docker已经为您处理好了。

这尤其意味着你绝对不应该做是为了弄乱DOCKER链条,iptables因为这些链条是由守护进程自动管理的docker

如上所述,在大多数情况下,默认值已经是你想要的。如果你需要更多的控制自定义配置,你需要禁用iptables管理docker并手动管理网络配置 - 通常这不是您想要做的。

您可以在docker网络文档

答案2

我认为我找到了一个可行的解决方案,但我不确定是否还有更好的方法。

通过我尝试的方法,我还必须声明为 DOCKER-USER 集建立的连接,与我的问题相比,添加了这两行:

iptables -A DOCKER-USER -i docker0 -j ACCEPT
> iptables -A DOCKER-USER -o docker0 -j ACCEPT
> iptables -A DOCKER-USER -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A DOCKER-USER -i ens192 -j DROP

答案3

根据您的问题,我假设这些 iptables 规则应该安装在 docker-host 上。

您在这里遗漏了一个中心点:防火墙规则仅对主机本身有效,对容器无效!

假设您的 docker0 接口的 IP 范围为 172.16.0.0/16。主机接口地址为 172.16.0.1,您的第一个容器的地址可能是 172.16.0.2。

但是,从容器到互联网的任何传出流量都需要从主机的主网络接口(即 ens192)发出。因此,任何用于容器的防火墙规则都必须位于 FORWARD 链内!(并且不要忘记为主机启用 ip_forwarding)

相关内容