ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux端口映射实战:iptables、Nginx与跳板服务的原理与配置

Linux端口映射实战:iptables、Nginx与跳板服务的原理与配置 简介Linux端口映射转发的方法是一份PDF电子文档面向需要在Linux环境下打通网络访问限制的开发者、运维人员及系统管理员重点解决第三方接口白名单受限、跨主机服务调用等常见问题。文档围绕跳板服务、Nginx反向代理转发、内核IP转发与iptables端口映射三条技术路线展开不仅覆盖HTTP场景也兼顾SFTP、SSH、FTP等非HTTP协议给出从配置文件修改到iptables规则设置的具体操作思路适合作为日常排查与网络配置时的参考笔记。资源为单个PDF文件体积仅50KB轻量易保存目前已有2490人学习下载。内容预览中完整保留了原文的配置示例与场景分析读者可快速提取要点。如果正面临“本地无法直连第三方测试环境”或“需要把2.2.2.2:8080流量转发到1.1.1.1:8080”的类似困境这份资料能帮助理解不同转发方案的优缺点选择跳板程序、Nginx代理或底层端口映射中的最合适方式。掌握这些方法有助于提升网络调试效率减少外部环境限制对开发进度的影响。1. 从白名单困境说起Linux 端口映射到底解决什么问题做过第三方系统对接的人几乎都撞上过这堵墙对方只放行了你线上某台机器的 IP本地开发环境根本调不通他们的测试接口。我最早遇到这事时被逼得在已加白名单的机器上临时起了个服务做中转结果代码改一次要重新部署一次调试效率低到怀疑人生。后来才发现Linux 端口映射转发就是专门解决这类问题的把线上那台机器的某个端口原样转发到第三方服务端口本地请求打到线上机器的 8080实际上访问的是对方的接口协议无关、代码不用改、白名单照旧生效。这篇笔记把三种实现方式——跳板服务、Nginx 转发、iptables 内核转发——从原理到配置、再到坑点全部拆开讲适合被网络策略卡住的开发也适合要维护线上转发规则的运维。2. 先选方案再动手跳板服务、Nginx 和 iptables 的本质区别2.1 选型第一原则先看协议再决定用哪一层转发很多人上来就搜端口映射然后照着命令敲一遍根本不理解为什么有时候需要 Nginx、有时候必须用 iptables。这里有个最简单的判断标准你转发的是 HTTP 请求还是任意 TCP/UDP 流量。HTTP 场景Nginx 反向代理最省事配置直观、还能顺手改 Header、加缓存。缺点是和协议强绑定转发 SSH、SFTP、MySQL 这类二进制协议就无能为力。非 HTTP 场景必须走网络层转发。iptables 的 DNAT/SNAT 工作在 TCP/UDP 层只管把数据包从一个地址端口搬到另一个地址端口协议栈之上是什么内容它完全不关心。SSH、SFTP、FTP、自研 TCP 长连接都能转。跳板服务本质是应用层写一个转发程序属于自己动手造轮子灵活但性能和稳定性都要自己负责一般只在不方便动线上防火墙规则时临时顶上。所以我的习惯是先问一句要转发的东西是什么协议?如果是 HTTP优先考虑 Nginx如果是 SSH 或某个自研端口直接上 iptables别犹豫。2.2 跳板服务最灵活但最重的后备方案跳板服务的思路很简单既然白名单只允许 2.2.2.2 访问 1.1.1.1那就让 2.2.2.2 上跑一个服务接收开发机的请求然后原封不动转给 1.1.1.1:8080。伪代码大概是import socket import threading def handle(client_sock, remote_host, remote_port): remote_sock socket.create_connection((remote_host, remote_port)) t1 threading.Thread(targetpipe, args(client_sock, remote_sock)) t2 threading.Thread(targetpipe, args(remote_sock, client_sock)) t1.start() t2.start() def pipe(src, dst): while True: data src.recv(4096) if not data: break dst.sendall(data)逻辑不复杂但这套方案有几个硬伤双线程搬运数据要考虑半关闭和异常处理不然连接断了会卡死每个连接都要占用两个 socket 和两个线程并发一高就吃力数据在用户态和内核态之间拷贝了好几次性能跟内核转发不在一个量级。所以我的判断是当应急手段可以当长期方案不建议。2.3 Nginx 转发HTTP 场景下的低成本高回报如果确定只转发 HTTPNginx 是性价比最高的选择。配置短、可读性强、运维同学几乎都会看。核心配置就三行server { listen 8080; location /test/api/ { proxy_pass http://1.1.1.1:8080; } }这里有个关键细节proxy_pass后面带不带 URI决定了请求路径怎么拼。如果写proxy_pass http://1.1.1.1:8080;请求/test/api/user会原样转发给后端如果写proxy_pass http://1.1.1.1:8080/;匹配到的/test/api/前缀会被替换成/请求变成/user。这个坑我后面单独讲配置前先想清楚你要的是哪种行为。2.4 iptables 端口映射在内核里就把流量转了iptables 的方案之所以万能是因为它工作在 Linux 内核的 netfilter 框架上数据包在进入协议栈处理之前就被改写了目标地址。看这条规则链客户端 → 2.2.2.2:8080 → PREROUTING 链 (DNAT) → 目标改为 1.1.1.1:8080 → 内核路由 → POSTROUTING 链 (SNAT) → 源地址改为 2.2.2.2 → 发给 1.1.1.1PREROUTING在路由决策之前处理入站数据包所以 DNAT 改的是我要去哪;POSTROUTING在数据包离开本机之前处理出站数据包SNAT 改的是我从哪来。两个配合才能保证 1.1.1.1 回包时知道要送回给 2.2.2.2再由 2.2.2.2 送回给客户端。这个链路必须理解不然后面排查回程丢包时会一头雾水。相比之下跳板服务和 Nginx 都改不了非 HTTP 协议的命运只有 iptables 能做到无论是 HTTP 还是 SSH统一转发。3. iptables 端口映射落地从开启内核转发到规则持久化3.1 第一步开启 net.ipv4.ip_forward让内核允许当路由器iptables 转发依赖内核的 IP 转发功能。默认情况下Linux 只转发本机收发的数据包不会帮别的机器传话。开启方式分两步# 查看当前值0 表示未开启 sysctl net.ipv4.ip_forward # 临时开启重启后失效 echo 1 /proc/sys/net/ipv4/ip_forward # 永久开启写入配置文件 vi /etc/sysctl.conf # 在文件里加一行或取消注释 # net.ipv4.ip_forward 1 # 让配置立即生效 sysctl -pCentOS 7 上有个特殊位置要注意/usr/lib/sysctl.d/50-default.conf这个文件里的默认值会覆盖/etc/sysctl.conf如果前面改完不生效检查一下这个目录下有没有冲突配置。我在 CentOS 7 上遇到过几次改完/etc/sysctl.conf但sysctl -p之后转发还是不生效的情况最后都是在这个50-default.conf里找到原因顺手在/etc/sysctl.d/下新建一个99-forward.conf把值覆盖回去才解决。3.2 第二步配上 DNAT 和 SNAT 两条规则核心转发就两条 iptables 命令# 转发请求把发往 2.2.2.2:8080 的 TCP 数据包目标地址改成 1.1.1.1:8080 iptables -t nat -A PREROUTING -p tcp -d 2.2.2.2 --dport 8080 -j DNAT --to-destination 1.1.1.1:8080 # 转发响应把源地址为 1.1.1.1:8080 的响应包源地址改回 2.2.2.2:8080 iptables -t nat -A POSTROUTING -p tcp -s 1.1.1.1 --sport 8080 -j SNAT --to-source 2.2.2.2:8080逐条拆开解释。第一条在nat表的PREROUTING链上追加规则-d 2.2.2.2限定目标地址是本机 IP--dport 8080限定目标端口-j DNAT --to-destination 1.1.1.1:8080表示把目标地址换成 1.1.1.1、端口换成 8080。逻辑就是所有进到这台机器 8080 端口的 TCP 包我帮你改道。第二条在POSTROUTING链上做源地址伪装。-s 1.1.1.1 --sport 8080匹配响应包的源地址和源端口--to-source 2.2.2.2:8080把源地址改成 2.2.2.2。这一步很重要如果不做 SNAT1.1.1.1 回包时会直接把包发给发起请求的客户端 IP而中间这台机器就被跳过了连接直接断掉。补充一个常见疑问为什么很多教程里只用 DNAT 不加下面这条 SNAT?因为如果客户端的默认网关恰好指向这台转发机器回程流量会自然回来这种情况下省略 SNAT 也能跑。但大多数业务场景里客户端网关不是它所以两条规则都配上最稳妥。3.3 第三步保存规则防止重启后一切归零iptables 规则是运行时生效的重启就没了。保存和恢复方式因系统而异# CentOS 6 / 老版本 service iptables save # CentOS 7先装 iptables-services再保存 yum install -y iptables-services systemctl enable iptables service iptables save service iptables restartCentOS 7 的坑在默认防火墙是 firewalld很多机器上连/etc/sysconfig/iptables这个文件都不存在。这时候直接敲service iptables save大概率报错。走一遍上面的安装流程把 iptables-services 装上规则才会持久化到/etc/sysconfig/iptables下次启动自动加载。3.4 第四步验证转发到底通没通规则配完别急着走验证链路是完整闭环才叫真完成。我一般分三步检查# 1. 确认规则已经在 nat 表里 iptables -t nat -L -n -v # 2. 确认监听端口存在 ss -tnl | grep 8080 # 3. 从开发机实际发请求 curl -v http://2.2.2.2:8080/test/api第二步特别容易引起误会如果机器上本来没有 8080 端口的监听进程ss可能什么都看不到。但 iptables 的 DNAT 在 PREROUTING 链上数据包还没走到本地 socket 就被改走了所以即使没有监听也不影响转发。判断标准以第三条实际请求为准别被ss的输出带偏。4. Nginx 转发替代方案配置细节与 HTTP 场景下的边界4.1 一个完整的 Nginx 转发配置模板如果场景限定在 HTTPNginx 的配置可以更精细。下面是一个带 Host 转发和日志记录的完整配置server { listen 8080; server_name _; access_log /var/log/nginx/access_forward.log; error_log /var/log/nginx/error_forward.log; location /test/api/ { proxy_pass http://1.1.1.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }proxy_pass是核心后面的地址可以写 IP 也可以是域名还可以带端口。proxy_set_header三行分别是把原始 Host 传过去、把访问者真实 IP 放到X-Real-IP、把整个代理链的 IP 串起来放进X-Forwarded-For。后端接口如果校验了 Host 头或取真实 IP这三行就是救命用的。重新加载配置用nginx -s reload不需要重启对线上请求零影响。4.2 proxy_pass 带不带斜杠转发路径天差地别这是 Nginx 转发最经典的坑。同样是/test/api/这个 locationproxy_pass后面写法不同实际转发路径完全不同# 情况 A不带 URI location /test/api/ { proxy_pass http://1.1.1.1:8080; } # 请求 /test/api/user → 转发到 http://1.1.1.1:8080/test/api/user # 原本路径保持原样 # 情况 B带 URI location /test/api/ { proxy_pass http://1.1.1.1:8080/; } # 请求 /test/api/user → 转发到 http://1.1.1.1:8080/user # /test/api/ 前缀被替换成 /情况 A 适合后端路径没变的场景比如第三方接口路径本来就带/test/api;情况 B 适合后端根路径就是接口根路径的场景。选择依据只有一个你希望后端收到的 URL 是什么样。配错的表现通常是接口 404 或者路径重复排错时先看 Nginx 的 access_log比较收到的请求路径和后端实际处理路径。4.3 Nginx 解决不了的场景转发 SSH、FTP、自研 TCPNginx 的 stream 模块虽然能做 TCP 层转发但默认编译经常不带。更关键的是如果要转发的地址端口不止一个、或者协议带多路复用Nginx 的配置会变得非常别扭。比如 SFTP 走 22 端口即便你用 stream 模块转了它也只是把 TCP 流转过去并不比 iptables 多出什么优势反而多了一层进程开销。所以我的结论很明确纯 HTTP 优先 Nginx因为可观测性好、还能改协议头;一旦涉及 SSH、FTP、数据库直连、或任何自定义二进制协议直接回到 iptables 方案别在 Nginx 里硬拗。4.4 同一台机器上 Nginx 和 iptables 并存的注意点有些场景会同时用到两者Nginx 管 HTTP 转发iptables 管其它协议的转发。这时候最容易翻车的是端口冲突。比如 Nginx 已经listen 8080iptables 再把--dport 8080做 DNAT两条链路会抢同一个端口。数据包先进 PREROUTING 链如果 iptables 规则先匹配并做了 DNAT包根本不会送到本机 Nginx 进程。表现就是 Nginx 配置没问题、8080 有监听但请求就是不进 Nginx。解决方式就是规划好端口分工HTTP 用 8080 走 Nginx其它协议用 8081 走 iptables互不重叠。5. 避坑指南端口映射翻车的五个典型案例5.1 规则都在但转发就是不生效现象iptables -t nat -L -n能看到规则但从客户端访问 2.2.2.2:8080 就是不通。原因最常见的是net.ipv4.ip_forward没开内核不允许转发数据包规则配了也是白配。其次是入站方向防火墙把包拦了DNAT 在 PREROUTING 之后filter表的FORWARD链如果默认 DROP包会在转发阶段被丢。解决先sysctl net.ipv4.ip_forward确认是 1;再iptables -L -n看 FORWARD 链策略必要时加一条放行规则iptables -I FORWARD -p tcp -d 1.1.1.1 --dport 8080 -j ACCEPT5.2 重启后规则全部消失现象配置完当天能通服务器一重启就废了。原因iptables 规则在内存里没有持久化到配置文件。CentOS 7 尤其典型firewalld 环境下/etc/sysconfig/iptables可能压根不存在。解决装 iptables-services 并启用保存时确认提示Saving firewall rules to /etc/sysconfig/iptables然后systemctl enable iptables让服务开机自启。5.3 请求能出去但响应回不来连接卡住现象telnet能连上但 HTTP 请求一直转圈最后超时。原因只配了 PREROUTING 的 DNAT没配 POSTROUTING 的 SNAT回包源地址是 1.1.1.1客户端不认。解决把第二条 SNAT 规则补上。排查时在转发机器上用tcpdump -i eth0 host 1.1.1.1看有没有回包是回包走了但地址不对还是压根没回。5.4 源端口和目的端口搞混规则匹配不到现象明明按教程抄的命令就是不通。原因--dport是多少就匹配目标端口--sport匹配源端口。很多场景里第三方服务端口是 8080但客户端访问的端口可能是 80配置时把--dport 80和--to-destination 1.1.1.1:8080混在一起语义就错了。解决先明确进来的端口和出去的端口两个值PREROUTING 里的--dport是入站端口--to-destination里的端口才是目标端口两者可以完全不同。建议配置前写一行注释标明两个端口分别是什么# 入口端口 8080映射到对端 1.1.1.1 的 9090 iptables -t nat -A PREROUTING -p tcp -d 2.2.2.2 --dport 8080 -j DNAT --to-destination 1.1.1.1:90905.5 目标机器有多个网卡转发走了错的出口现象转发机器有内网和外网两个网卡流量从 eth0 进来但服务端回包从 eth1 走了客户端连不上。原因路由表决定回包走哪个网卡不做强制绑定时可能走默认路由。解决给目标 IP 加一条静态路由强制回包走指定网卡。比如 1.1.1.1 只能通过 eth0 到达就执行ip route add 1.1.1.0/24 dev eth0配合 SNAT确保回包从正确的网卡出去。这类问题在双网卡机器上极其隐蔽排查优先级可以提前。6. 进阶用法从单条转发到可持续维护的转发方案6.1 本机端口转发不改防火墙也能用的隐藏技能上面讲的都是跨机器的端口映射其实 iptables 也能做本机端口转发——把本机的 8080 端口转给本机的 9090 端口。这在服务只监听 localhost但外部需要访问的场景下很实用很多中间件默认只绑 127.0.0.1用这条规则就能在不改服务配置的前提下把它暴露出去iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 127.0.0.1:9090 iptables -t nat -A POSTROUTING -p tcp -d 127.0.0.1 --dport 9090 -j SNAT --to-source 127.0.0.1注意这里 PREROUTING 规则里没有写-d 2.2.2.2意味着本机所有网卡的 8080 端口都会被转走配置前想清楚是不是你要的效果。不想影响外网访问就把-d限定为127.0.0.1。6.2 不止 TCPUDP 端口映射只需改一个参数有些业务走 UDP比如 DNS、SNMP、视频流。iptables 对 UDP 的转发逻辑相同规则里把-p tcp换成-p udp即可iptables -t nat -A PREROUTING -p udp -d 2.2.2.2 --dport 5353 -j DNAT --to-destination 1.1.1.1:5353 iptables -t nat -A POSTROUTING -p udp -s 1.1.1.1 --sport 5353 -j SNAT --to-source 2.2.2.2:5353UDP 没有连接状态有一个隐患如果对端长期没有数据返回NAT 表里的映射条目会超时过期后续包可能转发失败。排查时看到时通时不通先怀疑这个适当调大 conntrack 超时参数。6.3 用脚本管理多条转发规则避免删一条错一条线上机器往往不止一条转发规则时间一长规则列表又长又乱。我习惯把规则写成一个 Bash 脚本带上参数化入口方便日常增删#!/bin/bash # 转发规则管理脚本 REMOTE_HOST2.2.2.2 TARGET_HOST1.1.1.1 add_forward() { local entry_port$1 local target_port$2 iptables -t nat -A PREROUTING -p tcp -d ${REMOTE_HOST} --dport ${entry_port} -j DNAT --to-destination ${TARGET_HOST}:${target_port} iptables -t nat -A POSTROUTING -p tcp -s ${TARGET_HOST} --sport ${target_port} -j SNAT --to-source ${REMOTE_HOST}:${entry_port} service iptables save } del_forward() { local entry_port$1 local target_port$2 iptables -t nat -D PREROUTING -p tcp -d ${REMOTE_HOST} --dport ${entry_port} -j DNAT --to-destination ${TARGET_HOST}:${target_port} iptables -t nat -D POSTROUTING -p tcp -s ${TARGET_HOST} --sport ${target_port} -j SNAT --to-source ${REMOTE_HOST}:${entry_port} service iptables save } case $1 in add) shift; add_forward $ ;; del) shift; del_forward $ ;; *) echo 用法: $0 {add|del} 入口端口 目标端口 ;; esac脚本里$1是动作参数shift后剩下的是端口参数。每次增删完自动保存规则避免改完忘保存、重启全丢的低级失误。我后来在好几台机器上都是靠这个脚本维护再也没有半夜被拉起来配规则的经历。做端口映射这事本质是拿 Linux 的网络栈能力去解决业务层面的访问限制难点不在命令本身而在理解每一条规则在数据包生命周期里的位置。搞懂了 PREROUTING 和 POSTROUTING 的先后关系很多奇奇怪怪的不通都能一眼看出问题在哪。如今我每次配完规则都强制自己走一遍验证流程查规则、看转发开关、再从外部实际请求一次三步缺一不可。希望这篇整理能帮你少走一些我当年走过的弯路。本文还有配套的精品资源点击获取
返回列表