基于Linux Netfilter NAT实现SNI分流并保留源IP的技术方案咨询
你遇到的这个问题确实很常见——SNIProxy默认的转发模式会把源IP替换成路由器自身的,导致后端依赖源IP的服务出问题,而且不想解密流量的顾虑完全合理,毕竟解密不仅占资源,证书管理也是个麻烦事。下面给你两个可行的方案,都不需要解密TLS流量,还能保留客户端原始源IP:
方案一:内核态Netfilter(nftables/iptables)结合TLS SNI匹配模块
这个方案完全在内核态处理,性能最高,没有用户态转发的开销,适合高流量场景。核心是利用Linux内核的xt_tls(iptables)或nftables的TLS匹配模块,直接提取TLS握手包里的SNI字段,然后做DNAT转发,全程不解密流量。
步骤说明:
确认内核支持:
先检查你的内核是否支持TLS SNI匹配:- 对于nftables,执行
nft list ruleset,如果能看到tls相关的匹配选项,或者执行modinfo nft_tls有输出,说明支持; - 对于iptables,执行
modinfo xt_tls,有输出则支持。如果没有,你可能需要安装内核额外模块包(比如Debian/Ubuntu下的linux-modules-extra-$(uname -r)),或者升级到5.0以上的内核(这个模块是5.0左右加入的)。
- 对于nftables,执行
配置分流规则:
这里推荐用nftables(比iptables更简洁高效),示例配置如下:table ip nat { chain prerouting { type nat hook prerouting priority dstnat; policy accept; # 匹配SNI为example.com的443端口流量,DNAT到VPN子网的对应后端 tcp dport 443 tls sni "example.com" dnat to 192.168.100.10:443 # 匹配另一个SNI的流量 tcp dport 443 tls sni "test.net" dnat to 192.168.100.11:443 # 其他未匹配的443流量,转发到默认后端(可根据需求修改或丢弃) tcp dport 443 dnat to 192.168.100.12:443 } }把配置保存到
/etc/nftables.conf,然后执行nft -f /etc/nftables.conf生效。这个规则会在流量进入路由器的PREROUTING阶段处理,直接修改目标IP,客户端的原始源IP完全保留,后端服务能正常获取到真实的客户端地址。
方案二:Sniproxy透明代理模式
如果你的内核版本较低,无法支持TLS匹配模块,或者不想折腾内核配置,可以用Sniproxy的透明代理模式,它能在用户态解析SNI并保留源IP转发,不需要解密流量。
步骤说明:
修改Sniproxy配置:
编辑Sniproxy的配置文件(通常是/etc/sniproxy.conf),开启透明模式:listen 0.0.0.0:8443 { proto tcp transparent yes table { "example.com" -> 192.168.100.10:443 "test.net" -> 192.168.100.11:443 } }这里让Sniproxy监听8443端口,开启
transparent yes选项。配置iptables转发规则:
添加规则把所有进入路由器443端口的TCP流量重定向到Sniproxy的8443端口:iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443别忘了开启路由器的IP转发:
sysctl -w net.ipv4.ip_forward=1 echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf重启Sniproxy服务:
执行systemctl restart sniproxy,确保服务正常运行。开启透明模式后,Sniproxy会用客户端的原始源IP和后端建立连接,后端能直接看到真实的客户端地址,同时全程不解密TLS流量。
方案对比与注意事项
- 性能差异:方案一(内核态)性能远高于方案二(用户态),适合高带宽、高并发的场景;方案二更适合中小流量的路由器,配置门槛更低。
- 回程路由:不管用哪个方案,都要确保VPN子网内的后端服务器能把响应流量发回路由器,路由器会通过连接跟踪(conntrack)自动把响应转发给客户端,无需额外配置SNAT。
- 权限要求:方案二的Sniproxy需要以root权限运行,因为透明模式需要修改socket的底层选项。
备注:内容来源于stack exchange,提问作者Yuuta Liang




