缘由
我写这篇文章,可以解决如下问题:在内网主机运行网站,无公网 ip 所以只能内网访问。但是主机内网可以访问外网,外网无法访问内网,现在我要做的就是通过反向代理,访问公网 ip 就能访问到内网主机网站,只需要一台vps。
以下我都会用 8080 端口来转发,因为我的 80 端口被占用了。
什么是代理
这里引用下知乎的答案。
代理其实就是一个中介,A 和 B 本来可以直连,中间插入一个 C,C 就是中介。刚开始的时候,代理多数是帮助内网 client 访问外网 server 用的(比如 HTTP 代理),从内到外。后来出现了反向代理," 反向 " 这个词在这儿的意思其实是指方向相反,即代理将来自外网 client 的请求 forward 到内网 server,从外到内。 作者:王明雨 链接:https://www.zhihu.com/question/24723688/answer/28828062 来源:知乎 著作权归作者所有,转载请联系作者获得授权。
执行方案
首先 ssh 登录到 公网 主机,修改 sshd 的配置文件/etc/ssh/sshd_config,加入如下选项。
GatewayPorts yes
重启sshd,如下
# service sshd restart
这个选项的意思是,你通过反向代理的 ip 是公网 ip(0.0.0.0),而不是 loopback 的 ip(127.0.0.1)。后面会提到这个选项的具体作用。
然后 ssh 登录到 内网 主机,通过 ssh 来执行反向代理到 公网 主机,这里先给出几个参数的解释。
-N ssh 不执行命令
-f 后台执行
-R 反向代理
所以只要执行以下命令,键入密码即可:
ssh -NfR 8080:localhost:80 <用户名>@公网主机 ip
这里表示将 内网 主机的 80 的端口反向代理到 公网 主机的 8080 端口,如果你不开启 GatewayPorts 选项,就只能转发到 公网 主机的 127.0.0.1 网络上。
这时候在 公网 主机上执行:
$ netstat -tnl|grep 8080
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN
tcp6 0 0 :::8080 :::* LISTEN
看到如上结果表明成功,这时候可以通过浏览器访问,公网 ip:8080。
免密码、稳定隧道
上述的 ssh 方式不够稳定,而且每次断开重连都要键入密码,这里先给出免密码方式。
在 内网 主机上,执行
$ ssh-copy-id <用户名>@公网主机 ip
以后通过这台 内网 主机 ssh 到公网 主机,就不需要输入密码了。
至于稳定隧道,可以用 autossh 来解决,先安装autossh。
# apt-get install autossh
autossh的参数和 ssh 的参数一致,但是它会在隧道断开重连,省去了手动重连的方式。这里需要指出它的 -M 参数,-M参数指定一个端口用来做回显 echo 测试,它会在 公网 主机开一个端口专门接收 内网 主机的 心跳包,然后回显回去(端口号 +1),以表明隧道是否正常,若不正常则重连。
于是最终的命令如下:
autossh -M 23333 -NfR 8080:localhost:80 <用户名>@公网主机 ip
为了开机自启动,可在 /etc/rc.local 写入上述命令。
最终方案
昨晚用 autossh 发现还是不稳定,8080端口会掉,而 -M 的端口不会掉,于是又换方案了,为 ssh 设置重连参数,修改 内网 主机的 ~/.ssh/config 文件,增加如下参数:
ServerAliveInterval 60
ServerAliveCountMax 9999999999
- 第一个参数表示如果服务器(外网)没数据发来则过 60 秒客户端(内网)会发送一个空包到服务器,以保持 tcp 长连接,默认值为 0,表示不会发心跳包,所以这里设置为 60 秒。
- 第二个参数表示,如果服务器(内网)没有收到心跳包指定次数,就中断连接。
今早起来,发现没掉,很稳定。