SSH 除了登录服务器,还有一大用途,就是作为加密通信的中介,充当两台服务器之间的通信加密跳板,使得原本不加密的通信变成加密通信。这个功能称为端口转发(port forwarding),又称 SSH 隧道(tunnel)。
端口转发有两个主要作用:
(1)将不加密的数据放在 SSH 安全连接里面传输,使得原本不安全的网络服务增加了安全性,比如通过端口转发访问 Telnet、FTP 等明文服务,数据传输就都会加密。
(2)作为数据通信的加密跳板,绕过网络防火墙。
端口转发有三种使用方法:动态转发,本地转发,远程转发。下面逐一介绍。
动态转发指的是,本机与 SSH 服务器之间创建了一个加密连接,然后本机内部针对某个端口的通信,都通过这个加密连接转发。它的一个使用场景就是,访问所有外部网站,都通过 SSH 转发。
动态转发需要把本地端口绑定到 SSH 服务器。至于 SSH 服务器要去访问哪一个网站,完全是动态的,取决于原始通信,所以叫做动态转发。
$ ssh -D local-port tunnel-host -N
上面命令中,-D
表示动态转发,local-port
是本地端口,tunnel-host
是 SSH 服务器,-N
表示这个 SSH 连接只进行端口转发,不登录远程 Shell,不能执行远程命令,只能充当隧道。
举例来说,如果本地端口是2121
,那么动态转发的命令就是下面这样。
$ ssh -D 2121 tunnel-host -N
注意,这种转发采用了 SOCKS5 协议。访问外部网站时,需要把 HTTP 请求转成 SOCKS5 协议,才能把本地端口的请求转发出去。
下面是 SSH 隧道建立后的一个使用实例。
$ curl -x socks5://localhost:2121 http://www.example.com
上面命令中,curl 的-x
参数指定代理服务器,即通过 SOCKS5 协议的本地2121
端口,访问http://www.example.com
。
如果经常使用动态转发,可以将设置写入 SSH 客户端的用户个人配置文件(~/.ssh/config
)。
DynamicForward tunnel-host:local-port
本地转发(local forwarding)指的是,SSH 服务器作为中介的跳板机,建立本地计算机与特定目标网站之间的加密连接。本地转发是在本地计算机的 SSH 客户端建立的转发规则。
它会指定一个本地端口(local-port),所有发向那个端口的请求,都会转发到 SSH 跳板机(tunnel-host),然后 SSH 跳板机作为中介,将收到的请求发到目标服务器(target-host)的目标端口(target-port)。
$ ssh -L local-port:target-host:target-port tunnel-host
上面命令中,-L
参数表示本地转发,local-port
是本地端口,target-host
是你想要访问的目标服务器,target-port
是目标服务器的端口,tunnel-host
是 SSH 跳板机。
举例来说,现在有一台 SSH 跳板机tunnel-host
,我们想要通过这台机器,在本地2121
端口与目标网站www.example.com
的80端口之间建立 SSH 隧道,就可以写成下面这样。
$ ssh -L 2121:www.example.com:80 tunnel-host -N
然后,访问本机的2121
端口,就是访问www.example.com
的80端口。
$ curl http://localhost:2121
注意,本地端口转发采用 HTTP 协议,不用转成 SOCKS5 协议。
另一个例子是加密访问邮件获取协议 POP3。
$ ssh -L 1100:mail.example.com:110 mail.example.com
上面命令将本机的1100端口,绑定邮件服务器mail.example.com
的110端口(POP3 协议的默认端口)。端口转发建立以后,POP3 邮件客户端只需要访问本机的1100端口,请求就会通过 SSH 跳板机(这里是mail.example.com
),自动转发到mail.example.com
的110端口。
上面这种情况有一个前提条件,就是mail.example.com
必须运行 SSH 服务器。否则,就必须通过另一台 SSH 服务器中介,执行的命令要改成下面这样。
$ ssh -L 1100:mail.example.com:110 other.example.com
上面命令中,本机的1100端口还是绑定mail.example.com
的110端口,但是由于mail.example.com
没有运行 SSH 服务器,所以必须通过other.example.com
中介。本机的 POP3 请求通过1100端口,先发给other.example.com
的22端口(sshd 默认端口),再由后者转给mail.example.com
,得到数据以后再原路返回。
注意,采用上面的中介方式,只有本机到other.example.com
的这一段是加密的,other.example.com
到mail.example.com
的这一段并不加密。
这个命令最好加上-N
参数,表示不在 SSH 跳板机执行远程命令,让 SSH 只充当隧道。另外还有一个-f
参数表示 SSH 连接在后台运行。
如果经常使用本地转发,可以将设置写入 SSH 客户端的用户个人配置文件(~/.ssh/config
)。
Host test.example.com
LocalForward client-IP:client-port server-IP:server-port
远程转发指的是在远程 SSH 服务器建立的转发规则。
它跟本地转发正好反过来。建立本地计算机到远程计算机的 SSH 隧道以后,本地转发是通过本地计算机访问远程计算机,而远程转发则是通过远程计算机访问本地计算机。它的命令格式如下。
$ ssh -R remote-port:target-host:target-port -N remotehost
上面命令中,-R
参数表示远程端口转发,remote-port
是远程计算机的端口,target-host
和target-port
是目标服务器及其端口,remotehost
是远程计算机。
远程转发主要针对内网的情况。下面举两个例子。
第一个例子是内网某台服务器localhost
在 80 端口开了一个服务,可以通过远程转发将这个 80 端口,映射到具有公网 IP 地址的my.public.server
服务器的 8080 端口,使得访问my.public.server:8080
这个地址,就可以访问到那台内网服务器的 80 端口。
$ ssh -R 8080:localhost:80 -N my.public.server
上面命令是在内网localhost
服务器上执行,建立从localhost
到my.public.server
的 SSH 隧道。运行以后,用户访问my.public.server:8080
,就会自动映射到localhost:80
。
第二个例子是本地计算机local
在外网,SSH 跳板机和目标服务器my.private.server
都在内网,必须通过 SSH 跳板机才能访问目标服务器。但是,本地计算机local
无法访问内网之中的 SSH 跳板机,而 SSH 跳板机可以访问本机计算机。
由于本机无法访问内网 SSH 跳板机,就无法从外网发起 SSH 隧道,建立端口转发。必须反过来,从 SSH 跳板机发起隧道,建立端口转发,这时就形成了远程端口转发。跳板机执行下面的命令,绑定本地计算机local
的2121
端口,去访问my.private.server:80
。
$ ssh -R 2121:my.private.server:80 -N local
上面命令是在 SSH 跳板机上执行的,建立跳板机到local
的隧道,并且这条隧道的出口映射到my.private.server:80
。
显然,远程转发要求本地计算机local
也安装了 SSH 服务器,这样才能接受 SSH 跳板机的远程登录。
执行上面的命令以后,跳板机到local
的隧道已经建立了。然后,就可以从本地计算机访问目标服务器了,即在本机执行下面的命令。
$ curl http://localhost:2121
本机执行上面的命令以后,就会输出服务器my.private.server
的 80 端口返回的内容。
如果经常执行远程端口转发,可以将设置写入 SSH 客户端的用户个人配置文件(~/.ssh/config
)。
Host remote-forward
HostName test.example.com
RemoteForward remote-port target-host:target-port
完成上面的设置后,执行下面的命令就会建立远程转发。
$ ssh -N remote-forward
# 等同于
$ ssh -R remote-port:target-host:target-port -N test.example.com
下面看两个端口转发的实例。
VPN 用来在外网与内网之间建立一条加密通道。内网的服务器不能从外网直接访问,必须通过一个跳板机,如果本机可以访问跳板机,就可以使用 SSH 本地转发,简单实现一个 VPN。
$ ssh -L 2080:corp-server:80 -L 2443:corp-server:443 tunnel-host -N
上面命令通过 SSH 跳板机,将本机的2080
端口绑定内网服务器的80
端口,本机的2443
端口绑定内网服务器的443
端口。
端口转发可以有多级,比如新建两个 SSH 隧道,第一个隧道转发给第二个隧道,第二个隧道才能访问目标服务器。
首先,在本机新建第一级隧道。
$ ssh -L 7999:localhost:2999 tunnel1-host
上面命令在本地7999
端口与tunnel1-host
之间建立一条隧道,隧道的出口是tunnel1-host
的localhost:2999
,也就是tunnel1-host
收到本机的请求以后,转发给自己的2999
端口。
然后,在第一台跳板机(tunnel1-host
)执行下面的命令,新建第二级隧道。
$ ssh -L 2999:target-host:7999 tunnel2-host -N
上面命令将第一台跳板机tunnel1-host
的2999
端口,通过第二台跳板机tunnel2-host
,连接到目标服务器target-host
的7999
端口。
最终效果就是,访问本机的7999
端口,就会转发到target-host
的7999
端口。