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:

1
yum install golang

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

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

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

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

1
2
3
4
5
6
7
export NGROK_DOMAIN=ngrok.frognew.com

openssl genrsa -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -subj "/CN=$NGROK_DOMAIN" -days 5000 -out rootCA.pem
openssl genrsa -out device.key 2048
openssl req -new -key device.key -subj "/CN=$NGROK_DOMAIN" -out device.csr
openssl x509 -req -in device.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out device.crt -days 5000

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

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

编译ngrok的服务端ngrokd:

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

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

1
GOOS=darwin GOARCH=amd64 make release-client

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

2. 配置启动服务端ngrokd

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

1
useradd ngrok

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

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

1
2
3
4
5
6
systemctl enable ngrokd
systemctl start ngrokd

netstat -nltp | grep ngrokd
tcp6       0      0 :::14443                :::*                    LISTEN      8485/ngrokd
tcp6       0      0 :::8081                 :::*                    LISTEN      8485/ngrokd

3.nginx配置

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
server {
    listen 80;
    server_name *.ngrok.frognew.com;
    rewrite ^(.*)$  https://$host$1 permanent;
}


server {
    listen       443 ssl;
    server_name  *.ngrok.frognew.com;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host  $http_host:8081;
    proxy_set_header X-Nginx-Proxy true;
    proxy_set_header Connection "";

    ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
    ssl_certificate /etc/letsencrypt/live/frognew.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/frognew.com/privkey.pem;
    location / {
       # proxy_intercept_errors on;
       # error_page 404 /404.html;
       proxy_pass http://127.0.0.1:8081;
    }

}

4.ngrok客户端配置

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

1
python3 -m http.server 8080

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

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

启动ngrok客户端:

1
2
3
4
5
6
7
8
./ngrok -config ngrok.yml start py-http-demo

Tunnel Status                 online
Version                       1.7/1.7
Forwarding                    http://py-http-demo.ngrok.frognew.com:8081 -> 127.0.0.1:8080
Web Interface                 127.0.0.1:4040
# Conn                        0
Avg Conn Time                 0.00ms

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