端口转发需同时满足内核ip_forward开启、NAT规则正确配置、FORWARD链放行三个条件;常见失败原因包括FORWARD默认DROP、DNAT目标地址错误、服务监听127.0.0.1、混淆PREROUTING与OUTPUT链、缺少MASQUERADE/SNAT导致返回流量不通,以及规则未持久化。

端口转发不是“配一条命令就完事”,必须同时满足内核转发、NAT规则、FORWARD放行三个条件,缺一不可。
为什么开了 ip_forward 还是不转发?
只改 /etc/sysctl.conf 并执行 sysctl -p 是必要但不充分的条件。常见失败原因包括:
- iptables 的
FILTER表中FORWARD链默认策略是DROP,而 DNAT 后的数据包必须经过FORWARD链;没加对应ACCEPT规则,包直接被丢弃 - DNAT 规则写在
nat表的PREROUTING链,但目标地址写错了(比如用了本机外网 IP 而非实际到达接口的 IP) - 内网服务监听的是
127.0.0.1,而非0.0.0.0或内网 IP,导致转发后连接被拒绝(错误信息类似Connection refused)
iptables -t nat -A PREROUTING 和 -A OUTPUT 的区别
这两个链处理完全不同的流量路径:
-
PREROUTING:处理**从外部进来的包**(比如公网用户访问你的服务器 80 端口),适合跨主机转发(如把 192.168.1.1:80 → 192.168.1.100:8080) -
OUTPUT:处理**本机进程发出的包**(比如 curl http://localhost:80),配合REDIRECT可实现本机端口重定向(如把发往本机 80 的请求转到 8080) - 误把
PREROUTING规则用于本机测试(比如用wget 127.0.0.1:80),会失败——因为本地回环流量不走PREROUTING,得用OUTPUT+REDIRECT或额外加lo接口规则
为什么返回流量不通?必须配 MASQUERADE 或 SNAT
DNAT 只改了目标地址,没动源地址。内网服务响应时,会直接把包发给原始客户端(比如公网用户),但该用户发包时源 IP 是公网地址,而内网服务路由表里没有去公网的路由,导致响应包发不出去或被丢弃。
- 用
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j MASQUERADE:让所有来自内网段的返回包,源 IP 替换成本机外网接口 IP(适合动态 IP 场景) - 用
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j SNAT --to-source 192.168.1.1:显式指定 SNAT 源 IP(适合静态 IP,更可控) - 漏掉这步,现象是:能建立 TCP 连接(SYN/SYN-ACK 正常),但应用层无响应(HTTP 请求卡住、SSH 登录黑屏)
转发规则重启后失效怎么办?
iptables 规则是内存态的,重启即丢失。不同发行版保存方式不同:
- Debian/Ubuntu:安装
iptables-persistent,运行netfilter-persistent save - RHEL/CentOS 7:用
service iptables save(需先启用iptables服务,禁用firewalld) - 通用兼容方案:把规则写成脚本,加入
/etc/rc.local(注意权限和执行顺序) - 别依赖
iptables-save > /etc/iptables.rules后手动恢复——没人保证开机时自动执行它
真正容易被忽略的是 FORWARD 链的匹配粒度:如果只写 -d 192.168.1.100,但内网有多个子网,或者转发目标变了,规则就失效。建议始终绑定接口(如 -i eth0 -o eth1)并明确协议和端口,避免隐式匹配引发的连通性漂移。

















