ngrok介绍

ngrok是一个反向代理软件,最早是因为微信调试需要而使用。后来发现用途不少,我常用的主要是以下方面:

  • 微信调试;
  • webhook需要,比如第三方登陆或者支付的webhook;
  • 对隧道中的HTTP请求抓包分析;
  • 将局域网的SSH端口映射出来(比如你的朋友请你远程技术支持linux,通过此方式可以直接ssh到对方电脑,不要用慢吞吞的远程桌面了)

为什么不使用现成的呢?

  1. 国外的不能用,主要原因是微信调试要求域名是备案的
  2. 国内的免费,不过大部分端口都不是80443端口,另外一个是不太稳定,经常要换客户端。
  3. 国内收费服务。我自己没使用过,另外一个我有服务器可以有更高灵活性,于是打算自己也搭一个。重点就是解决端口问题和稳定性问题。

准备工作

两台电脑:

  • 一台装有docker的公网电脑当服务端(笔者使用的电脑操作系统是Centos 7.2)
  • 一台PC电脑当客户端

知识要求:

  • nginx的基础知识,特别是反向代理的配置;
  • docker的基础知识;

特别强调:在nginx+ngrok配合的情况下,不要在同一台电脑上同时起服务端和客户端,否则可能出现反复连接断开的现象

快速搭建

注意点

  • 关于ngrok首先要强调一下,ngrok的服务端与客户端是配套的,所以随便网上找客户端连接自己编译的服务端是不能用的。
  • DNS的记录记得修改;
  • ngrok 2.x版本是不开源的,因此我们使用的是ngrok 1.x的版本;

操作步骤:

所有的示例都在 https://github.com/pheye/dockers.gitweb目录下,通过以下命令获取源码:

$git clone https://github.com/pheye/dockers.git
$cd dockers/web/
$cp env.example .env

然后按如下操作:

1.将.envNGROKD_DOMAIN也改成你的域名。

2.修改身份认证的文件,即客户端必须传递正确的auth_token参数才能正常连接,防止网址公开后任意人都可以使用

$cp ngrok/ngrok-secrets{.example,}

该文件的格式为:

# username      password
  example-user  example-password

3.编译映像

$docer-compose build #耗时很长

编译映像时需要注意,一定要通过上面的命令,而不要进入ngrok命令然后通过docker build手动编译映像,会提示NGROK_BASE_DOMAIN环境变量不存在,如需手动编译,请自行做相应修改。

4.修改nginx配置

ngrok.confserver_name改成自己的域名。

5.启动容器

$docker-compose run -d

6.下载客户端

正如前面所说,客户端与服务端是配套的。这时客户端在容器中,需要将其复制出来.
通过docker ps找到ngrokd容器的名称,比如web_ngrokd_1,将客户端复制到宿主机中:

$docker cp web_ngrokd_1:/ngrok bin

其中bin目录下的内容:

ngrok # linux下的客户端
darwin_amd64/ngrok # mac下的客户端
windows_amd64/ngrok # windows下的客户端
ngrok.cfg # ngrok客户端配置文件,将其移到客户端机器的~/.ngrok,或者在启动时加-config=ngrok.cfg参数

ngrok.cfg的内容如下:

server_addr: tunnel.papamk.com:4443 #应改成自己的域名
trust_host_root_certs: false #必须为false
auth_token: abc:17605033425abc #前面ngrok-secrets配置的<user>:<password>的组合
tunnels: #预先配置好通道,主要是为了演示,不是必须的
  ssh:
    proto:
      tcp: "22"
    remote_port: 2222
  web:
    subdomain: abc
    proto:
        http: 80
        https: 80

7.修改DNS记录,添加2A记录到你主机的IP,以tunnel.papamk.com为例:

tunnel.papamk.com <你的IP>
*.tunnel.papamk.com <你的IP>

8.客户端启动ngrok:

首先确保ngrok -v的版本是在1.x,以及~/.ngrok已经配置好,然后启动客户端,看到Status为online就是成功了,同时可以看到映射端口都是标准端口:

$ngrok start web
ngrok                                                                                                                                                                (Ctrl+C to quit)
                                                                                                                                                                                     
Tunnel Status                 online                                                                                                                                                 
Version                       1.7/1.7                                                                                                                                                
Forwarding                    https://abc.tunnel.papamk.com -> 127.0.0.1:80                                                                                                          
Forwarding                    http://abc.tunnel.papamk.com -> 127.0.0.1:80                                                                                                           
Web Interface                 127.0.0.1:4040                                                                                                                                         
# Conn                        0                                                                                                                                                      
Avg Conn Time                 0.00ms 

关于ngrok的更多用法,推荐:https://imlonghao.com/27.html

搭建过程关键点分析

我们推荐先根据此教程在宿主机上熟悉下ngrok的搭建过程:http://zpblog.cn/linux/run-ngrok-on-your-own-server.html,然后再看下面的说明。

ngrok服务端与客户端的构建由Dockerfile完成,完整内容见:https://github.com/pheye/dockers/blob/master/web/ngrok/Dockerfile

