为什么firewalld允许公共流量到绑定到Docker容器的非公共端口?

为什么firewalld允许公共流量到绑定到Docker容器的非公共端口?

我正在尝试在 Fedora 中实现一个非常简单的防火墙,其中公共互联网可以访问 SSH、HTTP、HTTPS 和 Cockpit,但不能访问其他任何内容。同时,服务器通过 Docker 运行微服务,这些微服务可以在端口 8000-8999 上相互通信。

网络图

我使用以下命令在全新安装的 Fedora Server 上进行了设置:

firewall-cmd --zone=public --add-service=cockpit
firewall-cmd --zone=public --add-service=http
firewall-cmd --zone=public --add-service=https

firewall-cmd --zone=internal --add-source=192.168.1.65
firewall-cmd --zone=internal --add-source=192.168.1.66

firewall-cmd --zone=internal --add-port=8000-8999/tcp

firewall-cmd --runtime-to-permanent

当我使用 检查我的配置时--list-all,一切看起来都正确:

> firewall-cmd --list-all --zone=internal
internal (active)
  target: default
  icmp-block-inversion: no
  interfaces:
  sources: 192.168.1.65 192.168.1.66
  services: dhcpv6-client ssh
  ports: 8000-8999/tcp
  protocols:
  forward: no
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

> firewall-cmd --list-all --zone=public
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp2s0
  sources:
  services: cockpit dhcpv6-client http https ssh
  ports:
  protocols:
  forward: no
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

然而,当我测试这个时,我能够击中http://192.168.1.65:8080。我可以从同一内部网络 (192.168.128.128) 上的一台机器访问它,也可以从外部机器访问公共请求。由于 中没有列出internalsources我假设防火墙不会允许请求通过。

我有一个带有接口的自动配置docker区域docker0,但删除它似乎并没有改变我访问内部端口的能力。

为什么我能够从未列出的来源成功请求 :8080 internal

答案1

好吧,问题出在我使用 Docker 作为内部端口。为了简化容器与外部世界和容器之间通信的过程,Docker 选择对防火墙/网络进行大量控制。这意味着如果你希望你的容器可以公开访问您需要通过与 Docker 守护程序位于同一台机器上的防火墙控制公共访问,因此您需要稍微不同地配置防火墙。Docker 有一些关于如何执行此操作的官方文档。基本上,您有以下选择:

  • 为防火墙设置一台单独的机器。这可能是最简单的方法,因为 Docker 和防火墙不必共享资源。
  • 将您的iptables规则添加到DOCKER-USER链中(这更像是对用户的答案iptables;我不确定如何firewalld复制这种方法)
  • iptables=false通过在 Docker 服务配置中进行设置来禁用整个功能。(这篇博文讨论此选项)

我也找到一个帖子我认为这是DOCKER-USERchain 选项的一个不错的变体。基本上,您创建一个iptables.conf可以使用 加载而无需刷新的文件iptables-restore -n。不幸的是,这只是一个iptables解决方案,而不是一个firewalld解决方案。

诊断此问题的一个困难部分是,我需要运行一个脚本来修改防火墙以符合我的要求,并且该脚本会一直有效,直到我重新启动机器或启动 Docker 守护程序。Dockeriptables在启动时会覆盖配置。

答案2

我设法通过重新创建DOCKER-USER链解决了这个问题,并愿意与大家分享,以防其他人发现它有用。

这是基于我在 Github 上找到的解决方案(https://github.com/firewalld/firewalld/issues/869)以及一些天才的博客(https://roosbertl.blogspot.com/2019/06/securing-docker-ports-with-firewalld.html),但我必须对其进行一些调整/改进。

# 1. Stop Docker
systemctl stop docker.socket
systemctl stop docker.service

# 2. Recreate DOCKER-USER iptables chain with firewalld. Ignore warnings, do not ignore errors
firewall-cmd --permanent --direct --remove-chain ipv4 filter DOCKER-USER
firewall-cmd --permanent --direct --remove-rules ipv4 filter DOCKER-USER
firewall-cmd --permanent --direct --add-chain ipv4 filter DOCKER-USER

# 3. Add iptables rules to DOCKER-USER chain - unrestricted outbound, restricted inbound to private IPs
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -m comment --comment 'Allow containers to connect to the outside world'
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -j RETURN -s 127.0.0.0/8 -m comment --comment 'allow internal docker communication, loopback addresses'
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -j RETURN -s 172.16.0.0/12 -m comment --comment 'allow internal docker communication, private range'

# 3.1 optional: for wider internal networks
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -j RETURN -s 10.0.0.0/8 -m comment --comment 'allow internal docker communication, private range'
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 1 -j RETURN -s 192.168.0.0/16 -m comment --comment 'allow internal docker communication, private range'

# 4. Block all other IPs. This rule has lowest precedence, so you can add rules before this one later.
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 10 -j REJECT -m comment --comment 'reject all other traffic to DOCKER-USER'

# 5. Activate rules
firewall-cmd --reload

# 6. Start Docker
systemctl start docker.socket
systemctl start docker.service

相关内容