通过隧道发布网站与让用户获得 VPN 访问权,是两种共用同一根线缆的不同安排。在前者中,公网反向代理接收访客请求,并沿隧道转发到无人能直接到达的源站。在后者中,用户必须先加入 VPN,应用才可能被访问。把这两种描述混为一谈,正是私有后端最终被公开暴露的原因。

在图上看起来相似的两类模型

先画箭头,再做选择。公网发布模型由浏览器经 HTTPS 到达代理,再由代理经隧道到达源站,而源站从不接受来自互联网的连接。仅 VPN 模型由客户端到达网关,应用只能从该路径内部访问。用户的笔记本与应用处于同一网络,这正是该模型的全部意义。

两类模型的差别在于谁来认证、在哪里认证。前者中,代理是公网入口,它按自身配置的策略转发任何到达的请求,包括恶意请求。后者中,可达性本身就是闸门,这是一种粗糙但真实的准入。

TLS 在哪里终止,它到底证明了什么

浏览器与代理之间的 TLS 保护这一段,并让浏览器验证它打算访问的站点。如果代理终止 TLS,请求在转发之前会以可读形式存在于该进程内。到源站的第二段 TLS 可以保护下一跳,并单独验证应用端点。两段会话都不决定哪个人可以使用应用。代理上的通配符证书也不会延伸到那第二跳。

这正是人们把它塞进“加密”一词的部分。机密性、完整性和服务器身份是传输属性;授权是应用层的决定。隧道之后的私有地址改变的是谁能到达套接字,而不是到达之后谁能登录。

只信任来自代理的转发头

按客户端地址记录日志或做授权的应用,通常读取代理添加的某个头部。那个头部只是文本,因此任何能直接到达源站的东西都能声称任意地址。限制源站,使只有代理的隧道地址可以连接,并配置应用只接受来自该地址的头部。

curl -s -o /dev/null -w '%{http_code}
' -H 'X-Forwarded-For: 203.0.113.9' http://172.20.5.40/

如果该请求从并非代理的主机成功,说明源站信任了一个本应忽略的头部。先修好监听器,再动应用。拒绝的结果远好于一条带着伪造地址的日志。

让源站只从一个地方可达

把源站的监听器绑定到隧道地址,或用防火墙把它限制到代理,然后确认真正在监听的是什么。以通配绑定启动的服务会接受主机所有接口上的连接,包括你忘了它可路由的那个。 监听表才是基本事实,而不是你打算应用的那份配置文件。如果通配绑定是有意为之,防火墙仍必须是收窄它的那一环。

ss -ltnp | grep -E ':(80|443)'

管理接口也要做同样的审查,意外暴露通常就发生在那里。然后把两类模型分开写下来:经代理的公网站点,以及只能经隧道到达的管理接口。“服务器在 VPN 后面”这句话对这些一个字都没说。

相关文章