nginx下面的ngrok.conf实现到ngrok的反向代理,完整内容见:https://github.com/pheye/dockers/blob/master/web/nginx/conf.d/ngrok.conf

关键点:

  • ngrok/Dockerfile是构建ngrokd映像的描述文件,其中,ngrok的源使用的是https://github.com/prikevs/ngrok.git,而不是官方ngrok的源码。官方源码不需要任何认证就可以连接上,太容易被人盗用,具体见《客户端安全问题》的讨论。
  • ngrok/Dockerfile映射了22224443端口,而没有映射80443端口。2222映射的目的见《tcp端口的转发问题?》的讨论,4443ngrok的控制端口,所以必须映射;而80443通过nginx反向代理,所以没必要开放。
  • 编译后的所有文件(证书与二进制文件)都在容器的/ngrok目录下,宿主机需要从容器中复制,并根据自己的客户端操作系统选择对应的客户端,见“操作步骤-5”,如果客户端不能满足你的需求,修改Dockerfile重新build即可。
  • 为了确保nginxngrok共存,并且都使用的是80443端口,应该将nginx80443映射到主机,而ngrok不映射。然后所有到ngrokHTTP请求都通过nginx反向代理实现,

几个问题

客户端安全问题?

默认的ngrokd服务器,只要知道地址就能使用,是非常不安全的。毕竟是私人服务器,最好加个身份认证。具体可参考本文章。
给ngrok加上身份认证

如果要公开使用,仅仅是验证用户名和密码其实是不够的,应该与域名绑定才能控制得住。能通过改ngrokd的源码当然最好。次点的方法,通过nginx做限制,只转发允许的域名,其他域名都不转发。

找到ngrok.confserver_name:

server_name  tunnel.papamk.com *.tunnel.papamk.com;

应该将通配域名改为允许访问的域名,比如a.tunnel.papamk.com,b.tunnel.papamk.com,在该文件中一一枚举出来。量大时显然不太方便,特别是要同时改80443两处的server_name。因此我做了个脚本简化此过程(https://github.com/pheye/dockers/blob/master/web/scripts/setdomain.sh):

先修改ngrok/ngrok-domains.example示例文件,里面为允许的域名,文件格式为每行一个域名:

tunnel.papamk.com
abc.tunnel.papamk.com

然后通过scripts/setdomain.sh脚本完成替换:

scripts/setdomain.sh ngrok/ngrok-domains.example nginx/conf.d/ngrok.conf 

该方法带来另外一个问题,没在白名单的其他域名可能匹配到nginx的其他server,给用户造成困惑。为解决此问题,没在白名单的域名应该提示出错才是比较好的做法,解决方法是添加一个nginx/conf.d/ngrok-forbid.conf(示例工程有提示nginx/conf.d/ngrok-forbid.conf.example)

server {
    listen 80;
    listen 443;
    server_name  *.tunnel.papamk.com;
    ssl off;
    location / {
        default_type text/html;
        add_header Content-Type 'text/html; charset=utf-8';
        return 200 "您未被授权访问,请联系管理员xxx@163.com";
    }
}

tcp端口的转发问题?

当本地的22端口开放出去,如下:

./ngrok -proto=tcp 22
ngrok                                                                                                                                                                (Ctrl+C to quit)
                                                                                                                                                                                     
Tunnel Status                 online                                                                                                                                                 
Version                       1.7/1.7                                                                                                                                                
Forwarding                    tcp://tunnel.papamk.com:43343 -> 127.0.0.1:22                                                                                                          
Web Interface                 127.0.0.1:4040                                                                                                                                         
# Conn                        0                                                                                                                                                      
Avg Conn Time                 0.00ms 

然而43343在宿主机上并没有映射,所以实际上没办法连接。需要通过iptables添加一条转发记录,对于自建服务器的同学这样显然是不方便的。

解决方案是:

1.客户端,编辑~/.ngrok,在tunnels下面添加ssh的通道,手动指明远程端口:

server_addr: tunnel.papamk.com:4443
trust_host_root_certs: false
auth_token: abc:17605033425abc
tunnels:
  ssh:
    proto:
      tcp: "22"
    remote_port: 2222
  web:
    subdomain: abc
    proto:
        http: 80
        https: 80

然后通过以下命令启动服务,远程端口2222就映射到了本地的22端口:

ngrok start ssh

2.服务端,ngrokddocker在启动时就映射2222端口。(docker实际上也是借助iptables完成的)

https的证书问题?

访问https时,Chrome会提示证书不安全。ngrok需要的是wildcard的证书,没找到比较好用的并且免费的。之前生成证书,一直都是使用Let's encrypt,不过不支持wildcard。好消息是,根据官方消息,2月份将会提供支持:

First, we’re planning to introduce an ACME v2 protocol API endpoint and support for wildcard certificates along with it. Wildcard certificates will be free and available globally just like our other certificates. We are planning to have a public test API endpoint up by January 4, and we’ve set a date for the full launch: Tuesday, February 27.

因此,要么等到那时看看。要么买一个支持wildcard的收费证书。

参考

版权声明:本文为pheye原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/pheye/p/8189407.html