SSH Tunneling: 远程服务端口转发

SSH隧道 可以为应用服务(端口)访问提供加密隧道,可以非常方便实现远程访问内网服务器。此外,SSH端口转发还有一个非常强大的远程服务端口转发功能,可以将远程服务器上的端口访问反向映射回本地主机。这个功能在以下场景非常实用:

  • 国内运营商(电信、移动、联通)个人家庭宽带大多都不提供互联网公网IP地址,计算机爱好者不能方便地在家中架设对外提供服务的计算机。你可以在公有云上租用一个非常廉价的VPS,然后采用SSH Tunneling的远程服务端口转发,将VPS的80和443端口映射到家中的主机服务端口上,这样就可以向互联网提供服务

  • 如果需要临时将内部主机(例如家中的开发测试主机)的调试端口开放给外部用户联调,也可以使用 SSH Tunneling的远程服务端口转发实现

实际上,这个简单的SSH Tunneling远程端口服务转发就是 localtunnel 的底层实现原理,可以用来构建个人服务器,对internet提供服务。

一条简单的SSH命令

使用以下命令,可以从本地主机上发起一个远程端口转发,建立 SSH隧道 之后,internet用户就可以通过访问远程服务器上的 REMOTE_PORT 访问到你本地局域网 DESTINATION 主机的 DESTINATION_PORT

ssh -R [REMOTE:]REMOTE_PORT:DESTINATION:DESTINATION_PORT [USER@]SSH_SERVER

例如,我使用 Sphinx文档 撰写 readthedocs平台上的云图 ,但是每次都要推送到 github 上才能刷新到readthedocs平台,显然比较繁琐。假如互联网上有人想要阅读我最新撰写的文档,那最快捷的方式就是使用互联网上的一个VPS做远程服务端口转发。

  • 在我撰写的 Cloud Atlas 的html目录下启动Python3 的 http.server 模块服务:

    cd cloud-atlas/build/html
    python3 -m http.server 20080
    

此时我的笔记本上启动了一个简单的http服务,监听端口是 20080

  • 现在登陆到远程VPS上,启用远程端口映射:

    ssh -R 20080:<myserver_ip>:20080 user@<myserver_ip>
    

举例:

#ssh -R 20080:192.168.10.101:20080 huatai@cloud-atlas.huatai.me
ssh -R 20080:localhost:20080 huatai@cloud-atlas.huatai.me

异常排查

我遇到一个奇怪的问题,执行了上述命令之后,在远程服务器上检查:

netstat -an | grep 20080

却发现远程服务器端口是绑定在 127.0.0.1 上,而不是所有网络借口:

tcp        0      0 127.0.0.1:20080         0.0.0.0:*               LISTEN
tcp6       0      0 ::1:20080               :::*                    LISTEN

而且即使我添加了远端服务器的IP地址指定 [REMOTE:]REMOTE_PORT 也是如此

仔细看了一下服务器端的 /etc/ssh/sshd_config 配置,以下配置似乎可疑:

#GatewayPorts no

也就是说,默认 sshd 是设置为 GatewayPorts no

man ssh 中有说明:

By default, TCP listening sockets on the server will be bound to the loopback interface only. This may be overridden by specifying a bind_address. An empty bind_address, or the address ‘*’, indicates that the remote socket should listen on all interfaces. Specifying a remote bind_address will only succeed if the server’s GatewayPorts option is enabled (see sshd_config(5)).

即,如果要绑定所有接口,则使用 * 或者空白的远程服务器地址;但是要使得远程服务器的指定绑定地址,只有激活 GatewayPorts yes 才行。

所以,需要在服务器上配置:

GatewayPorts yes

然后再次执行:

ssh -R 20080:localhost:20080 huatai@cloud-atlas.huatai.me

此时在服务器上检查:

netstat -an | grep 20080

就可以看到:

tcp        0      0 0.0.0.0:20080           0.0.0.0:*               LISTEN
tcp6       0      0 :::20080                :::*                    LISTEN

此时访问 http://cloud-atlas.huatai.me:20080/ 就会看到自己本地笔记本上的 python http.server 显示有访问,同时浏览器中可以看到自己的WEB页面,这表明已经将本地主机上的服务输出到因特网上,对外提供服务。

多端口映射

参考