【ssh反向代理】外网访问内网主机
利用反向ssh从外网访问内网主机
前言
内网一台主机仅能在内网访问,没有公网ip,外网无法直接访问。下面是个反向ssh连接方案,从而可以连接到校园内网主机。
工作原理
内网主机B可以访问到外网服务器A,于是让B与A创建通信隧道连接,随后再通过外网主机A连接到内网主机B
所谓反向ssh最通俗的理解,这就像寄信一样,虽然我不知道你的地址,但是你知道我的地址,那么你就先给我写封信,告诉我你的地址,然后我不就可以回信给你了么?
前提:使用密钥登录
为了不输入密码,这边选择用密钥登录,下面是密钥登录的方法
在内网主机B创建密钥,使用命令ssh-keygen -t rsa
,这个命令会在**~/.ssh/**目录下生成一个公钥,一个私钥。
将公钥的内容,复制到服务器A的**~/.ssh/authorized_keys**(这里的~是user的home路径)内
1 | ssh-copy-id -i ~/.ssh/mykey.pub user@host |
随后,在内网主机B尝试无密码登录到服务器A(假设A的ip为12.12.12.12)
1 | ssh username@12.12.12.12 |
如果不需要输入密码,直接登录成功,再进行下面的步骤
如果仍要输入密码,请检查:服务器A上的 SSH 配置文件可能未正确启用密钥登录
服务器A上检查 SSH 配置文件 /etc/ssh/sshd_config
:
1 | sudo vim /etc/ssh/sshd_config |
确保以下选项被正确配置:
1 | # 把no改成yes,如果默认是yes,则不是这个问题 |
如果做了修改,重启 SSH 服务:
1 | sudo systemctl restart sshd |
基本操作步骤
下面是使用原始的ssh进行连接,但ssh本身容易自动断开,所以需要用autossh保持稳定(看实际方案)
由于我们自己使用的电脑未必有公网ip,因此我们需要一个有固定公网ip的服务器
1、准备好有固定ip的服务器A,以及待访问的内网机器B。两者都开着sshd服务,端口号默认都是22。顺便做好ssh免密码登陆。
2、内网主机B上执行以下命令:
1 | ssh -NfR 7777:localhost:22 username@servername -p 2222 |
这条命令的意思是
- 在后台执行(-f)
- 不实际连接而是做port forwarding(-N)
- 做反向ssh(-R)
- 将远程服务器的7777端口映射成连接本机(B)与该服务器的反向ssh的端口
执行完ss -ant |grep 7777
或netstat -ap | grep 7777
命令
我们可以在服务器A上看到他的7777端口已经开始监听,这个7777端口就已经映射成了内网主机B的22端口了,
1 | ss -ant |grep 7777 |
3、在服务器A中执行:
1 | ssh username@localhost -p 7777 |
这样就成功地在服务器A本地登陆内网的主机
4、使用下面的命令,就可以通过服务器A来ssh登录到内网主机B了
1 | ssh username@servername -p 7777 |
[!Important]
一定要在云服务器上修改
sshd_config
,将里面的GateWayPort
后面的东西为yes
,随后一定要重启ssh服务!否则会出现Connection refuse
1 | vim /etc/ssh/sshd_config |
附:这里有必要加强一下记忆,这个端口号一不小心就容易搞混,man文档中的参数命令是这样的:
1 | -R [bind_address:]port:host:hostport |
- bind_address以及其后面的port是指远程主机的ip以及端口,由于ssh命令本身需要远程主机的ip(上上条命令中的servername),因此这个bind_address原则上是可以省略的,如果不省略就写0.0.0.0。
- host以及其后的hostport是指本机的ip和端口。
功能优化
上面的做法其实有一个问题,就是反向ssh可能会不稳定,主机B对服务器A的端口映射可能会断掉,那么这时候就需要主机B重新链接,而显然远在外地的我无法登陆B,这其实有一个非常简单的解决方案,就是用autossh替代步骤2中的ssh:
1 | autossh -M 2222 -NfR 7777:localhost:22 username@servername -p 2222 |
后面的参数跟ssh都一样,只是多了一个-M参数,这个参数的意思就是用本机的2222端口来监听ssh,每当他断了就重新把他连起来。。。不过man文档中也说了,这个端口又叫echo port,他其实是有一对端口的形式出现,第二个端口就是这个端口号加一。因此我们要保证这个**端口号(2222)和这个端口号加一(2223)**的端口号不被占用。
实际方案
在内网主机B上的操作
使用密钥登录
为了不输入密码,这边选择用密钥登录,下面是密钥登录的方法
在内网主机B创建密钥,使用命令ssh-keygen -t rsa
,这个命令会在**~/.ssh/**目录下生成一个公钥,一个私钥。
将公钥的内容,复制到服务器A的**~/.ssh/authorized_keys**内
1 | ssh-copy-id -i ~/.ssh/mykey.pub user@host |
随后,在内网主机B尝试无密码登录到服务器A(假设A的ip为12.12.12.12)
1 | ssh username@12.12.12.12 |
如果不需要输入密码,直接登录成功,再进行下面的步骤,否则下面的步骤也无法功能
[!Important]
再次提醒:一定要在云服务器上修改
sshd_config
,将里面的GateWayPort
后面的东西为yes
,随后一定要重启ssh服务!否则会出现Connection refuse
1 | vim /etc/ssh/sshd_config |
安装autossh
1 | apt install autossh |
autossh软件可以自动断开重连
创建自动登录脚本
创建个shell脚本,作用是让内网主机B连接到服务器A,这边使用密钥登录
脚本名:autossh.sh
(此脚本调用了autossh功能,可以改名为其它)
功能:如果已经存在autossh进程,则显示这个进程pid;否则,强制执行登录
1 | !/bin/bash |
检查autossh是否启用
可以定时执行下面的命令,来检测autossh是否启动
脚本名:check_autossh.sh
功能:如果已经存在autossh进程,则显示这个进程pid;否则,先询问是否需要登录,再判断并执行登录
1 | !/bin/bash |
开机执行自动连接脚本(推荐)
使用crontab实现开机执行autossh脚本,每三小时检测一次autossh是否启动中
1 | @reboot /home/autossh.sh |
ssh连接到内网主机
在任意外网主机
使用下面命令即可登录到内网主机B
1 | ssh username@12.12.12.12 -p 6688 |
如果是外网主机A
需要登录到内网主机B
,还可以使用
1 | ssh username@localhost -p 6688 |
开机执行某个脚本(备用方案)
在 /etc/init.d/ 中创建自己的开机运行脚本
1 | 这里的文件名 mystart 可以修改为任何你喜欢的名称,但是必须放在/etc/init.d/目录中 |
写入需要执行的命令,
1 | !/bin/bash |
修改脚本文件权限(将命令中的mystart.sh替换成实际的脚本文件名称)
1 | sudo chmod 755 /etc/init.d/mystart.sh |
加入开机启动(将命令中的mystart.sh替换成实际的脚本文件名称)
1 | sudo update-rc.d mystart.sh defaults 90 |