Docker/主机上用 nftables 做透明代理端口改写


场景:当容器或主机访问 xx.xx.xx.xx:5668 / 9100 时,自动改写为 yy.yy.yy.yy.yy:26880 / 26881


主机上执行(PREROUTING)

# 新建一个名为 nat 的 IPv4 表;若系统已存在同名表会报错,可用 `nft list tables` 先检查
sudo nft add table ip nat

# 在 ip nat 表下创建基础链 prerouting;用于 NAT,挂载在 prerouting hook(路由前处理进入本机的包),priority -100 是常见优先级
sudo nft add chain ip nat prerouting '{ type nat hook prerouting priority -100; }'

# 规则:当目标 IP=xx.xx.xx.xx 且 TCP 端口=5668 时,做 DNAT,把目标改写为 yy.yy.yy.yy.yy:26880
sudo nft add rule ip nat prerouting ip daddr xx.xx.xx.xx tcp dport 5668 dnat to yy.yy.yy.yy.yy:26880

# 规则:当目标 IP=xx.xx.xx.xx 且 TCP 端口=9100 时,做 DNAT,把目标改写为 yy.yy.yy.yy.yy:26881
sudo nft add rule ip nat prerouting ip daddr xx.xx.xx.xx tcp dport 9100 dnat to yy.yy.yy.yy.yy:26881

# 查看当前 prerouting 链规则,确认是否写入成功(会显示 handle 编号,可用于回滚删除)
sudo nft list chain ip nat prerouting


(可选)容器内做出站透明代理(OUTPUT)

# 仅在容器内部执行;容器需要具备 CAP_NET_ADMIN
sudo nft add table ip nat

# 在 ip nat 表下创建 output 链;用于 NAT,挂载在 output hook(处理容器自己发出的包),priority -100 常见
sudo nft add chain ip nat output '{ type nat hook output priority -100; }'

# 规则:容器内发向 xx.xx.xx.xx:5668 的连接,DNAT 到 yy.yy.yy.yy.yy:26880
sudo nft add rule ip nat output ip daddr xx.xx.xx.xx tcp dport 5668 dnat to yy.yy.yy.yy.yy:26880

# 规则:容器内发向 xx.xx.xx.xx:9100 的连接,DNAT 到 yy.yy.yy.yy.yy:26881
sudo nft add rule ip nat output ip daddr xx.xx.xx.xx tcp dport 9100 dnat to yy.yy.yy.yy.yy:26881

# 查看当前 output 链规则,确认是否写入成功
sudo nft list chain ip nat output


PREROUTING vs OUTPUT 对比表

场景使用链生效时机适用对象典型用途
主机 / 宿主机执行prerouting路由前处理进入主机的数据包外部请求、容器出网经过主机转发的流量拦截发往特定 IP:PORT 的连接并转发到代理
容器内执行output容器自己发出的数据包刚产生时容器内进程主动发起的连接在容器内部“透明代理”出站连接

PREROUTING是“从外面拦截”,不跨命名空间、不要容器权限、不依赖容器DNS;OUTPUT是“从里面改写”,容易踩权限/命名空间/DNS的坑,更容易出问题。

留下评论

通过 WordPress.com 设计一个这样的站点
从这里开始