很早就有这样一个想法: 通过远程控制内网主机,原来有尝试借助于路由器的端口映射或DMZ来实现,可是后面发现这并不现实,首先是假如有多级路由,那每个路由都要配置,非常麻烦,而且有些根路由,根本无法触及到;其次电信、联通等网络并不一定暴露公网IP给我们,因此外网根本访问不到(可以理解为我们处于一个大局域网下);由于这种种原因,渐渐放弃了这个想法,直到有一天发现SSH反向连接这个东西. 当时一看到这个,就好像发现新大陆一样,无比兴奋,经过一段时间的折腾,终于找到了控制内网直接的好方案.

背景

  • SSH正向连接 - 客户端连接服务端,并将服务端端口映射到客户端上
ssh -L [客户端IP或省略]:[客户端端口]:[服务器侧能访问的IP]:[服务器侧能访问的IP的端口] [登陆服务器的用户名@服务器IP] -p [服务器ssh服务端口(默认22)]
  • SSH反向连接 - 客户端连接服务端,并将客户端端口映射到服务器上
ssh -R [服务器IP或省略]:[服务器端口]:[客户端侧能访问的IP]:[客户端侧能访问的IP的端口] [登陆服务器的用户名@服务器IP] -p [服务器ssh服务端口(默认22)]

PS: 其中,服务器IP如果省略,则默认为127.0.0.1,只有服务器自身可以访问。指定服务器外网IP的话,任何人都可以通过[服务器IP:端口]来访问服务

配置

一般情况下,我们是使用ssh来连接远程主机的,其默认端口是22,下面我们来对他进行配置

1. 反向链接

# 内网主机执行
ssh -RN 7023:localhost:22 server_user@server_ip

PS: 本配置是指将内网主机端口22映射到服务器7023端口,接下来你可以在服务器执行ssh -p7023 client_user@localhost命令来接入内网主机,并进行控制

2. 端口转发

通过上一步的配置,我们已经可以进行了远程控制,但不是很方便,我们需要登录服务器,再登录内网主机,显得很麻烦,有没有直接登录内网主机的办法?肯定有,方法如下:

  • 方法一
# 编辑服务端/etc/ssh/sshd_config
GatewayPorts yes

关于GatewayPorts说明如下:

Specifies whether remote hosts are allowed to connect to ports forwarded for the client.  By default, sshd(8)
             binds remote port forwardings to the loopback address.  This prevents other remote hosts from connecting to for-
             warded ports.  GatewayPorts can be used to specify that sshd should allow remote port forwardings to bind to non-
             loopback addresses, thus allowing other hosts to connect.  The argument may be ``no'' to force remote port for-
             wardings to be available to the local host only, ``yes'' to force remote port forwardings to bind to the wildcard
             address, or ``clientspecified'' to allow the client to select the address to which the forwarding is bound.  The
             default is ``no''.
  • 方法二
# 服务器执行
ssh -fCNL *:8023:localhost:7023 localhost

PS: 将7023映射为外部可以访问的8023端口,这里的*很关键,如果不加,外部主机还是无法接入. 可以加入到开机启动项中,这样就完全自动化.

优化

通过以上配置,基本实现了外网控制内网主机的方法,但我在操作的过程中,发现问题很多,特别是经常断线,而严重影响着使用,经过几天的努力,终于一一解决了,如下是遇到的问题及解决方案.

1. SSH掉线

相信用过SSH的童鞋会发现,但一段时间不操作后,SSH就卡死了,需要重新连接,对于我们来说非常麻烦,每次再控制前都得让客户端重新执行下SSH反向连接,体验非常差,经过Google发现已经有了解决方案,如下(可以参考此文):

  • 客户端配置
#打开
sudo vim /etc/ssh/ssh_config
# 添加
ServerAliveInterval 20
ServerAliveCountMax 999
  • 服务端配置
# 打开
sudo vim /etc/ssh/sshd_config
# 添加
ClientAliveInterval 30
ClientAliveCountMax 6

PS: 经过上面的配置后,我发现SSH就很少断

2. SSH断开自动重连

经过上一点的优化后,已经得到了大大的改善,但总是有断开的时候,那能否保证断开后重新连接上,我最先想到用守护进程去执行ssh,来保证ssh不会停止,而后面发现个更好的东西autossh:

autossh -N -R 7076:localhost:22 server_user@server_ip -i /home/client_user/.ssh/id_rsa

详情参见这里

3. 开机自动配置

为了保证重启后自动配置,需要添加开机启动项,这里使用service进行配置

(1) 配置service

sudo vim /etc/default/autossh

AUTOSSH_POLL=60
AUTOSSH_FIRST_POLL=30
AUTOSSH_GATETIME=0
AUTOSSH_PORT=22000
SSH_OPTIONS="-N -R 2222:localhost:22 example.com -i /home/autossh/.ssh/id_rsa"

sudo vim /lib/systemd/system/autossh.service

[Unit]
Description=autossh
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=用户名
EnvironmentFile=/etc/default/autossh
ExecStart=
ExecStart=/usr/bin/autossh $SSH_OPTIONS
Restart=always
RestartSec=60

[Install]
WantedBy=multi-user.target
(2) 软连接
sudo ln -s /lib/systemd/system/autossh.service \
      /etc/systemd/system/autossh.service
(3) 启动服务
sudo systemctl daemon-reload # 重载
sudo systemctl start autossh # 启动
sudo systemctl status autossh # 查看状态
(4) 开机启动
sudo systemctl enable autossh

PS: 想要了解更详细的内容,可以访问这里

4. centos6下autossh安装

通过yum发现无法找到autossh,原因是未启动Repoforge库,解决方法如下

rpm -Uvh http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm
yum install autossh

后续

以上是针对SSH 22端口进行映射,其他端口原理一样,可以自由发挥