SSH 端口转发

2020/08/13 计算机网络

本文整理自https://www.ibm.com/developerworks/cn/linux/l-cn-sshforward/index.html,主要是将示例改成自己熟悉的 redis。

端口转发

SSH 会自动加密和解密所有SSHClientSSHServer之间的网络数据,同时还提供了一个很有用的功能,这就是端口转发。它能够将其他 TCP 端口的网络数据通过 SSH 链接来转发,并且自动提供加密和解密服务。这一过程有时也被叫做隧道(tunneling)

假设SSHServer安装了其它服务,但是只开放了 SSH 的默认端口,那么就可以通过端口转发来绕过防火墙限制,达到与服务通讯的目的。

SSH 端口转发提供了两个功能:

  • 加密SSHClientSSHServer之间的通讯数据。
  • 绕过防火墙限制建立之前无法建立的 TCP 连接。

SSH 端口转发

如上图所示,TCP PortA 通过 SSH 端口转发实现了跟 TCP PortB 的通讯。

本地转发与远程转发

本地转发实例

本地端口转发的命令格式:

$ ssh -L <local port>:<remote host>:<remote port> <SSH hostname>

现在SSHServer的 redis 服务(以下用redisServer代替),但是没有开放 redis 端口。使用端口转发的命令如下:

$ ssh -L 9999:localhost:6379 -l root 192.168.10.68

local

注意,如果当前用户不是跟远程登录的用户名不一致,则需要指定用户名,也就是以上的-l参数。如无意外,执行完以上命令后,已经登录了SSHServer,这时千万不要退出,因为一退出就相当于关闭了 SSH 连接,端口转发当然也失效了。以上命令的意思就是SSHClientSSHServer分别跟各自机器上的 9999、6379 端口建立连接。以上的localhost是相对于SSHServer的地址,跟SSHClient无关。

现在打开另一个本地终端窗口,执行以下命令即可使用redisServe服务:

$ redis-cli -p 9999
127.0.0.1:9999> 

redisClientredisServer的通讯过程如下:

  • redisClient将数据通过 9999 端口发送到SSHClient
  • SSHClient加密数据并转发到SSHServer
  • SSHServer解密数据并将其转发到redisServer监听的 6379 端口上;
  • redisServer将数据发送到redisClient的过程是以上的逆过程;

这里有几个地方需要注意:

  1. SSH 端口转发是通过 SSH 连接建立起来的,我们必须保持这个 SSH 连接以使端口转发保持生效。一旦关闭了该连接,端口转发随之关闭。
  2. 只能在建立 SSH 连接的同时创建端口转发,不能给一个已存在的 SSH 连接增加端口转发。
  3. <remote host>之所以用 localhost,是因为相对于<SSH hostname>来说,redis-server 的 hostname 就是 localhost。如果没有限制其它地址,那么也可以用其它可用的 hostname。
  4. <remote host><SSH hostname>不一定在同一台机器。
  5. 其它机器无法使用本机的端口转发,不过 SSH 同时提供了 GatewayPorts 关键字,可以指定它和其他机器共享这个本地端口转发。

    $ ssh -g -L <local port>:<remote host>:<remote port> <SSH hostname>
    
远程转发实例分析

现在假设由于网络或防火墙的原因,我们不能用 SSH 直接从本机连接到服务器,但是反向连接却是被允许的,这时我们自然要选择远程端口转发。它的命令格式如下:

$ ssh -R <local port>:<remote host>:<remote port> <SSH hostname>

以上命令应该在服务器执行,这时 192.168.10.111 是我们的本机,这时它是SSHServer

$ ssh -R 9999:localhost:6379 -l root 192.168.10.111

注意,虽然命令是在服务器执行的,但是<local port>指的仍然是redisClient主机上的端口,<remote host><remote port>指的是redisServer主机;而<SSH hostname>就跟之前的不一样了,现在它指的是redisClient所在主机。

不出意外,现在已经登录到redisClient所在主机了,执行以下命令即可跟redisServer通讯:

$ redis-cli -p 9999
127.0.0.1:9999> 

远程端口转发的通讯过程跟本地端口转发相同。

本地转发与远程转发的对比与分析

本地转发时,redisClientSSHClient位于同一端,redisServerSSHServer位于同一端。

远程转发时,redisClientSSHServer位于同一端,redisServerSSHClient位于同一端。

其他类型的转发

动态转发实例分析

前面的本地转发和远程转发,前提都是要求有一个固定的应用服务端的端口号,如redisServer的 6379 端口。如果没有这个端口怎么办?例如要访问某个网站。

当我们在一个不安全的 WiFi 环境下上网,用 SSH 动态转发来保护我们的网页浏览等无疑是十分必要的。动态转发的命令格式如下:

$ ssh -D <local port> <SSH Server>

例如:

$ ssh -D 9999 -fN root@45.32.xxx.yyy

local

这里其实是用 SSH 创建了一个 SOCKS 代理服务,以上的示例命令跟之前的相比有点区别,多了-fN参数,这个是为了让端口转发后台运行,不用像之前那样直接登录并且退出登录会导致转发中断。另外,-l参数可以用username@hostname来代替。接着设置一下浏览器的代理就可以愉快地冲”浪”了~

socks

X 协议转发实例分析

暂无需求,本文省略。

总结

SSH 端口转发的思路是通过将 TCP 连接转发至 SSH 通道上以解决数据加密以及突破防火墙的种种限制。

Search

    Table of Contents