用 SSH 反向隧道把内网里的服务接到云服务器
要解决的问题
服务跑在内网的一台机器上,内网没有公网地址,也不想在路由器上开端口。手里有一台带公网地址的云服务器,希望外面的请求先到云服务器,再转回内网。
做法
让内网机器主动连到云服务器,并请云服务器替它监听一个端口。下面的例子里,云服务器本机的 9000 端口会通到内网机器的 8000 端口:
ssh -N -R 127.0.0.1:9000:127.0.0.1:8000 tunnel@云服务器地址
连接是从内网往外发起的,所以内网一侧不需要开放任何端口。9000 端口只绑定在云服务器的本机地址上,外面访问不到,要由云服务器上的 nginx 转发过去。数据在 SSH 连接里传输,全程加密。
让它自己重连
把上面的命令做成系统服务,设置失败后自动重启,再加三个参数:
ServerAliveInterval=30:每 30 秒探测一次,连接假死时能及时发现。ExitOnForwardFailure=yes:端口没转发成功就退出,交给系统服务重启,避免连接在、转发不在。StrictHostKeyChecking=yes:只认事先保存的服务器公钥。
把隧道密钥的权限收到最小
隧道用一把单独的密钥、一个单独的账号。账号的登录程序设为不可登录,并在云服务器的授权文件里给这把密钥加上限制:
restrict,port-forwarding,permitlisten="127.0.0.1:9000" ssh-ed25519 AAAA...
这样即使密钥泄露,拿到的人也只能转发这一个端口,不能登录、不能执行命令。配置完要实际试一遍:用这把密钥执行命令、监听别的端口,都应当被拒绝。
踩过的坑
- 页面内容是边生成边输出的,nginx 要关闭缓冲(
proxy_buffering off),并把读取超时放长,否则内容会攒到最后一次性出来。 - 把一段脚本通过标准输入交给远程机器执行时,脚本里再调用 ssh 要加
-n。不加的话,里面的 ssh 会把剩下的脚本当成输入读走,后面的命令悄悄地不执行。