Firewalld端口重定向至无root权限Podman容器后源IP异常问题求助
Firewalld端口重定向至无root权限Podman容器后源IP异常问题求助
我现在碰到一个头疼的问题:用无root权限的Podman跑了个容器,容器开了8080和8443端口,我想通过主机的80和443端口访问它,于是用firewalld配了端口转发规则,还加了masquerade伪装,具体命令是:
firewall-cmd \ --add-rich-rule "rule family=ipv4 forward-port port=80 protocol=tcp to-port=8080" \ --add-rich-rule "rule family=ipv6 forward-port port=80 protocol=tcp to-port=8080" \ --add-rich-rule "rule family=ipv4 forward-port port=443 protocol=tcp to-port=8443" \ --add-rich-rule "rule family=ipv6 forward-port port=443 protocol=tcp to-port=8443" \ --add-rich-rule "rule family=ipv4 forward-port port=443 protocol=udp to-port=8443" \ --add-rich-rule "rule family=ipv6 forward-port port=443 protocol=udp to-port=8443" firewall-cmd --add-masquerade
现在端口转发倒是正常能用,但容器里看不到真实的客户端源IP,显示的全是容器自己的IP。我也试着改iptables规则,但没搞明白怎么弄,有没有大佬能指点下怎么解决这个问题?
问题原因分析
这个问题的核心在于rootless Podman默认的slirp4netns网络模式加上你配置的firewalld masquerade规则:
- slirp4netns是一种用户态的NAT网络,容器的所有流量都会经过一层NAT转发;
- 你添加的
--add-masquerade又对进入容器的流量做了SNAT(源地址转换),把客户端的真实IP替换成了主机或容器网桥的IP,所以容器里只能看到转换后的IP,而非真实的客户端地址。
解决方案推荐
方法一:用Podman原生端口转发(最省心)
rootless Podman默认不能绑定1024以下的特权端口,所以先调整系统参数允许非特权用户绑定这些端口:
# 临时生效,重启后失效 sysctl net.ipv4.ip_unprivileged_port_start=80 # 永久生效,写入配置文件 echo "net.ipv4.ip_unprivileged_port_start=80" | sudo tee /etc/sysctl.d/allow-unprivileged-ports.conf sudo sysctl -p /etc/sysctl.d/allow-unprivileged-ports.conf
之后启动容器时直接用-p参数映射端口,Podman会自动处理转发,还能保留真实源IP:
podman run -d -p 80:8080 -p 443:8443 [你的容器镜像/名称]
如果不想修改系统参数,也可以给容器添加CAP_NET_BIND_SERVICE权限(新版本Podman都支持):
podman run -d --cap-add NET_BIND_SERVICE -p 80:8080 -p 443:8443 [你的容器镜像/名称]
方法二:调整Firewalld规则(适合坚持用firewalld转发的场景)
如果一定要用firewalld来做端口转发,需要去掉masquerade,同时确保容器的回程流量能正确路由回客户端:
- 先移除之前的masquerade规则:
firewall-cmd --remove-masquerade firewall-cmd --runtime-to-permanent
- 找到容器的私网IP(用
podman inspect [容器ID/名称] | grep IPAddress查看),然后给主机添加路由,让主机能访问容器所在的私网网段:
# 假设容器IP是10.0.2.10,slirp4netns的网关是10.0.2.1 sudo ip route add 10.0.2.0/24 via 10.0.2.1 dev tap0
- 修改firewalld的富规则,添加
notrack标记避免SNAT,同时指定容器的IP:
firewall-cmd \ --add-rich-rule "rule family=ipv4 forward-port port=80 protocol=tcp to-port=8080 to-addr=[你的容器IP] notrack" \ --add-rich-rule "rule family=ipv6 forward-port port=80 protocol=tcp to-port=8080 to-addr=[你的容器IP] notrack" \ --add-rich-rule "rule family=ipv4 forward-port port=443 protocol=tcp to-port=8443 to-addr=[你的容器IP] notrack" \ --add-rich-rule "rule family=ipv6 forward-port port=443 protocol=tcp to-port=8443 to-addr=[你的容器IP] notrack" \ --add-rich-rule "rule family=ipv4 forward-port port=443 protocol=udp to-port=8443 to-addr=[你的容器IP] notrack" \ --add-rich-rule "rule family=ipv6 forward-port port=443 protocol=udp to-port=8443 to-addr=[你的容器IP] notrack" firewall-cmd --runtime-to-permanent
不过这种方法需要手动维护容器IP,容器重启后IP可能会变,所以不如方法一方便。
备注:内容来源于stack exchange,提问作者user496909
相关产品推荐
相关产品推荐

