我有一个使用 CentOS 7 的远程 vps,相关firewalld
信息如下,firewalld
正在积极运行。
[root@doer mydir]# firewall-cmd --get-zone-of-interface=eth0
no zone
[root@ doer mydir]# firewall-cmd --list-ports
You're performing an operation over default zone ('public'),
but your connections/interfaces are in zone 'home' (see --get-active-zones)
You most likely need to use --zone=home option.
3306/tcp
我运行一个 Docker 容器,其中有一个 Spring Boot 程序监听 8080 端口,该端口映射到主机的 9182,9182 不在开放端口列表中,但我仍然可以通过http://主机 IP:9182, 怎么了?
我已添加eth0
到公共区域
firewall-cmd --permanent --zone=home --add-interface=eth0
现在
[root@ doer mydir]# firewall-cmd --get-zone-of-interface=eth0
public
[root@ doer mydir]# firewall-cmd --list-ports
3306/tcp
我仍然可以通过以下方式访问网络服务器http://主机 IP:9182。
# firewall-cmd --list-all-zones
block
target: %%REJECT%%
icmp-block-inversion: no
interfaces:
sources:
services:
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
dmz
target: default
icmp-block-inversion: no
interfaces:
sources:
services: ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
drop
target: DROP
icmp-block-inversion: no
interfaces:
sources:
services:
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
external
target: default
icmp-block-inversion: no
interfaces:
sources:
services: ssh
ports:
protocols:
masquerade: yes
forward-ports:
source-ports:
icmp-blocks:
rich rules:
home (active)
target: default
icmp-block-inversion: no
interfaces: eth1
sources:
services: dhcpv6-client mdns samba-client ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
internal
target: default
icmp-block-inversion: no
interfaces:
sources:
services: dhcpv6-client mdns samba-client ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
答案1
Docker 在发布端口时直接将自己的防火墙规则安装到主机服务器的内核中,而无需使用用户友好的防火墙管理工具(如firewalld
脚注 1和相关工具firewall-cmd
(或类似地ufw
或 Shorewall 等))提供的抽象层。
由于 docker 不使用它们 当你仅使用这些工具检查防火墙时,docker 创建的任何规则通常都不会显示。
要查看 Docker(或任何其他创建自己的规则的应用程序)在防火墙中实际创建的规则,您需要使用更低级别的命令iptables
和/或iptables-save
将显示内核中的实际实时配置的命令。
尝试
[sudo] iptables -L -v -n --line-numbers
和
[sudo] iptables -L -v -n -t nat --line-numbers
或使用
[sudo] iptables-save
通常,Docker 创建的防火墙规则会具有先例,因为它们插入到用户友好的防火墙管理工具管理的规则之前。
我运行一个docker,里面有一个springboot程序监听8080端口,该端口映射到宿主机的9182,9182不在开放端口列表中,但是我仍然可以通过访问web服务器
http://<HOST_MACHINE_IP>:9182
,哪里出了问题?
没有什么是错的。
这正是您在创建已发布端口时指示 Docker 执行的操作:
已发布的端口
默认情况下,当您创建容器时,它不会向外界发布任何端口。要使端口可供 Docker 之外的服务或未连接到容器网络的 Docker 容器使用,请使用 --publish 或 -p 标志。这将创建防火墙规则将容器端口映射到 Docker 主机上的端口。
https://docs.docker.com/config/containers/container-networking/
要在 Docker 发布的端口上添加更多访问控制,需要在DOCKER-USER
iptables 链中创建自己的规则,如下所示:https://docs.docker.com/network/iptables/
脚注 1从 docker 20.10.0 开始,docker 应该根据此处的文档与firewalld 集成:https://docs.docker.com/network/iptables/#integration-with-firewalld
答案2
所有答案均无法解释真正原因。
正如 HermanB 所说,Docker 创建了自己的规则......但不是任何地方!
有两个链中的过滤表对于输入流量很重要INPUT
:FORWARD
Firewalld 使用 INPUT 过滤表作为其规则。
PREROUTING
Docker 在链表中创建路由DNAT
。这会导致将数据包路由到FORWARD
链而不是INPUT
链。
因此,所有应该由 Docker 容器使用的流量都会“绕过”防火墙。
在 serverfault 上搜索“firewalld docker”会出现数十个类似的问题。
解决方案:
必须手动添加iptables
规则违背了防火墙。很遗憾 Docker 无法与 Firewalld 正常协作。不过,我相信我们可以做一些更简洁的事情,通过告诉 Firewalld 在 FORWARD 链中应用相同的规则并放置 Firewalld 的自定义链前Docker 的链。
目前,Firewalld 自定义链 ( FORWARD_direct, FORWARD_IN_ZONES, FORWARD_OUT_ZONES
) 位于 Docker 自定义链 ( DOCKER-USER, DOCKER-ISOLATION-STAGE-1, DOCKER
) 之后。
iptables -vL -t filter
- https://github.com/firewalld/firewalld/issues/461
答案3
使用firewall-cmd --list-all-zone,查看“home”区域,你的接口连接到该区域。如果你有其他问题,请发布完整的结果。
答案4
我遇到过类似的问题 - 可以从外部接口 eth0 访问 docker 容器的开放端口 8000,但我需要它仅通过 Nginx 作为代理可见。
深入研究后iptables -L -v -n
发现,DOCKER 规则链是从 FORWARD 链运行的,而添加防火墙命令 rich-rules(和直接规则)则转到 INPUT 链,因此它不能按我预期的方式工作。
以下命令解决了该问题:
firewall-cmd --direct --add-rule ipv4 filter DOCKER 0 -i eth0 -d 172.22.0.0/24 -j DROP
其中 172.22.0.0/24 是带有 docker 容器的子网。