【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除
背景
上篇 blog
【Ubuntu】【远程开发】内网穿透:反向隧道建立(三)
分析了 SSH 反向隧道的建立,下面继续分析
内网穿透
SSH 反向隧道建立之后,用户先用 ed25519 算法在本地生成公私钥
bash
ssh-keygen -t ed25519 -C "your_email@example.com"
然后将公钥先上传到内网服务器

这里可以先用局域网,上传好公钥,上传方法之前 blog 【Ubuntu】【远程开发】内网穿透:SSH 建立连接 介绍过了,输入命令
bash
echo "公钥信息" >> ~/.ssh/authorized_keys
假设现在是 Windows 电脑,打开 PowerShell,输入
bash
ssh -p ServerPort ClientUser@ServerIp
连接云服务器上已经建立反向隧道的端口即可

可以看到,用另一台远程主机通过云服务器,成功连接上了内网的电脑 ,注意,这里的用户名是内网服务器的用户名,不是云服务器上的用户名
至此,内网穿透已成功实现

通过设置云服务器防火墙,可以在不用的时候,将该端口禁用掉,防止受到攻击

如果在远程主机的 IP 固定,也可以对来源 IP 进行限制,这样就可以不用 0.0.0.0 开放所有 IP 访问了
OK,内网穿透已经实现了,下面再聊点别的,比如稳定性,在 SSH 反向隧道建立后,常态运行过程中,可能因为各种原因出现断联,比如
- 网络中断:比如 WiFi,网线断开,ISP 网络运营商的 NAT 超时,防火墙切断空闲连接等
- 服务器主动关闭连接:比如长时间未响应,重启 SSH 服务,OOM(Out Of Memory 内存耗尽)杀死进程等
- 客户端本地异常:比如休眠,关机,或者不小心关闭对应的进程等

此时 SSH 会话就会断联,无法继续通信,如果人在外地,不能及时恢复的话,就很麻烦,而且如果是假死状态,就更麻烦(对自动化脚本来说) ,这里的假死状态指的是 连接状态未更新,用户误以为还通着,其实早已断了,SSH 一些假死的典型表现比如
-
终端卡住,按回车没反应 发的命令根本没传到服务器
-
ctrl c无效,因为中断信号都发不出去 -
然后过了几分钟后突然显示
send disconnect: Broken pipe,TCP 最终超时 ,SSH 这才意识到连接已断

-
如果开启了 tmux 会话,
tmux attach的时候会发现会话(terminal multiplexers 终端复用器)无法恢复,实际上早就断了,只是客户端没检测到 -
上面的 tmux 再多说两嘴,对于普通的 Shell 会话,一旦 SSH 断开,Shell 会收到
SIGHUP信号,自动退出,并杀死由它启动的所有子进程,而 tmux 能隔离这个信号,让程序不受 SSH 断开影响 -
tmux 可以在一个 SSH 连接里创建多个虚拟终端 ,就像开了多个标签页 ,即使后面 SSH 断开,里面的程序仍在后台运行,重新连接 SSH,tmux 可以恢复回原来的会话,能看到程序还在跑,输出还在继续
所以可以看到,SSH 连接在表面上看起来还活着(终端没报错,光标还能闪),但实际上底层网络已经断开,任何输入的命令都不会被服务器收到,也不会有任何响应 ,给人的感觉就像卡住了一样

这种假死状态主要是因为 TCP 协议本身没有自动探测机制(除非开启 TCP keepalive)
- 正常断开:服务器发送 FIN 包通知结束,客户端就知道了连接关闭
- 异常断开 :比如断电,拔网线,NAT 表老化,就不会有任何通知 ,但此时客户端仍认为连接有效 ,直到尝试发数据 失败后触发重传,直到超时失败(可能几十秒甚至几分钟),或者系统 TCP keepalive 超时(Linux 默认大约要 2 小时)

所以,在没有应用层保活的情况下,SSH 对这种静默断开毫无感知能力
OK,本篇先到这里,如有疑问,欢迎评论区留言讨论,祝各位功力大涨,技术更上一层楼!!!更多内容见下篇 blog
【Ubuntu】【远程开发】内网穿透:连接可靠性(一)