路漫漫其修远兮
吾将上下而求索

ssh学习:反向隧道建立

这篇文章主要介绍了如何利用SSH 反向隧道穿透NAT,并演示了如何维持一条稳定的SSH 隧道。

假设有机器A 和B,A 有公网IP,B 位于NAT 之后并无可用的端口转发,现在想由A 主动向B 发起SSH 连接。由于B 在NAT 后端,无可用公网IP + 端口 这样一个组合,所以A 无法穿透NAT,这篇文章应对的就是这种情况。

这里默认你的系统init 程序为systemd

首先有如下约定,因为很重要所以放在前面:

机器代号 机器位置 地址 账号 sshd/ssh端口 是否需要运行sshd
A 位于公网 abc.cn usera 20000
B 位于NAT之后 localhost userb 22
C 位于NAT之后 localhost userc 22222

SSH 反向隧道

这种手段实质上是由B 向A 主动地建立一个SSH 隧道,将A 的60000 端口转发到B 的22 端口上,只要这条隧道不关闭,这个转发就是有效的。有了这个端口转发,只需要访问A 的60000 端口反向连接B 即可。

首先在A公网机器上面创建用户usera,并配置密码

在B 上机器上建立一个SSH 隧道,将A 的60000端口转发到B 的22 端口上:

B $ ssh -p 20000 -qngfNTR 60000:localhost:22 usera@abc.cn   然后输入密码

B机器执行进程查看命令就有以下进程
ssh -p 20000 -qngfNTR 60000:localhost:22 usera@abc.cn

然后在A 上利用60000端口反向SSH 到B:这里其实还是B机器和A机器的20000端口在通信,60000端口是为了区分不同机器,防火墙不用放开

A $ ssh -p 60000 userb@localhost

要做的事情其实就是这么简单。A机器就可以连接到B机器了

隧道的维持,稳定性维持

然而不幸的是SSH 连接是会超时关闭的,如果连接关闭,隧道无法维持,那么A 就无法利用反向隧道穿透B 所在的NAT 了,为此我们需要一种方案来提供一条稳定的SSH 反向隧道。

一个最简单的方法就是autossh,这个软件会在超时之后自动重新建立SSH 隧道,这样就解决了隧道的稳定性问题

下面在B 上做之前类似的事情,不同的是该隧道会由autossh 来维持:

-M 参数指定的端口用来监听隧道的状态,与端口转发无关。下面的没有使用密码,而是事先将B的公钥添加到A主机的usera用户下面

B $ autossh -M 60010 -p 20000 -qngfNTR 60000:localhost:22 usera@abc.cn

之后你可以在A 上通过6766 端口访问B 了:

A $ ssh -p 60000 userb@localhost

隧道的自动建立

然而这又有了另外一个问题,如果B 重启隧道就会消失。那么需要有一种手段在B 每次启动时使用autossh 来建立SSH 隧道。很自然的一个想法就是做成服务,之后会给出在systemd 下的一种解决方案。

“打洞”

之所以标题这么起,是因为自己觉得这件事情有点类似于UDP 打洞,即通过一台在公网的机器,让两台分别位于各自NAT 之后的机器可以建立SSH 连接。

下面演示如何使用SSH 反向隧道,让C 连接到B。

首先在A 上编辑sshd 的配置文件/etc/ssh/sshd_config,将GatewayPorts 开关打开:

GatewayPorts yes

然后重启sshd:

A $ sudo systemctl restart sshd

然后在B 上对之前用到的autossh 指令略加修改:

B $ autossh -M 60010 -p 20000 -qngfNTR '*:60000:localhost:22' usera@abc.cn

之后在C 上利用A 的60000 端口SSH 连接到B:

C $ ssh -p 60000 userb@abc.cn

至此你已经轻而易举的穿透了两层NAT。

最终的解决方案

整合一下前面提到的,最终的解决方案如下:

首先打开A 上sshd 的GatewayPorts 开关,并重启sshd(如有需要)。

然后在B 上新建一个用户autossh,根据权限最小化思想,B 上的autossh 服务将以autossh 用户的身份运行,以尽大可能避免出现安全问题:

B $ sudo useradd -m autossh
B $ sudo passwd autossh

紧接着在B 上为autossh 用户创建SSH 密钥,并上传到A:

B $ su - autossh
B $ ssh-keygen -t 'rsa' -C 'autossh@B'
B $ ssh-copy-id usera@abc.cn

注意该密钥不要设置密码,也就是运行ssh-keygen 指令时尽管一路回车,不要输入额外的字符。

然后在B 上创建以autossh 用户权限调用autossh 的service 文件。将下面文本写入到文件/lib/systemd/system/autossh.service,并设置权限为644:

[Unit]
Description=Auto SSH Tunnel
After=network-online.target
[Service]
User=autossh
Type=simple
ExecStart=/usr/bin/autossh -M 60010 -p 20000 -qngfNTR '*:60000:localhost:22' usera@abc.cn -i /home/autossh/.ssh/id_rsa
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=always
[Install]
WantedBy=multi-user.target
WantedBy=graphical.target

在B 上让network-online.target 生效:

B $ systemctl enable NetworkManager-wait-online

如果你使用systemd-networkd,你需要启用的服务则应当是systemd-networkd-wait-online 。

然后设置该服务自动启动:

B $ sudo systemctl enable autossh

如果你愿意,在这之后可以立刻启动它:

B $ sudo systemctl start autossh

然后你可以在A 上使用这条反向隧道穿透B 所在的NAT SSH 连接到B:

A $ ssh -p 60000 userb@localhost

或者是在C 上直接穿透两层NAT SSH 连接到B:

C $ ssh -p 60000 userb@abc.cn

参考文档:http://arondight.me/2016/02/17/%E4%BD%BF%E7%94%A8SSH%E5%8F%8D%E5%90%91%E9%9A%A7%E9%81%93%E8%BF%9B%E8%A1%8C%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F/

未经允许不得转载:江哥架构师笔记 » ssh学习:反向隧道建立

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址