利用ssh隧道内网穿透——实践记录和注意事项

最近遇到了这么一个需求,要接一家外部供应商,其中牵涉到接口回调,对方会把一些信息通过调用我们的接口发送给我们。

那么问题来了:

  1. 我想在本地调试
  2. 本地是内网环境
  3. 公司的ip也不是固定的(==!)
  4. 我手里恰好有一台便宜的阿里云机器,固定ip

所以最方便的做法当然是阿里云和我本地机器建立一个隧道啦,阿里云的某个端口收到的请求proxy到我本地。

网上查了一些技术文章,发现原理偏多,实操和注意事项偏少。这里就流水账记录下解决上面需求的流程。

流程

  1. 确保你阿里云上面的某个端口是打开的,比如你想要把阿里云上的8899端口映射到本地的12345端口,那么请先检查阿里云的配置,具体的位置在ecs配置菜单的实例安全组,入网方向规则。如下图

    屏幕截图 2019-03-26 22.18.42

  2. 然后在阿里云上检查/etc/ssh/sshd_config配置文件,看看选项GatewayPorts yes 有没有被添加,如果已经被添加,请直接跳到下一步,如果没有被添加,那么:

    1. 在sshd_config中添加GatewayPorts yes这个配置,这个配置确保外网也能访问这个8899这个监听端口,而不是只能被localhost访问。
    2. 重启你的sshd服务,在ubuntu中重启的方法是sudo service ssh restart,这一步确保上一步的配置生效。其他发行版的服务重启方法,请自行查找。
  3. 在本地机器上执行

    ssh -v -R 8899:127.0.0.1:12345 -N username@remote_ip
    

    建立ssh隧道,这里简单介绍下各个配置的作用

    1. -v 的意思是显示详情,第一次运行的时候要看一些信息知道隧道是否正确建立了,方便排错。
    2. -R的意思是将远程某个端口的tcp连接forward到本地的某个端口,8899是远程服务器要监听的tcp端口,12345是本地服务器被forward到的端口,username是登陆远程服务器的用户名,remote_ip是远程服务器的地址。
    3. -N Do not execute a remote command. This is useful for just forwarding ports.
    4. 另外再说一个-f,如果你调试完毕了,可以用-f把这个ssh进程放到后台。
  4. 本地测试。

    1. 我写了一个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()
      
    2. 同时本地用curl命令请求远程服务器的8899端口。

      >> curl http://remote_ip:8899
      Hello, World
      

Bingo! 都搞定了。

注意事项

  1. GatewayPorts yes,这个配置很多原理性的文章都没提到,如果只是从远程服务器ssh到内网,没有的话ok,但是如果想转发http的请求,必须要有。
  2. 想维持更稳定的连接?请参考守护线程,ssh更详细的配置,或者直接异步内网穿透的解决方案。
posted on 2019-03-26 22:40  王 帅  阅读(368)  评论(0编辑  收藏  举报