热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

搭建并配置优雅的ngrok服务实现内网穿透

问题随着互联网生态圈的发展,现今的Web项目中开始越来越多的使用第三方服务,通常这些第三方服务都是由Client通过Server的API主动发起请求,但是Serv

问题

随着互联网生态圈的发展,现今的 Web 项目中开始越来越多的使用第三方服务,通常这些第三方服务都是由 Client 通过 Server 的 API 主动发起请求,但是 Server 回调 Client 这种方式也是很多服务中不可避免的一种方式。这样的场景下,对于开发者就有个比较麻烦的问题:

如何在开发的过程中让处于内网的开发机收到回调?

古老的解决方案

方案一

传统解决方案中,如果没有固定 ip 首先需要动态域名,然后需要维护一份外网到内网的端口映射表,最后如果 Client 中有取 Host 信息的操作还需要响应的 Hack(这点后面会提到)。当然,如果你连公网 ip 都没有,那么就可以直接放弃这个方案了。

方案二

一种更为有效的解决方案是:使用一台拥有公网 IP 的主机,通过隧道来实现转发。其实在很早之前,为了让处于校园网内网的服务器在外网可以访问,我经常通过 SSH Tunnel 来解决这个问题。

# 将远程主机的 10086 转发到本地的 3000
ssh -C -f -N -g -R 10086:127.0.0.1:3000 user@Tunnel_Server

这种方式虽然使用简单,但是稳定性并不理想,一段时间内没有请求 Tunnel 就会自动断开。而且使用者必须有 Tunnel_Server 的 ssh 登录权限,每开一个服务就需要占用 Tunnel_Server 一个端口。

Ngrok

正当我苦于写 SSH Tunnel 的各种连接脚本和守护脚本的时候,第一次接触到了 Ngrok。(2013年)那个时候 ngrok 还是一个很冷门的小工具,它所依赖的 Go 也一样,加上文档有限,各种尝试之后并没有把这套服务搭起来。

再后来,国内出现了金数据团队维护的 tunnel.mobi,默默的为国内的开发者提供了很长一段时间的便利。国内 ngrok 的快速普及,个人觉得很大程度上都得益与 tunnel.mobi 的影响。然而这样一个优秀的服务,在维持一年之后(2014.10-2015.10),选择了关闭。

此后,国内各种 ngrok 服务提供者如雨后春笋般出现,我司对于 ngrok 的依赖也比较大,于是我也在我们自己的服务器上搭了一套 ngrok,不觉然都快过去一年了。在这段时间的使用里,也发现了一些不方便或不够友好的地方,加上之前的搭建的那台服务器如今已不堪重负,于是趁周末的时候重新搭建了一份,并做了一点点配置上的优化。


服务端

我使用的环境是 Aliyun ECS + Ubuntu 14.04,双网卡(内网网卡+外网网卡)

源码安装

首先装必要的工具:

sudo apt-get install build-essential golang mercurial git

获取 ngrok 源码:

git clone https://github.com/inconshreveable/ngrok.git ngrok
cd ngrok

编译&安装:

sudo make release-server
sudo cp bin/ngrokd /usr/local/bin/ngrokd

Apt-get 安装(二选一)

如果你并不需要最新版本的 ngrokd, 同时对源码安装也没什么兴趣,那么其实可以偷懒

sudo apt-get install ngrok-server

域名

选定你要使用的域名,比如:yii.im,添加两条解析到你的服务器

  • yii.im
  • *.yii.im

证书

ngrok 通讯依赖 TLS 证书来加密,所以启动的时候需要指定你的域名和对应的证书

既然依赖证书的话,那你应该先有一份证书。在搭建 ngrok 服务的时候,对于证书的处理有多种方式可选:

  • 使用 CA 颁发的证书,也就是正式的 TLS 证书
  • 使用自签名证书,并自行编译分发带自签名证书的客户端
  • 使用自签名证书,使用通用的客户端,但需要用户把自签名证书添加到自己根证书

本文中使用第一种方式,域名证书通过 沃通CA免费SSL证书 取得。

关于第二种方式,可以参考:https://imququ.com/post/self-hosted-ngrokd.html。目前网上流行的 ngrok 服务或教程,基本上都是基于这种方式的。

第三种方式,除了需要用户添加根证书以外,其他配置与本文一样。

关于 https 的支持

由于 ngrok 工作是通过分配 subdomain 的方式,所以我们实际使用到的域名都是 yii.im 的子域名,如 pub.yii.im 如果要对这个子域名启用 https 服务,那么至少需要三点支持:

  1. ngrok 支持 https, 这个默认就是开启的
  2. pub.yii.im 也需要有证书或包含在一个泛域名证书中
  3. 浏览器(或其他终端)信任 pub.yii.im 的根证书

根据这三点要求,我们重新解读上面三种证书的处理方式:

