SSH 端口转发与自动维护

1 ssh 端口转发

ssh(Secure Shell)是一种网络传输协议,它能够在客户端和服务器之间创建安全的隧道连接。最常见的功能就是远程登录,这不是本文的重点,不过也会涉及到密钥登录(避免输入密码)。本文将使用ssh做端口转发,ssh端口转发有三种模式:

  • 本地端口转发(Local Port Forwarding)
  • 远程端口转发(Remote Port Forwarding)
  • 动态端口转发(Dynamic Port Forwarding)

1.1 本地端口转发

本地主机登录服务器后,将本地主机指定的端口映射到服务器指定的端口,从而实现用户在访问本地主机时,就等于访问服务器的效果。例如:服务器上有一个web站点访问地址是http://192.168.0.3:5000 ,服务器防火墙上并没有开启5000端口,如果直接输入网址,是访问不到该web站点的。这时可以利用本地端口转发功能,将请求本地主机的4000端口的数据转发到服务器上的5000端口上,具体做法如下:

ssh -fNL 4000:192.168.0.3:5000 root@192.168.0.3

上面命令的中

  • -f 选项表示本地主机的ssh客户端在后台运行

  • -N 选项表示不执行远程命令,只用于转发端口,及本地主机在登录了192.168.0.3后,不会分配一个tty窗口。

  • -L 选项表示这是一个本地转发模式,它的参数是 port:host:hostport,对应到这里是4000:192.168.0.3:5000

    • port 参数表示本地主机的端口,及要通过该端口转发数据到目标主机

    • host:hostport 参数表示经过远程服务器(及使用ssh登录的服务器ssh root@192.168.0.3)转发的目标主机和端口。在这个例子中,目标主机和远程服务器都是192.168.0.3。既然他们是一台主机,那么也可以用下面的命令来代替:

    • ssh -fNL 4000:localhost:5000 root@192.168.0.3
      # 或者
      ssh -fNL 4000:127.0.0.1:5000 root@192.168.0.3
      

      上面的localhost和127.0.0.1是相对于通过ssh登录的远程服务器的。

  • root@192.168.0.3 是ssh的登录命令,只有登录了远程服务器后,才能执行端口转发

执行完上面的命令后,可以在本地主机的浏览器上输入http://localhost:4000 访问远程服务器192.168.0.3:5000上的web站点。

