我正在尝试在 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) 上的一台机器访问它,也可以从外部机器访问公共请求。由于 中没有列出internal
,sources
我假设防火墙不会允许请求通过。
我有一个带有接口的自动配置docker
区域docker0
,但删除它似乎并没有改变我访问内部端口的能力。
为什么我能够从未列出的来源成功请求 :8080 internal
?
答案1
好吧,问题出在我使用 Docker 作为内部端口。为了简化容器与外部世界和容器之间通信的过程,Docker 选择对防火墙/网络进行大量控制。这意味着如果你不希望你的容器可以公开访问和您需要通过与 Docker 守护程序位于同一台机器上的防火墙控制公共访问,因此您需要稍微不同地配置防火墙。Docker 有一些关于如何执行此操作的官方文档。基本上,您有以下选择:
- 为防火墙设置一台单独的机器。这可能是最简单的方法,因为 Docker 和防火墙不必共享资源。
- 将您的
iptables
规则添加到DOCKER-USER
链中(这更像是对用户的答案iptables
;我不确定如何firewalld
复制这种方法) iptables=false
通过在 Docker 服务配置中进行设置来禁用整个功能。(这篇博文讨论此选项)
我也找到一个帖子我认为这是DOCKER-USER
chain 选项的一个不错的变体。基本上,您创建一个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