利用ssh隧道内网穿透——实践记录和注意事项
最近遇到了这么一个需求,要接一家外部供应商,其中牵涉到接口回调,对方会把一些信息通过调用我们的接口发送给我们。
那么问题来了:
- 我想在本地调试
- 本地是内网环境
- 公司的ip也不是固定的(==!)
- 我手里恰好有一台便宜的阿里云机器,固定ip
所以最方便的做法当然是阿里云和我本地机器建立一个隧道啦,阿里云的某个端口收到的请求proxy到我本地。
网上查了一些技术文章,发现原理偏多,实操和注意事项偏少。这里就流水账记录下解决上面需求的流程。
流程
-
确保你阿里云上面的某个端口是打开的,比如你想要把阿里云上的8899端口映射到本地的12345端口,那么请先检查阿里云的配置,具体的位置在ecs配置菜单的实例安全组,入网方向规则。如下图
-
然后在阿里云上检查/etc/ssh/sshd_config配置文件,看看选项GatewayPorts yes 有没有被添加,如果已经被添加,请直接跳到下一步,如果没有被添加,那么:
- 在sshd_config中添加GatewayPorts yes这个配置,这个配置确保外网也能访问这个8899这个监听端口,而不是只能被localhost访问。
- 重启你的sshd服务,在ubuntu中重启的方法是sudo service ssh restart,这一步确保上一步的配置生效。其他发行版的服务重启方法,请自行查找。
-
在本地机器上执行
ssh -v -R 8899:127.0.0.1:12345 -N username@remote_ip
建立ssh隧道,这里简单介绍下各个配置的作用
- -v 的意思是显示详情,第一次运行的时候要看一些信息知道隧道是否正确建立了,方便排错。
- -R的意思是将远程某个端口的tcp连接forward到本地的某个端口,8899是远程服务器要监听的tcp端口,12345是本地服务器被forward到的端口,username是登陆远程服务器的用户名,remote_ip是远程服务器的地址。
- -N Do not execute a remote command. This is useful for just forwarding ports.
- 另外再说一个-f,如果你调试完毕了,可以用-f把这个ssh进程放到后台。
-
本地测试。
-
我写了一个tornado小程序,监听本地的12345端口。
import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web import json from tornado.options import define, options import datetime import os define("port", default=12345, help="run on the given port", type=int) class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") def main(): tornado.options.parse_command_line() application = tornado.web.Application([ (r"/", MainHandler) ]) http_server = tornado.httpserver.HTTPServer(application) http_server.listen(options.port) tornado.ioloop.IOLoop.current().start() if __name__ == "__main__": main()
-
同时本地用curl命令请求远程服务器的8899端口。
>> curl http://remote_ip:8899 Hello, World
-
Bingo! 都搞定了。
注意事项
- GatewayPorts yes,这个配置很多原理性的文章都没提到,如果只是从远程服务器ssh到内网,没有的话ok,但是如果想转发http的请求,必须要有。
- 想维持更稳定的连接?请参考守护线程,ssh更详细的配置,或者直接异步内网穿透的解决方案。