SSH实现内网穿透
背景
- 需求:内网服务接口暴露给外网调用(外网不能直接访问内网)
- 前提:拥有一台有公网ip的服务器
环境
-
设备A:内网服务器,内网服务器是一台虚拟机,虚拟机上运行nginx服务器,端口是8014,后续描述中设备A简称为A
-
设备B:有公网ip的服务器,公网IP:149.83.24.124(举例IP,替换成你的公网服务器ip地址即可),开放的端口有80、443、8081、8024,(服务器已经关闭了防火墙,而且将80、443、8081、8024端口,加入安全组放行,允许任意ip访问这些端口),后续描述中设备B简称为B
-
访问者C : 可以通过任意公网访问B,但不能访问A,后续描述中访问者C简称为C
注意:
- 此例中ip和端口,要替换为你本人的ip和端口
- 此例中是B上的8024端口,所以可以使用非root用户通过ssh建立隧道,也就是此例中的local用户,如果你想使用B的80端口,那么一定要使用root用户来建立隧道,因为非root用户只能操作1024以上得端口,切记
在以下例子中一定要注意命令是在A上执行还是在B上执行
实现方法(使用的是root用户,操作以下命令)
方法一(掌握方法一即可,方法二只是一个扩展方法)
在A的虚拟中执行命令ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124
,显示如下,此例中,密码处输入的就是B设备上的local用户的密码
[root@localhost ~]# ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124
The authenticity of host '[149.83.24.124]:50237 ([149.83.24.124]:50237)' can't be established.
ECDSA key fingerprint is ce:5e:6d:0a:d4:05:5f:27:21:04:9f:f6:bf:d3:38:a9.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[149.83.24.124]:50237' (ECDSA) to the list of known hosts.
local@149.83.24.124's password:
[root@localhost ~]#
ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124
意思是:使用local用户通过B的50237端口创建ssh隧道,将B的8024端口接收的数据转发到A的8014端口
注意:
- A上的8014端口,就是指A中的nginx服务器实际listen的端口,如果你的虚拟机和宿主机之间进行了端口转发操作,也不要误以为是转发到宿主机后的端口,简单讲假如nginx的8014端口映射到宿主机的端口是8017,但是这里的
ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124
依然要用8014而不是8017,切记- 在A中执行
ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124
成功之前,如果在B上的执行netstat -antupl | grep local
应该是没有记录的,因为在A中执行ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124
成功后。会在B上开启一个8024端口,如下:[root@iZwz9466l9048v1ijne6nxZ ~]# netstat -antupl | grep local Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:8024 0.0.0.0:* LISTEN 8223/sshd: local tcp 0 0 149.83.24.124:50237 67.59.189.84:21918 ESTABLISHED 8221/sshd: local [p
可能会出现的问题
- [root@localhost ~]# Warning: remote port forwarding failed for listen port 8024
解决方法:
查看A虚拟机中/root/.ssh/known_hosts文件[root@localhost ~]# cat /root/.ssh/known_hosts [149.83.24.124]:50237 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXsasYTItbmlzFqfBAfFvufZX8f2JEds*Osasa2Q/swp34dMkpDcff+lUT6n+QQMN+m10JByCz2VQ8yYSxs0=
删除/root/.ssh/known_hosts 文件中以[149.83.24.124]:50237开头行
然后再查看A上刚创建的ssh隧道,kill掉,此例就是kill 5690
[root@localhost ~]# ps aux | grep ssh
root 5431 0.0 0.1 141864 5208 ? Ss 18:05 0:00 sshd: root@pts/0
root 5494 0.0 0.1 85732 3876 ? Ss 18:33 0:00 /usr/sbin/sshd -D
root 5690 0.0 0.0 83920 2024 ? Ss 20:15 0:00 ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124
root 5697 0.0 0.0 112704 968 pts/0 S+ 20:22 0:00 grep --color=auto ssh
然后再查看B上刚创建的ssh隧道,kill掉,此例就是kill 8221 8223
(如果B上没有,就无需kill)
[root@iZwz94nxZ ~]# ps aux | grep ssh
root 7476 0.0 0.5 157348 6040 ? Ss 17:52 0:00 sshd: root@pts/1
root 8215 0.0 0.4 112812 4272 ? Ss 20:15 0:00 /usr/sbin/sshd -D
root 8221 0.0 0.5 157208 5940 ? Ss 20:16 0:00 sshd: local [priv]
local 8223 0.0 0.2 157528 2588 ? S 20:16 0:00 sshd: local
root 8232 0.0 0.0 112704 972 pts/1 S+ 20:27 0:00 grep --color=auto ssh
最后再执行如下命令即可[root@localhost ~]# ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124
- 错误展示
这是因为B的
/etc/ssh/sshd_config
文件中的GatewayPorts
为注释的,修改成如下:GatewayPorts yes
然后将已经在A和B上创建的ssh隧道kill掉,然后再如下重启sshd服务
[root@iZwne6nxZ ~]# systemctl restart sshd.service
- bind: Cannot assign requested address(使用ipv6导致的,加上-4指定ipv4)
[root@localhost ~]# ssh -4 -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124
方法二(不再详细描述)
思路:在B创建一个隧道,将B的8024端口的数据转发到B的8081端口,然后再在A中创建隧道将B中8081端口的数据转到A中的8014端口上
- 在B创建一个隧道,将B的8024端口的数据转发到B的8081端口
ssh -4 -p 50237 -fCNL 8024:localhost:8081 localhost
- 在A中创建隧道将B中8081端口的数据转到A中的8014端口上
ssh -p 50237 -fCNR 8081:localhost:8014 local@149.83.24.124
ssh不稳定会断开的解决方案使用autossh替换ssh(这个是在网上看的,我本地实验发现长期不连依然是会断开,有可能是我配置不对,这个不敢妄言,我使用其他的方法解决了,后面有描述),使用方法如下(在A上操作):
- 安装autossh
yum install -y autossh
- 在A中运行如下命令
autossh -M 6677 -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124
- linux查看进程显示
[root@localhost ~]# ps aux | grep ssh | grep -v grep
root 946 0.0 0.1 85732 3896 ? Ss 10:06 0:00 /usr/sbin/sshd -D
root 4969 0.0 0.1 141864 5208 ? Ss 14:39 0:00 sshd: root@pts/2
root 5016 0.0 0.0 6516 472 ? Ss 14:48 0:00 autossh -M 6677 -p 40682 -CNR 80:localhost:8014 root@112.74.61.154
root 5017 0.0 0.1 83920 5016 ? S 14:48 0:00 /usr/bin/ssh -L 6677:127.0.0.1:6677 -R 6677:127.0.0.1:6678 -p 40682 -CNR 80:localhost:8014 root@112.74.61.154
查看进程你会发现使用autossh命令后进程会多出一个监听6677端口进程,来维护ssh远程隧道的稳定
如果希望在本地xshell窗口关闭,但是本地虚拟机依然运行的时候隧道依然可以稳定使用,需如下操作(在A上操作):
- 安装screen
yum install -y screen
- 创建screen会话,命名为wap
screen -S wap
- 退出screen会话窗口
ctrl+A+D
- 查看创建的screen会话(注意ls左边的那个-,千万不要忘记)
[root@localhost ~]# screen -ls
There is a screen on:
5112.wap (Detached)
1 Socket in /var/run/screen/S-root.
[root@localhost ~]#
- 进入wap会话
[root@localhost ~]# screen -r wap
# 创建隧道
#-o ServerAliveInterval=60 本地 ssh 每隔60s向 server 端 sshd 发送 keep-alive 包,以保持连接
[root@localhost ~]# ssh -p 50237 -fCNR 8024:localhost:8014 local@149.83.24.124 -o ServerAliveInterval=60
# 退出wap会话
ctrl+A+D
-
关闭连接A的所有xshell窗口,隧道会依然存在
-
然后再使xshell连接A,使用步骤4、5就可以再次进入会话查看创建隧道的命令
-
假如你想释放screen会话,就先进入对应会话,执行
exit
如果希望B端口443转发到A的8014端口,需要借助nginx实现(这个方法不是很优雅,但是目前未找到更好的,如果有朋友知道,请在评论区留言):
- 安装nginx
yum install -y nginx
- 开放端口443和8081(用于转发),允许客户端访问
- 在B上配置配置文件(要去申请对应域名的ssl证书,以支持443端口使用)
vim /etc/nginx/conf.d/domain.conf
server {
listen 443 ssl;
server_name domian;
charset utf-8;
ssl_certificate /etc/nginx/ssl/domain.crt;
ssl_certificate_key /etc/nginx/ssl/domain.key;
location / {
proxy_pass http://127.0.0.1:8081;
proxy_set_header Host $host:8081;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
配置的意思是将B上443端口的数据转发到B上的8081端口
- 重载nginx
systemctl reload nginx
- 在A上创建隧道监听B上的8081端口的数据,并将B上8081端口的数据转发到A上的8014端口上,命令如下
ssh -p 50237 -fCNR 8081:localhost:8014 local@149.83.24.124 -o ServerAliveInterval=60
- C去访问B的443端口,就会被转到A的8014端口上
能力有限,如有错误,欢迎指正,谢谢
网友评论