本文整理自https://www.ibm.com/developerworks/cn/linux/l-cn-sshforward/index.html,主要是将示例改成自己熟悉的 redis。
端口转发
SSH 会自动加密和解密所有SSHClient
和SSHServer
之间的网络数据,同时还提供了一个很有用的功能,这就是端口转发
。它能够将其他 TCP 端口的网络数据通过 SSH 链接来转发,并且自动提供加密和解密服务。这一过程有时也被叫做隧道(tunneling)
。
假设SSHServer
安装了其它服务,但是只开放了 SSH 的默认端口,那么就可以通过端口转发来绕过防火墙限制,达到与服务通讯的目的。
SSH 端口转发提供了两个功能:
- 加密
SSHClient
与SSHServer
之间的通讯数据。 - 绕过防火墙限制建立之前无法建立的 TCP 连接。
如上图所示,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
注意,如果当前用户不是跟远程登录的用户名不一致,则需要指定用户名,也就是以上的-l
参数。如无意外,执行完以上命令后,已经登录了SSHServer
,这时千万不要退出,因为一退出就相当于关闭了 SSH 连接,端口转发当然也失效了。以上命令的意思就是SSHClient
和SSHServer
分别跟各自机器上的 9999、6379 端口建立连接。以上的localhost
是相对于SSHServer
的地址,跟SSHClient
无关。
现在打开另一个本地终端窗口,执行以下命令即可使用redisServe
服务:
$ redis-cli -p 9999
127.0.0.1:9999>
redisClient
跟redisServer
的通讯过程如下:
redisClient
将数据通过 9999 端口发送到SSHClient
;SSHClient
加密数据并转发到SSHServer
;SSHServer
解密数据并将其转发到redisServer
监听的 6379 端口上;redisServer
将数据发送到redisClient
的过程是以上的逆过程;
这里有几个地方需要注意:
- SSH 端口转发是通过 SSH 连接建立起来的,我们必须保持这个 SSH 连接以使端口转发保持生效。一旦关闭了该连接,端口转发随之关闭。
- 只能在建立 SSH 连接的同时创建端口转发,不能给一个已存在的 SSH 连接增加端口转发。
<remote host>
之所以用 localhost,是因为相对于<SSH hostname>
来说,redis-server 的 hostname 就是 localhost。如果没有限制其它地址,那么也可以用其它可用的 hostname。<remote host>
和<SSH hostname>
不一定在同一台机器。-
其它机器无法使用本机的端口转发,不过 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>
远程端口转发的通讯过程跟本地端口转发相同。
本地转发与远程转发的对比与分析
本地转发时,redisClient
跟SSHClient
位于同一端,redisServer
跟SSHServer
位于同一端。
远程转发时,redisClient
跟SSHServer
位于同一端,redisServer
跟SSHClient
位于同一端。
其他类型的转发
动态转发实例分析
前面的本地转发和远程转发,前提都是要求有一个固定的应用服务端的端口号,如redisServer
的 6379 端口。如果没有这个端口怎么办?例如要访问某个网站。
当我们在一个不安全的 WiFi 环境下上网,用 SSH 动态转发来保护我们的网页浏览等无疑是十分必要的。动态转发的命令格式如下:
$ ssh -D <local port> <SSH Server>
例如:
$ ssh -D 9999 -fN root@45.32.xxx.yyy
这里其实是用 SSH 创建了一个 SOCKS 代理服务,以上的示例命令跟之前的相比有点区别,多了-fN
参数,这个是为了让端口转发后台运行,不用像之前那样直接登录并且退出登录会导致转发中断。另外,-l
参数可以用username@hostname
来代替。接着设置一下浏览器的代理就可以愉快地冲”浪”了~
X 协议转发实例分析
暂无需求,本文省略。
总结
SSH 端口转发的思路是通过将 TCP 连接转发至 SSH 通道上以解决数据加密以及突破防火墙的种种限制。