第一种:由于免费证书是单域名证书,所以你需要给可能会用到二级域名也签上证书才行,当然,如果够钱,买个包含所有二级域名的证书也是可以的

第二种:自签名证书很容易做到第二点,然而并无卵用,除了自编译的 ngrok-client外谁也不认这个证书

第三种:可以支持 https,但是要所有用户(包括访问用户)都添加根证书这种要求,略微有点…

综上所述,我们选择放弃了 https ,因为日常使用并没有强制要求 https 的情况,能跑就够了,要什么自行车。

ㄟ( ▔, ▔ )ㄏ手动滑稽

启动设定

前面生成了 ngrokd 就是 ngrok server ,指定证书、域名和端口就可以启动它了:

# 获取帮助信息
ngrokd -h

# Usage of ngrokd:
# -domain="ngrok.com": Domain where the tunnels are hosted
# -httpAddr=":80": Public address for HTTP connections, empty string to disable
# -httpsAddr=":443": Public address listening for HTTPS connections, emptry string to disable
# -log="stdout": Write log messages to this file. 'stdout' and 'none' have special meanings
# -tlsCrt="": Path to a TLS certificate file
# -tlsKey="": Path to a TLS key file
# -tunnelAddr=":4443": Public address listening for ngrok client

# 试着启动
ngrokd -tlsKey=server.key -tlsCrt=server.crt -domain=yii.im -httpAddr=:8081 -httpsAddr=

到这一步,ngrok 服务已经跑起来了,可以通过屏幕上显示的日志查看更多信息。httpAddr、httpsAddr 分别是 ngrok 用来转发 http、https 服务的端口,可以随意指定。由于我不需要 https,所以留空了。 ngrokd 还会开一个 4443 端口用来跟客户端通讯(可通过 -tunnelAddr=”:xxx” 指定),如果你配置了 iptables 规则,需要放行这几个端口上的 TCP 协议。

现在,通过 http://sub.yii.im:8081 就可以访问到 ngrok 提供的转发服务。在客户端连进来之前,你应该会看到:

Tunnel sub.yii.im:8081 not found

这说明万事俱备,只差客户端来连了。

端口问题(可选)

url 上带上端口通常来说并不会有什么影响,而且通过 nginx 隐藏起来也很简单:

# ngrokd.conf
server {
    server_name *.yii.im;
    listen 80;

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host:8081;
        proxy_redirect off;
        proxy_pass http://127.0.0.1:8081;
    }

}

但是!这里就有一个很烦躁的地方了,ngrokd 里面有一层自己的 Host 处理,于是 proxy_set_header Host 必须带上 ngrokd 所监听的端口,否则就算请求被转发到对应端口上, ngrokd 也不会正确的处理。

带上端口号又会导致了另一个操蛋的问题:你请求的时候是 sub.yii.im,你在 web 应用中获取到的 Host 是 sub.yii.im:8081,如果你的程序里面有基于 Request Host 的重定向,就会被重定向到 sub.yii.im:8081 下面去。

要完美的解决这个端口的问题,就需要让 ngrokd 直接监听 80 端口。

通常来说 VPS 都是双网卡的(一内一外),让 ngrokd 监听外网的 80 实在有些浪费,这个端口还是留给 nginx 比较合理。所以比较理想的方式是:nginx 监听外网 80,ngrokd 监听内网 80,让 nginx 将对应的请求转发到内网 80 上来。

如:

  • 内网 ip: 10.160.xx.xx
  • 外网 ip: 112.124.xx.xx

启动 ngrokd:

sudo ngrokd -tlsKey=server.key -tlsCrt=server.crt -domain=yii.im -httpAddr=10.160.xx.xx:80 -httpsAddr=

配置 nginx:

# ngrokd.conf
server {
    listen      112.124.xx.xx:80;
    server_name *.yii.im;

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_redirect  off;
        proxy_pass      http://10.160.xx.xx:80;
    }
}

# the_others_need_80.conf
server {
    listen      112.124.xx.xx:80;
    #...
}

如果你是单网卡,那么还可以通过 docker 来解决: http://www.hteen.cn/docker/docker-ngrok.html

也可以手动添加ip
[root@nginx conf]# ifconfig eth0:0 192.168.3.60/24 up  
[root@nginx conf]# ifconfig -a
eth0      Link encap:Ethernet  HWaddr 00:0C:29:12:99:D4  
          inet addr:192.168.3.49  Bcast:192.168.3.255  Mask:255.255.255.0
          inet6 addr: fe80::20c:29ff:fe12:99d4/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:792652 errors:0 dropped:0 overruns:0 frame:0
          TX packets:460481 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:972250327 (927.2 MiB)  TX bytes:92493014 (88.2 MiB)


eth0:0    Link encap:Ethernet  HWaddr 00:0C:29:12:99:D4  
          inet addr:192.168.3.60  Bcast:192.168.3.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

维护脚本(可选)

