你可能会以为 SOCKS 代理必须运行在已有网络访问的那一端。OpenSSH 可以把监听端放在完全不同的地方。远程动态转发会在远程主机上打开 SOCKS 端口,并让每个连接都从你本地的 SSH 客户端发起,由它承载笔记本已获批准的 VPN 路径。Pi 得到的是一个范围受限的代理;它不会继承你的隧道或你的权限。

监听端与连接的发起方可以不同

普通的ssh -D 1080在本机运行监听端,并从那里发出连接。远程动态转发把这一点反过来:监听端出现在远程主机上,而它接受的每个连接都从本地一侧打开。OpenSSH 客户端手册把不带固定目标的-R记录为正是这种 SOCKS4/5 安排。

从笔记本执行的一条受支持的命令如下,其中账户、主机和端口都视为占位符:

ssh -NT -S none -o ExitOnForwardFailure=yes \
  -o PermitRemoteOpen=app.corp.example:443 \
  -R 1080 [email protected]

这里有三个细节在起作用。-S none让会话避开共享的控制主连接,ExitOnForwardFailure快速失败,而不是把你留在一个半开的会话上,PermitRemoteOpen则限制远程监听端能够到达的范围。请使用已获授权的 Pi 账户,并在加入上述任何内容之前核验主机密钥。

远程监听端实际暴露了什么

在依赖监听端之前,先检查服务器的转发策略。在GatewayPorts no设置下,远程转发仍绑定在 loopback 上,这才是你想要的暴露面。若服务器被配置为强制使用通配转发,它会在每个接口上都发布同一个端口,这就把个人代理变成了本地网络也能访问的东西。

loopback 也不是按用户划分的边界。Pi 上的另一个进程只要知道端口就能连到127.0.0.1:1080,所以 Pi 的本地账户以及上面运行的一切都属于你访问设计的一部分。这是把目标列表保持窄的理由,而不是跳过策略检查的理由。

把允许的目标限定为你确实要访问的那个应用。为了一个不相干的程序能跑而改成无限制转发,会把这条路径扩得远远超出你最初的问题。目标列表还会记录这次会话是做什么用的,这对之后审计的人有帮助。把它写在所用账户旁边。

让一个应用走上代理

Pi 上懂代理的客户端随后就可以发出请求。socks5h前缀把目标名称交给代理,因此解析和连接都发生在笔记本一侧,也与PermitRemoteOpen中指定的目标一致:

curl --proxy socks5h://127.0.0.1:1080 \
  --noproxy '' https://app.corp.example/

保留常规的证书校验和应用自身的身份验证。代理改变的是 TCP 连接在哪里建立;它不会让一个没有身份验证的服务变安全,也不会把自签名的端点变成可信端点。

笔记本仍然需要自己的路由,以及到 SSH 服务器和应用两边的解析器访问。它的 SSH 客户端流量必须由已获批准的 VPN 策略选中,而 split-tunnel 例外可能把这份流量送到与你测试时不同的路径上。

验证这条路径,然后关掉它

确认监听端出现在你预期的位置,并且只有预期的目标会应答:

$ ss -ltnp | grep 1080
LISTEN 0 128 127.0.0.1:1080 0.0.0.0:*
$ curl -sS -o /dev/null -w '%{http_code}\n' \
    --proxy socks5h://127.0.0.1:1080 https://app.corp.example/

ExitOnForwardFailure可以证明转发已经建立,但依然无法证明应用登录在下游会成功。要测应用,而不只是测端口。一个接受之后让每个请求都失败的监听端,通常意味着目标名称或笔记本的路由不对,而不是隧道出了问题。

这种安排只承载 TCP。UDP、ICMP 以及忽略代理设置的程序都在它之外,而 SSH 会话结束的那一刻转发就结束。用完就停掉那个会话,并撤掉你在应用里加上的代理设置。

要长期访问,请改用网络所有者支持的正规登记或受管网关。用反向代理去规避 VPN 的设备或资源限制是选错了工具;在限制之内使用,它只是一条有明确边界的授权应用路径。

相关文章