使用 autossh 建立反向 SSH 隧道管理个人计算机

设你有这样一个需求:你在家中有一台 Linux/Unix 设备,可以是路由器、NAS 或者台式机,存有自己常用工具或者数据,想要在自己外出时也能随时访问。那么你现在的目的和我一样了,你所需要的是少许 Linux/Unix 经验以及一台能够从公网访问的中继服务器。我们所需要的技术是通过 SSH 隧道搭建一个反向代理。

配置

在你的 SSH 配置文件中加入这一行 GatewayPorts clientspecified。可以直接使用命令:sudo echo “GatewayPorts clientspecifie” >> /etc/ssh/sshd_config。

然后重新加载 SSH 配置文件:sudo reload ssh。

然后在本地建立连接: ssh -f -R 0.0.0.0:20000:localhost:22 local_user@a_a_a_a

现在还有两个问题:你需要保持终端开启防止 SSH 进程被关闭;由于网络故障/波动导致 SSH 终断时无法自动重连。

前者可以使用 -N 参数来解决,后者需要 supervisor 等第三方监控工具。

使用 autossh 代替 ssh

不过我们还有一个更常用的选择方案:autossh:

autossh -M 20001 
-fN -o "PubkeyAuthentication=yes" 
-o "StrictHostKeyChecking=false" -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" 
-R a_a_a_a:20000:localhost:22 
-p 8383 remote_user@a_a_a_a

说明:

  • -M 20001 选项指定中继服务器上的监视端口,用于交换监视 SSH 会话的测试数据,需要保证该端口在服务器上未被占用。
  • -o 用于设置 autossh 参数。
  • -f 指定 autossh 在后台运行,并不会传给 ssh。和 ssh 的 -f 不一样,autossh 指定 -f 时将无法寻求密码。指定 -f 时,会将环境变量 AUTOSSH_GATETIME 覆盖为 0!

开机启动

在 Ubuntu 中我们可以使用 systemd 管理 autossh 的开机启动问题(旧版本中可以使用 init.d)。配置很简单,只需要创建一个 /etc/systemd/system/remote-autossh.service 文件:

[Unit]
Description=AutoSSH service for remote tunnel
After=network-online.target

[Service]
User=your_username
ExecStart=/usr/bin/autossh -M 20001 -N -o "PubkeyAuthentication=yes" -o "StrictHostKeyChecking=false" -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" -R a_a_a_a:20000:localhost:22 -p 8383 remote_user@a_a_a_a

[Install]
WantedBy=multi-user.target

这样就创建了一个 remote-autossh 服务,并指定其在网络服务启动后启动。可以运行 systemctl daemon-reload && systemctl start remote-autossh 立即启动服务,或者 systemctl enable remote-autossh.service 启动服务并设置为开机启动。

需要注意的是,配置文件中的 autossh 命令需要替换为其绝对地址,以及不支持 -f 参数。

SSH反向连接及Autossh

简介

接触Linux恐怕对SSH再熟悉不过了,还有scp,sftp各种方便的功能,一般的使用都需要ip:port(如果不是默认22的话),但有些情况比较特殊,就是想连接一台内网主机(比如公司内网,当然你肯定做不了Port Forwarding,除非你想在公司防火墙上拆个洞)。稍懂一点网络的童鞋会明白,Internet上去主动连接一台内网是不可能的,一般的解决方案分两种,一种是端口映射(Port Forwarding),将内网主机的某个端口Open出防火墙,相当于两个外网主机通信;另一种是内网主机主动连接到外网主机,又被称作反向连接(Reverse Connection),这样NAT路由/防火墙就会在内网主机和外网主机之间建立映射,自然可以相互通信了。但是,这种映射是NAT路由自动维持的,不会持续下去,如果连接断开或者网络不稳定都会导致通信失败,这时内网主机需要再次主动连接到外网主机,建立连接。

环境

A要控制B

A主机:外网,ip:123.123.123.123,sshd端口:2221

B主机:内网,sshd端口:2223

无论是外网主机A,还是内网主机B都需要跑ssh daemon

一、使用SSH方式连接

1.1 首先在B上执行

$ ssh -NfR 1234:localhost:2223 [email protected] -p2221

这句话的意思是将A主机的1234端口和B主机的2223端口绑定,相当于远程端口映射(Remote Port Forwarding)。

这里每次需要输入A主机user1的登陆密码,后面会讲到解决办法。

1.2 这时在A主机上sshd会listen本地1234端口

$ ss -ant
State      Recv-Q Send-Q        Local Address:Port          Peer Address:Port
LISTEN     0      128               127.0.0.1:1234                     *:*

1.3 像平时一样连接到A主机的1234端口就可以控制内网B主机了

$ ssh localhost -p1234

二、使用Autossh方式

一开始提到,这种反向连接(Reverse Connection)不稳定,可能随时断开,需要内网主机B再次向外网A发起连接,这时需要个“朋友”帮你在内网B主机执行这条命令。它就是Autossh。

在此之前还要解决之前的一个问题,那就是每次内网主机B连接外网主机A时都需要输入密码,这个问题ssh本身是提供另外一种验证方式——通过密钥验证用户身份,实现自动登录。

2.1 在内网B主机上生产公钥和私钥

$ ssh-keygen
...(一直按Enter,最后在~/.ssh/下生成密钥)
$ ls ~/.ssh/
id_rsa id_rsa.pub known_hosts

2.2 复制B主机上生成的id_rsa.pub公钥到外网A主机上,并将内容加入到~/.ssh/authorized_keys中

$ cat id_rsa.pub >> ~/.ssh/authorized_keys

试下,内网B主机连接外网A主机,就不再输入密码验证了

补充:今天了解到ssh-copy-id这个命令,上面这个操作就变的简单了

$ ssh-copy-id [email protected]

2.3 再来看看Autossh的用法

$ autossh -M 5678 -NR 1234:localhost:2223 [email protected] -p2221

比之前的命令添加的一个-M 5678参数,负责通过5678端口监视连接状态,连接有问题时就会自动重连,去掉了一个-f参数,因为autossh本身就会在background运行。

三、终极方案:当重启内网B主机,谁来自动Autossh呢,加入daemon吧

以daemon方式执行,相当于root去执行autossh, ssh,这时刚才普通用户目录下的.ssh/authorized_keys文件会不起效。有两种办法解决,一种是用autossh的参数指定.ssh路径;另外一种是以普通用户身份执行daemon,下面是第二种方式。

/bin/su -c '/usr/bin/autossh -M 5678 -NR 1234:localhost:2223 [email protected] -p2221' - user1

autossh还有很多参数,用来设置重连间隔等等。

将上面命令放入下面各启动方式中,根据自己系统自己配置:

  • SysV:/etc/inid.d/autossh

  • Upstart: /etc/init/autossh.conf

  • systemd: /usr/lib/systemd/system/autossh.service