为什么 Port-Share 让所有网站访客都显示成 localhost
后端看到的是转发跳,而不是浏览器。先查清源地址在哪一层丢失,再决定由哪个组件为它背书。
访问日志的每一行都显示同一个回环地址,而页面本身能打开、证书校验也正常。从“连不上”的意义上说没有任何故障,所以这个问题能拖上几个月。坏掉的是归属:Web 后端记录的是转发连接的那一跳的地址,之后每一个信任该日志的决策都继承了同一个错误答案。
下面几项检查由外向内推进。在第一个能解释你所见取值的检查处停下,因为后面的每一步都以先前几步成立为前提。这些检查都不需要猜测前端配置。
追踪两条连接,而不是那一行日志
Port-Share 在一个 TCP 监听上复用两种协议。OpenVPN 检查入站连接的前几个字节,像自家协议就留下,其余全部交给第二个目标。那个目标通常是同一台主机上的 Web 服务器,于是 Web 服务器接受的套接字对端就是 OpenVPN 进程。
ss -ltnp 'sport = :443'
ss -tnp 'dport = :8080'
第一条命令显示公网监听归谁所有。第二条显示到达后端端口的连接,它们的对端地址就是访问日志会打印的那个值。如果对端是回环地址,日志是准确的,信息在更早的一层就丢了。改日志字段名或启用客户端 IP 模块都找不回连接从未携带过的地址。
这一项检查决定其余一切。如果后端的对端是浏览器地址,那端口共享不是你的问题,日志格式才是。
传输转发不是头部注入
转发器只搬字节。它不解析 HTTP,因此无法往一个它从不当作请求读取的请求里插入头部,也无法往原样透传的 TLS 流里添加任何东西。指望代理头的模块会一直等一个没有任何组件生成的头部。
有两种机制可以携带原始地址,两者都需要两端达成一致。PROXY 协议在连接开头附加一个小头部,后端必须配置成期待它;不期待的后端会把这些字节当成请求开头,然后以令人困惑的方式失败。像 X-Forwarded-For 这样的可信代理头,需要由理解 HTTP 的代理设置,并告知后端哪些对端有权断言。
两者都不是自动的,也都不是默认安全的。相信任何入站转发头的后端,等于让客户端自选地址,把日志里的偏差变成绕过访问控制。在生产路径上信任任何头部之前,先在测试主机上跑一遍验证。
curl -sS -H 'X-Forwarded-For: 127.0.0.1' https://site.example.com/health
grep -F '127.0.0.1' /var/log/nginx/access.log | tail -n 3
可选的源地址映射目录
端口共享有一个可选目录参数,在实现了该特性的平台上记录原始源地址与转发连接之间的映射。Web 一侧应当查询这份映射;它不是头部,也不会被注入到请求里。
这个设计值得了解,因为它解释了为什么前端配置看起来一样,有的部署能报告真实地址,有的却不能。差别在接收一侧,通常住在大多数打包版 Web 服务器默认不启用的模块或补丁里。去读你所用的 Web 服务器文档,确认它提供的具体钩子,并核实模块确实已加载,而不是想当然。
如果选择这条路,就把它当作两半的集成:转发器写出的映射,以及后端执行的查询。两边都要验证,并确认该目录在重启后仍然存在,且在繁忙监听上不会无限膨胀。
决定由哪个组件为地址背书
改动配置之前,先点名哪个组件有权断言客户端地址,哪些组件可以相信它。写下一个唯一答案,就能避免常见漂移:三层各自信任不同的来源。
三种设计覆盖大多数需求。把公网监听和 Web 监听分开,让 Web 服务器看到真实对端,根本不存在归属问题。在前面放一个懂 HTTP 的反向代理,在那里终结或解析请求,并让它为只信任该代理的后端设置转发头。或者保留端口共享,接受日志显示的是那一跳,并把访问规则写成基于应用身份而不是地址。
行不通的做法是把转发来的回环地址当成请求源自本机的证据。这个推断恰好反了:地址存在,正是因为本地组件发起了连接。这样写出来的规则会放行任何能到达监听的人。
测试规则,而不是页面
页面能打开只证明转发链路通,对归属什么也没说。要去测地址的使用方:访问规则、速率限制、审计记录、地理查询。
curl -sS -o /dev/null -w '%{http_code}\n' https://site.example.com/
tail -n 5 /var/log/nginx/access.log
grep -c '' /var/log/nginx/access.log
把一次获准的外部请求与前端和后端两边的记录对照,确认该拒绝的规则仍然拒绝、该放行的规则仍然放行。如果日志行是对的,规则却仍把所有人当本地,那说明规则读的是另一个字段。
最后把归属链写进部署笔记:哪个监听、哪个转发器、哪个头部或映射、哪个后端被配置为信任它。下一位读日志的人需要这四项,而正是这份笔记的缺失让同一张工单在一年后重新打开。把它放在配置旁边,而不是聊天记录里。