通过 SSH 转发访问仅限 VPN 的 HTTPS 站点
用一个本地 SSH 转发桥接仅可通过 VPN 访问的单个 HTTPS 服务,用 --connect-to 保留真实主机名,并了解单个 TCP 转发无法覆盖哪些情况。
一台机器已有获批的 VPN 访问权限,另一台机器恰好需要一个私有 HTTPS 服务,而一个本地 SSH 转发无需给第二台机器开隧道就能补上这个缺口。转发承载的是单个 TCP 目标,而不是整个网络,而这个限制正是关键所在。请按顺序完成下面五个步骤,因为每一步失败的原因各不相同,后一步也无法挽救前一步的缺口。只要缺少任何一个前提,这套设计就无法解决,任何命令调优都无济于事。
第一步:确认三个前提
在任何命令起作用之前,三件事必须已经成立。你需要获准把第一台机器用作转发主机、拥有可用的 SSH 访问,以及允许目标地址的服务器策略。为文件传输创建的 SSH 账户可能已禁用转发,而允许转发的服务器仍可能限制能通过它访问哪些目标。测试之前先询问,因为未经同意就把同事的工作站当作跳板属于策略问题,即使命令运行得很干净。
两条命令就能解决这份清单的技术部分。第一条证明账户和密钥无需交互提示即可使用,第二条输出实际生效的服务器策略,而不是配置文件里被注释掉的示例。
ssh -o BatchMode=yes [email protected] 'echo ok'
sudo sshd -T | grep -E 'allowtcpforwarding|permitopen'
第二步:绑定回环端口并保持会话存活
在运行客户端应用的机器上,打开指向私有服务的本地转发。把它显式绑定到回环地址,以免发布到本地网络,并选择没有本地服务占用的高位端口。工作期间保持会话运行,因为关闭它会同时关闭监听器。ExitOnForwardFailure 会把被拒绝的绑定或被占用的端口变成立即退出,而不是留下一个没有转发的会话,而这正是最浪费时间的故障模式。
私有连接由跳板主机发起,因此该主机的 DNS、路由、VPN 策略和服务访问都必须正常工作。SSH 会话不会创造跳板主机本身没有的连通性,也无法修复 VPN 从未下发的路由。要在监听器真正所在的位置确认它,而不是相信命令的沉默。
ssh -N -o ExitOnForwardFailure=yes -L 127.0.0.1:9443:portal.lab.example:443 [email protected]
ss -ltn '( sport = :9443 )'
第三步:套接字在本地时保留 HTTPS 名称
浏览 https://127.0.0.1:9443/ 会改变 TLS 服务器名称和应用授权。为 portal.lab.example 签发的证书无法对回环地址通过验证,由此产生的错误看起来像证书损坏,而不像转发捷径。做命令行测试时,保留真实 URL,只改变套接字目标。--connect-to 做的就是这件事,curl 手册记录了下面使用的四字段形式。
curl --noproxy portal.lab.example --connect-to portal.lab.example:443:127.0.0.1:9443 https://portal.lab.example/
名称仍保留在 URL、TLS 握手和 Host 头中,只有 TCP 连接走向本地监听器。不用代理的选项让测试留在这条转发上,而不是让已配置的 HTTP 代理拦截它;保持证书校验开启,结果才有意义。
第四步:了解单个转发无法覆盖什么
单个 TCP 转发只覆盖一个目标端口。重定向到第二个主机名、从私有网络内部主机加载资源、或向另一个端口提交的页面,都需要各自的转发,而每一个都需要和第一个相同的策略检查。浏览器工作流还会提出自己的要求,因为必须通过代理配置或本地映射让浏览器把真实名称发往本地端口。因此命令行测试成功并不能证明浏览器也能成功,尽管它证明了通路存在。
UDP、SMB 发现和域登录根本不经过 TCP 转发。如果真正的需求是通用网络访问,这套设计就是错误的工具,受支持的远程访问网关才是正确的选择。
第五步:清理并记录
测试结束后停止会话,并确认监听器已经消失。如果所选端口已被占用,请另选一个未使用的回环端口,并同时更新 URL、测试命令和记录,而不是为了腾出端口号去停掉无关的本地服务。把转发留在笔记本上开着,等于把一次诊断变成一个无人管理的服务。
记下跳板主机、被转发的目标、本地端口,以及是谁批准了这一安排。只存在于某人 shell 历史里的转发,距离重复排查只差一次重启,而后来的人不会知道三个前提里哪个已经检查过。