ngrok是使用go语言开发的反向代理服务,可以在公共的端点和本地内网的Web服务之间建立一个安全通道,实现内网穿透功能。 虽然ngrok 2.x的版本已经闭源,但ngrok 1.x还是开源的,版本停留在了1.7不再开发和维护,但它作为个人平时开发和调试的内网穿透工具偶尔使用还是够用的。之前小白的ngrok部署在个人的一台AWS EC2主机上已有好几年,最近这台EC2释放掉了,小白需要在新的主机上部署ngrok服务,正好借着这个机会整理一下ngrok的部署和配置过程。

0.前提条件

  1. 一台Linux云主机(这里是CentOS 7),并要求具有固定公网IP暴露这台云主机指定端口的能力
  2. 有一个域名解析到这个公网IP,如ngrok.frognew.com*.ngrok.frognew.com解析到这个公网IP

1. 编译ngrok服务端ngrokd和客户端ngrok

首先需要从ngrok 1.x的源码编译ngrok的服务端ngrokd和客户端ngrok程序。ngrok使用golang开发,这里先在这台新的云主机上安装Go语言。 这台云主机是CentOS 7,这里直接使用yum安装golang:

1yum install golang

yum安装完成的go,会自动创建/usr/bin/go到安装目录的软链接,所以这里不再配置环境变量。

接下来使用git clone ngrok 1.x的源码:

1cd /usr/local
2git clone https://github.com/inconshreveable/ngrok.git
3cd ngrok

因为这里ngrok服务的域名是ngrok.frognew.com,接下来为这个域名生成ngrok服务端和客户端的证书:

1export NGROK_DOMAIN=ngrok.frognew.com
2
3openssl genrsa -out rootCA.key 2048
4openssl req -x509 -new -nodes -key rootCA.key -subj "/CN=$NGROK_DOMAIN" -days 5000 -out rootCA.pem
5openssl genrsa -out device.key 2048
6openssl req -new -key device.key -subj "/CN=$NGROK_DOMAIN" -out device.csr
7openssl x509 -req -in device.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out device.crt -days 5000

使用生成的证书替换ngrok源码中的证书:

1cp rootCA.pem assets/client/tls/ngrokroot.crt
2cp server.crt assets/server/tls/snakeoil.crt
3cp server.key assets/server/tls/snakeoil.key

编译ngrok的服务端ngrokd:

1export GOPATH=/usr/local/ngrok
2export GOOS=linux 
3export GOARCH=amd64 
4make release-server

按需编译ngrok的客户端ngrok,小白这里是Mac所以只编译Mac OS系统的客户端:

1GOOS=darwin GOARCH=amd64 make release-client

关于golang的交叉编译,通过GOOS指定对应操作系统,darwin对应Mac OS,windows对应Windows,linux对应Linux.

2. 配置启动服务端ngrokd

接下来配置启动服务端ngrokd,创建一个非特权用户ngrok作为服务端的运行用户:

1useradd ngrok

编写ngrokd的Systemd配置文件/etc/systemd/system/ngrokd.service:

 1[Unit]
 2Description=ngrok server
 3After=network.target
 4[Service]
 5Type=simple
 6User=ngrok
 7ExecStart=/usr/local/ngrok/bin/ngrokd --domain=ngrok.frognew.com --tunnelAddr=:14443  --httpAddr=:8081 --httpsAddr=
 8Restart=on-failure
 9[Install]
10WantedBy=multi-user.target

ngrokd包含了下面的启动参数:

  • --domain ngrok的域名,也是前面生成证书时用到的那个域名
  • --httpAddr http协议端口,默认为80,这里修改为8081,
  • --httpsAddr https协议端口 默认为443,这里修改空表示不监听和使用https端口
  • --tunnelAddr 通道端口 默认4443,这里修改为14443

注: 关于http协议只启用了http,是因为这里并不会通过公网IP直接暴露8081这个端口,而在ngrokd前面会部署一个nginx暴露80和443,nginx通过443做TLS Termination,所以--httpsAddr就不再需要了。

启动ngrokd:

1systemctl enable ngrokd
2systemctl start ngrokd
3
4netstat -nltp | grep ngrokd
5tcp6       0      0 :::14443                :::*                    LISTEN      8485/ngrokd
6tcp6       0      0 :::8081                 :::*                    LISTEN      8485/ngrokd

3.nginx配置

接下来配置nginx反向代理ngrokd的http服务:

 1server {
 2    listen 80;
 3    server_name *.ngrok.frognew.com;
 4    rewrite ^(.*)$  https://$host$1 permanent;
 5}
 6
 7
 8server {
 9    listen       443 ssl;
10    server_name  *.ngrok.frognew.com;
11    proxy_set_header X-Real-IP $remote_addr;
12    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
13    proxy_set_header Host  $http_host:8081;
14    proxy_set_header X-Nginx-Proxy true;
15    proxy_set_header Connection "";
16
17    ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
18    ssl_certificate /etc/letsencrypt/live/frognew.com/fullchain.pem;
19    ssl_certificate_key /etc/letsencrypt/live/frognew.com/privkey.pem;
20    location / {
21       # proxy_intercept_errors on;
22       # error_page 404 /404.html;
23       proxy_pass http://127.0.0.1:8081;
24    }
25
26}

4.ngrok客户端配置

使用python3在本地快速启动一个http服务用于测试:

1python3 -m http.server 8080

下面将使用ngrok将这个http服务暴露到公网,将前面编译的ngrok客户端ngork可执行文件下载到本地。 编写ngrok的客户端配置文件ngrok.yml:

1server_addr: ngrok.frognew.com:14443
2trust_host_root_certs: false
3tunnels:
4  py-http-demo:
5    proto:
6      http: 8080

启动ngrok客户端:

1./ngrok -config ngrok.yml start py-http-demo
2
3Tunnel Status                 online
4Version                       1.7/1.7
5Forwarding                    http://py-http-demo.ngrok.frognew.com:8081 -> 127.0.0.1:8080
6Web Interface                 127.0.0.1:4040
7# Conn                        0
8Avg Conn Time                 0.00ms

使用http://py-http-demo.ngrok.frognew.com即可从公网访问前面使用python启动的用于测试的http服务。