SSH 连接是非常方便的连接形式,同时还支持在各设备间建立加密通道,也就是建立代理。在使用 SSH 服务打破网络边界文章里讲得很好,这里总结一下,加上 .ssh/config 里的配置方法。

正向代理

一般使用时常见需要在本地访问远程设备的场景,比如本地访问远程服务器所在网络中的服务。

ssh -L localport:remoteaddress:remoteport user@remote

此时本地访问 localport 时,ssh 会转发到远程上去访问 remoteaddress:remoteport。如果 remoteaddress 是 localhost,可以不写,如-L 1234::1235,本地访问 1234 端口时会访问远程的 1235 端口。

另外,ssh -W host:port也可以作为代理使用,类似于nc host port,将标准输入输出流进行转发。后面的ssh -J也是基于这个命令。

反向代理

反过来,如果远程想访问本地的服务,可以设置反向代理。

ssh -R remoteport:localaddress:localport user@remote

此时,在远程上访问 remoteport 时会转发到本地。

注:如果前面localhost略去,此时本地收到的连接可能来自 IPv6 本地链接地址(fe80::/10),而不是回环地址(::1/128),导致一些仅限回环地址的应用无法使用。所以最好还是写上localhost为好。

SSH 跳板

一个很常见的场景是用 SSH 代理 SSH,即有两台机器 A 和 B,其中本地可以访问 A 但本地不能访问 B,而 A 可以访问到 B。

这种情况下,可以连接到 A 后建立正向代理,转发 B 的 SSH 端口到本地,再连接到该端口。而 SSH 针对这一情况已经进行简化,可以直接使用下列指令以 A 作为跳板访问 B。这个过程中使用的是上面提到的ssh -W形式的代理。

ssh -J user@A user@B

通用代理

如果代理不是以ssh -W的形式提供,可以用ProxyCommand设置一个类似于nc host port的代理程序,将输入输出流转发到对应端口。在命令行时使用-oProxyCommand=进行设置。

配置文件

以上设置可以直接在 .ssh/config 中配置,省去每次输入一大串指令的麻烦。

  • -L localport:remoteaddress:remoteport对应 LocalForward localport remoteaddress:remoteport。 和命令行参数的区别是第一个端口号和后面的地址间用空格而不是冒号隔开。
  • -R remoteport:localaddress:localport对应RemoteForward remoteport localaddress:localport
  • -J A对应ProxyJump A

性能

要注意的是,上述所有的代理或者转发都是通过 SSH 的安全信道进行,也就是说需要在 SSH 连接的两端进行加密和解密,比如客户端 H 使用ssh -J A B命令将 A 作为跳板访问 B,除去传输层如 WireGuard 可能进行的加密外,在应用层还要加密两次。比如一个信息从 B 发出时,需要:

  1. B 把原信息用 B 和 H 建立的密钥加密,发送到 A。
  2. A 并不知道 B 发送的信息内容,为了保证安全,A 用 A 和 H 建立的密钥加密,发送到 B。
  3. H 用 A 和 H 建立的密钥解密,得到 A 所收到的内容,再用 B 和 H 建立的密钥解密,得到原信息。

这个过程中,SSH 服务端各需要加密一次,而连接 SSH 服务器的客户端需要加密多次。