今天遇到RD要在外网访问内网服务器的需求。公司用的电信宽带,浮动ip,直接路由器端口转发这条路行不通。联想到ssh端口转发,配合一个外网固定ip的服务器作为跳板,结合SSH隧道,就能成功实现内网穿透。本文介绍具体操作步骤。

SSH端口转发实现穿透内网

假设有本地内网服务器A,外网服务B(固定IP)。为了进行端口转发,需要从A ssh连到B(建立隧道)并监听端口。达到该目的的命令是:

ssh -R 外网监听端口:本地服务器:本地端口 username@host

例如要将B上的8081端口请求转发到A,则在A上运行命令:

ssh -R 8081:localhost:8080 username@hostB

该命令会建立一个A到B的连接,并在B上监听8081端口,将该端口收到的请求转发到A的8080端口。在连接到的B的终端上输入: netstat -nltp| grep sshd 或者 ss -lntp | grep sshd,可以看到sshd进程已经在监听8081端口。

离正式使用还差一步:B的8081端口只监听了本地(127.0.0.1或者::1),意味着其他用户不同通过 “http://B:8081” 访问A上的内容。这是因为ssh默认不允许远程主机进行端口转发。为了启用端口转发,编辑 /etc/ssh/sshd_config文件,找到 GatewayPorts 选项,配置为:

GatewayPorts yes

重启sshd服务并重新连接到B,打开浏览器,输入”http://B:8081″,访问到的内容和 “http://A:8080” 将会一致。

如果只是进行端口转发,就没必要登录到B上并打开终端。ssh命令的-R参数可以配合-f和-N一起使用,达到静默连接和转发的效果。运行命令:

ssh -fNR 8081:localhost:8080 user@hostB

-f参数表示后台执行,-N表示不执行远程命令。该命令只建立连接,不分配终端,进程后台执行,连接成功后终端控制权马上交给用户。

解决了内网穿透问题,接下来是浮动ip问题。浮动ip会导致建立的ssh连接断开,转发就失效了。为了让转发长时间有效,保证服务的可用性,可以定时检测ssh连接,断线自动重连。以下是断线重连的脚本:

#!/usr/bin/bash

while true
    do
        RET=`ps ax | grep "ssh" | grep -v grep`
        if [ "$RET" = "" ]; then
            echo "the recent ssh disconnect at `date`" >> ~/check.log
            echo "restart ssh connect"
            ssh -fNR 8081:localhost:8080 user@host
        else
            echo "ssh connect at `date`"
            break;
        fi
    done

为了自动重连,你需要先配置ssh免密登录。然后配置crontab定期执行脚本: */2 * * * * 脚本路径。如此一来内网穿透和浮动ip的问题就都解决了。

其他

可以使用ngrok、花生壳、frq等内网穿透工具打到同样的效果,但是需要安装客户端。

参考

  1. http://blog.creke.net/722.html
  2. https://wiki.freebsdchina.org/doc/s/ssh
  3. http://blog.trackets.com/2014/05/17/ssh-tunnel-local-and-remote-port-forwarding-explained-with-examples.html