为何Docker转发端口5665未被防火墙拦截?及容器联网配置方案
问题背景
我在主机上部署了Nginx反向代理,同时在Docker容器中运行Web应用,容器启动命令如下:
docker run -d --name mywebapp -v /webapp/:/data/ --restart unless-stopped -p 5665:80 mywebapp/server:latest
容器内Web应用监听80端口,映射到主机5665端口;Nginx监听主机5666端口,请求转发至5665端口。netstat -lntup相关输出显示docker-proxy监听5665端口,nginx监听5666端口。
我通过nftables配置防火墙,仅允许外部访问5666端口,规则如下:
define IIFNAME = "ens3"; table inet filter { chain input { type filter hook input priority filter policy drop ct state invalid drop comment "early drop of invalid connections" ct state {established, related} accept comment "allow tracked connections" iifname lo accept comment "allow from loopback" ip protocol icmp accept comment "allow icmp" meta l4proto ipv6-icmp accept comment "allow icmp v6" tcp dport ssh accept comment "allow sshd" tcp dport 5666 accept comment "webserver"; pkttype host limit rate 5/second counter reject with icmpx type admin-prohibited counter } chain forward { type filter hook forward priority filter policy drop iifname "docker0" oifname $IIFNAME accept; iifname $IIFNAME oifname "docker0" accept; } }
实际测试发现,5666端口可正常访问,但外部仍能连接5665端口绕过Nginx。排查后发现forward链的两条规则是问题所在,删除规则可拦截5665,但会导致容器无法联网。
现提出两个问题:
- 为何forward链规则会影响该场景?我认为docker-proxy是用户态应用,数据包应走INPUT链而非FORWARD链。
- 如何配置才能阻止外部(ens3接口)访问5665端口,同时允许容器访问互联网?
解答
问题1:forward链规则影响场景的原因
Docker端口映射默认有两种处理逻辑:
- 用户态代理模式:就是你看到的
docker-proxy,此时外部发往主机5665的数据包会进入主机INPUT链,被docker-proxy接收后转发到容器,确实走INPUT链。 - 内核态转发模式:Docker默认启用
iptables功能,会自动添加内核级转发规则,把发往主机5665的数据包直接通过FORWARD链转发到容器,完全跳过用户态的docker-proxy。
你的forward链规则允许ens3和docker0双向转发,外部请求可以直接通过内核转发到达容器的80端口,绕开了主机INPUT链对5665端口的拦截,这就是外部能直接访问5665的核心原因。
问题2:满足需求的nftables配置方案
下面提供两种可行配置,按需选择:
方案1:限制外部主动访问,仅允许容器出站
修改forward链规则,替换原有两条规则:
chain forward { type filter hook forward priority filter policy drop # 允许容器主动发起的请求的响应流量 ct state {established, related} accept comment "allow container response traffic" # 允许容器之间的内部通信(可选,根据业务需求决定) iifname "docker0" oifname "docker0" accept comment "allow inter-container communication" # 允许容器出站访问互联网 iifname "docker0" oifname $IIFNAME accept comment "allow container outbound internet access" }
效果:
- 容器主动访问互联网的请求和响应完全正常
- 外部主动发往
docker0的流量被默认drop策略拦截,无法绕过Nginx访问容器服务 - 主机INPUT链已限制仅开放5666,用户态
docker-proxy监听的5665端口也会被INPUT链拦截
方案2:保留双向转发,精准拦截5665对应端口
如果需要保留ens3与docker0的双向转发,但要阻止外部访问5665映射的容器端口,可在forward链添加前置拦截规则:
chain forward { type filter hook forward priority filter policy drop # 拦截外部发往容器80端口的流量(对应主机5665端口映射) iifname $IIFNAME oifname "docker0" tcp dport 80 reject comment "block external access to container web port" # 允许容器出站访问互联网 iifname "docker0" oifname $IIFNAME accept comment "allow container outbound" # 允许外部到容器的其他合法流量(如果有需要) iifname $IIFNAME oifname "docker0" accept comment "allow external to container (except web port)" # 允许已建立连接的响应流量 ct state {established, related} accept }
效果:
- 容器正常访问互联网
- 外部无法通过主机5665端口直接访问容器Web服务,但其他允许的外部到容器流量不受影响
内容的提问来源于stack exchange,提问作者g00dds
相关产品推荐
相关产品推荐