现在,假如有另外一台服务器10.1.1.10,上面部署了一个web应用(地址为http://10.1.1.10 ,端口为80),本地主机不能直接访问该站点的web服务器,而192.168.0.3上能够访问得到这台服务器。根据上面的知识,我们知道,10.1.1.10:80是我们的目标主机和端口,192.168.0.3作为远程服务器,与我们本地主机和目标主机的网络都是连通的,我们就可以通过以下命令将本地主机的4000端口的数据转发到目标主机(10.1.1.10)的80端口上。

ssh -fNL 4000:10.1.1.10:80 root@192.168.0.3

此时,我们输入http://localhost:4000,就能访问到http://10.1.1.10站点的内容

1.2 远程端口转发

远程端口转发是将远程服务器端口映射到本地端口,方便其他不能直接访问本地网络的主机访问。比如,家里的电脑要访问公司内部服务器(192.168.0.5)上的web站点(部署在80端口上),因为它们是不能直接通信的,所以需要有一台远程服务器(比如:123.123.123.123)作端口转发。

此时,我们需要在远程服务器上开一个端口,这里假设为8000端口,将8000端口与内部服务器上的80端口做绑定,然后我们在家里只需要访问远程服务器的8000端口,即可与访问到内部服务器的web站点。具体做法是,在内部服务器192.168.0.5上执行如下指令:

ssh -fNR 8000:localhost:80 root@123.123.123.123

在上面的命令中:

  • -R 选项代表的是远程端口转发模式,它的参数是port:host:hostport,对应这里的是8000:localhost:80
    • port-L 参数中的port不同,-L参数中port代表的是本地的端口,而这里的port代表的是远程服务器123.123.123.123的端口。
    • host:hostport 参数表示要绑定的本地端口,这里的localhost代表的我们操作的内部服务器,即192.168.0.5,也可以变为127.0.0.1或192.168.0.5。可以是这台内部服务器能访问的任何其他服务器资源,这里就不在赘述。

执行完上面的命令后,我们在家里通过输入http://123.123.123.123:8000,即可访问内部服务器上http://192.168.0.5 的站点。

1.3 动态端口转发

当目标主机不定时,我们就需要使用动态端口转发。比如,公司内部只有一台服务器192.168.0.3能够访问外网,现在我们需要到网上去查询一些资料,这些资料的来至不同的服务器,也就是我们的目标地址是不固定的,那么我们就需要这台服务器192.168.0.3为我们做动态端口转发。具体指令如下:

ssh -fND 8000 root@192.168.0.3
  • -D 参数表示使用动态端口转发模式,8000表示本地主机要绑定的端口。当本地主机登录远程服务器后,8000端口所收到的数据将会转发到服务器192.168.0.3上,再由这台服务器为我们动态的转发到目标地址。

比如,我们要访问百度,我们需要将浏览器发出的请求先转发到localhost:8000端口上,由8000端口将数据传输到服务器192.168.0.3上,再由服务器动态的将请求转发至百度服务器上,及实现了本地主机上网查找资料的目的。这里后面的两步都已经完成,只需要将第一步在本地主机上把浏览器的请求转发到本地的8000端口上。我们可以使用浏览器的socks代理,将其请求发送到本地端口即可。

2 使用autossh自动维护连接

不论使用哪种模式,SSH做端口转发时,都需要在本地主机和远程服务器之间建立连接(隧道)。这种连接是不稳定的,当网络情况不好时,连接可能会中断。autossh可以解决这种问题,它会在本地主机和远程服务器上做连接检查(发送测试数据到指定端口),如果发现连接中断后,会自动重连。

上面的描述中,有两个问题需要解决

  1. 在什么端口做连通性检测
  2. 如何重连

2.1 使用密钥登录服务器

使用ssh连接服务器时,使用用户名密码登录,每次都要输入密码。如果是自动重连,我们就需要免密登录服务器。这里我们要解决的是ssh连接远程服务器时需要输入用户名密码的问题。ssh除了用户名密码登录外,还有一种登录方式是使用密钥登录,大致过程如下:

我们在本地主机上生成一个密钥对,将公钥放在远程服务器中,私钥保存在本地主机上,当使用密钥登录远程服务器时,流程如下:

  1. 客户端发起连接请求,并将公钥发送至服务器
  2. 服务器比对客户端发送的公钥是否与本地的公钥相同,如相同则用公钥加密一个随机字符,发送给客户端,这个认证机制叫challenge/response(质疑-应答认证)
  3. 客户端在收到challenge后,使用私钥解密,并将结果回传给服务器
  4. 服务器验证客户端回传的数据后,建立ssh连接

生成密钥

使用ssh-keygen生成密钥对,ssh-keygen有很多参数选项,下面列出我用到的:

  • -t 表示生成密钥的类型,有dsa,rsa等,一般用rsa加密算法
  • -C 后面跟附加信息,一般我是跟的个人的名字
ssh-keygen -t rsa -C zebra

这里在输入私钥密码时,选择为空,为后续autossh自动重连提供方便。生成完后,默认会在用户家目录的.ssh目录下生成id_rsa和id_rsa.pub两个文件。id_rsa为私钥文件,id_rsa.pub为公钥文件,可以看到id_rsa.pub公钥文件的最后有我们-C选项的附加信息zebra,这样我们将公钥上传至服务器后,能够很方便的找到自己的公钥。

上传公钥

使用ssh-copy-id上传公钥文件

ssh-copy-id -i root@123.123.123.123

在ssh-copy-id命令中,-i选项后面跟公钥文件的路径,如果不加参数,默认是找~/.ssh/id_rsa.pub公钥文件,我们使用的默认公钥文件,root@123.123.123.123是指要将公钥数据存放在远程服务器的root用户下,注意:端口转发必须是连接root用户。执行完上面的命令后,会在root家目录下的.ssh目录中,生成authorized_keys文件,里面保存做上传的公钥内容。

2.2 使用autossh维护连接

在客户端使用命令yum install autossh安装autossh,autossh在ssh的基础上新增加一个参数-M来检查隧道的连通性,原理是这样的,autossh会在-M指定的端口上发送测试数据,并在这个端口+1的端口上接收回响数据。比如:我们设置-M 20000,autossh 会在20000端口上发送测试数据,并在20001端口上接收回传数据。

举例说明,我要执行远程端口转发的功能,将本地80端口映射到远程服务器的8000端口,使用ssh命令如下:

ssh -fNR 8000:localhost:80 root@123.123.123.123

如果使用autossh可执行这个命令

autossh -M 20000 -fNR 8000:localhost:80 root@123.123.123.123

autossh会在20000和20001端口上检查本地客户端和远程服务器的连通性,如果发现隧道断开了,会在执行自动连接,因为我们使用了密钥登录,且没有设置密钥的密码,所以就会达到自动重连的效果。

推荐阅读更多精彩内容