由于 ngrokd 的启动命令老长老长,偶尔发现它死了需要重启,拼(找)命令都拼半天,于是我顺手写了一个维护脚本

注意: Ubuntu 适用,Centos 需要一点修改

wget https://gist.githubusercontent.com/IvanChou/1be8b15b1b41bf0ce2e9d939866bbfec/raw/1a2445599fe7fd706505a6e103a9dc60b4d3a0ed/ngrokd -O ngrokd

# 修改 脚本中的配置
vi ngrokd

chomd +x ngrokd
sudo mv ngrokd /etc/init.d/ngrokd

TCP支持 - SSH etc.(可选)

ngrok 是 TCP 穿透,也就是说只要是基于 TCP 协议的通讯,它都能协助我们进行穿透,当然也包括 SSH 和 mstsc。

ngrok 在进行 TCP 连接的时候,是通过额外开启一个端口的方式,如果 Client 没有指定端口,ngrokd 将会随机开启一个大号端口。如指定 ngrokd 使用 10086 端口,连接建立后可通过 yii.im:10086 访问到 Client 的指定端口。

建议可以在 iptables 中放行少量 大口径端口 备用。


客户端

下载

由于使用的是 CA 证书,所以不需要自行编译客户端,可以网上自行下载各种 ngrok v1.7 的客户端,理论上都是可用的(有的似乎对客户端做了修改,或许有其它未知原因而无法使用,请自行略过)

2016年10月: Mac 升级到 10.12 后,稍微旧一点的 ngrok-client 都有心跳 bug,导致经常断开且界面状态无变化,需要重新下载或编译一下最新的客户端。

资源地址 ㄟ( ▔, ▔ )ㄏ => http://pan.baidu.com/s/1b548fO

MAC & Linux 下,可以将 ngrok 放到 /usr/local/bin/ 下备用

ngrok.yml

server_addr: "yii.im:4443"
trust_host_root_certs: true

这段配置是用来指定 Server 和 认证方式 的:

  • server_addr 中的 host 需要与 ngrokd 所使用的证书严格对应
  • trust_host_root_certs 是否信任系统根证书,如果是带自签名证书编译的 ngrok 客户端,这个值应该设置为 false;如果使用 CA 证书,或者用户添加了根证书,这个值应该设置为 true。

更多关于 ngrok configuration file 的设定可以参考:https://ngrok.com/docs#config

由于官网现在只有 2.0 以上版本的支持,这里只能参照配置的写法,启动方式请勿参考。

Http 连接

ngrok -cOnfig=path/to/ngrok.yml -proto=http -subdomain pub 3000

可以看到

ngrok                                               (Ctrl+C to quit)

Tunnel Status                 online
Version                       1.7/1.7
Forwarding                    http://pub.yii.im -> 127.0.0.1:3000
Web Interface                 127.0.0.1:4040
# Conn 0
Avg Conn Time                 0.00ms

说明连接成功,现在访问 http://pub.yii.im 就可以访问到本机 3000 端口上的服务了

界面管理(推荐)

在上面的运行时界面中,有一个 Web Interface 地址,这是 ngrok 提供的监控界面。通过这个界面可以看到远端转发过来的 http 详情,包括完整的 request/response 信息,相当于附带了一个抓包工具。

TCP 连接

指定 server 端口需要在 ngrok.yml 中配置才能实现

不指定端口

ngrok -cOnfig=path/to/ngrok.yml -proto=tcp 22

连接状态:

ngrok                                               (Ctrl+C to quit)

Tunnel Status                 online
Version                       1.7/1.7
Forwarding                    tcp://yii.im:17476 -> 127.0.0.1:22
Web Interface                 127.0.0.1:4040
# Conn 0
Avg Conn Time                 0.00ms

– EOF –

盗用 Jerry Qu 的一句话

实际上,由于 ngrok 可以转发 TCP,所以还有很多玩法,原理都一样,这里就不多写了。

– PS –

文中使用的 yii.im 只是为了说明使用的域名,这个域名下并没有搭 ngork 服务

(/= _ =)/~┴┴ ╮(╯▽╰)╭


推荐阅读
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
  • 本文介绍了在使用Python中的aiohttp模块模拟服务器时出现的连接失败问题,并提供了相应的解决方法。文章中详细说明了出错的代码以及相关的软件版本和环境信息,同时也提到了相关的警告信息和函数的替代方案。通过阅读本文,读者可以了解到如何解决Python连接服务器失败的问题,并对aiohttp模块有更深入的了解。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 本文介绍了在Linux下安装和配置Kafka的方法,包括安装JDK、下载和解压Kafka、配置Kafka的参数,以及配置Kafka的日志目录、服务器IP和日志存放路径等。同时还提供了单机配置部署的方法和zookeeper地址和端口的配置。通过实操成功的案例,帮助读者快速完成Kafka的安装和配置。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
author-avatar
尕尕东东东_534
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有