从编译到使用 - ngrok 服务搭建

背景

ngrok官网被q,而且访问速度慢,最主要的是不支持域名绑定,每次启动都是随机的二级域名,333

ngrok.cc-Sunny-Ngrok内网转发,服务器在香港,访问速度也慢,支持自定义系统域名,但只能定义一个(可以先定义TCP端口转发,保存后编辑,可以修改成系统分配域名,这样子就可以有多个自定义的系统域名,然并卵,客户端连接不上,一直提示该域名已被注册,官方做限制了吧)。

对比了上面两个服务,加上自己的需求:

  • 需要多个自定义的域名;
  • 尽量提升访问速度;决定自己搭建一个ngrok服务。

搭建ngrok服务需要的资源:

  • 一个域名,需要定义泛解析;
  • 一台ECS,需要公网IP

以下操作建议在Ubuntu上尝试,不建议在Windows上尝试(花了一个晚上搞都没搞成功,第二天早上花一个小时在Ubuntu上完美运行)

拉取ngrok源码

1
git clone git@github.com:inconshreveable/ngrok.git /work/ngrok

使用ngrok.com官方服务时,我们使用的是官方的SSL证书。

自建ngrokd服务,如果不想买SSL证书,我们需要生成自己的自签名证书,并编译一个携带该证书的ngrok客户端。

证书生成过程需要一个NGROK_BASE_DOMAIN

以ngrok官方随机生成的地址693c358d.ngrok.com为例,其NGROK_BASE_DOMAIN就是ngrok.com,如果你要提供服务的地址为example.ngrok.xxx.com,那NGROK_BASE_DOMAIN就应该是ngrok.xxx.com

这里以NGROK_BASE_DOMAIN="codz.me"为例,生成证书的命令如下:

1
2
3
4
5
6
cd /work/ngrok
openssl genrsa -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -subj "/CN=codz.me" -days 5000 -out rootCA.pem
openssl genrsa -out device.key 2048
openssl req -new -key device.key -subj "/CN=codz.me" -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
4
5
6
-rw-r--r-- 1 root root  985 Jul 21 08:52 device.crt
-rw-r--r-- 1 root root 895 Jul 21 08:52 device.csr
-rw-r--r-- 1 root root 1679 Jul 21 08:52 device.key
-rw-r--r-- 1 root root 1675 Jul 21 08:51 rootCA.key
-rw-r--r-- 1 root root 1103 Jul 21 08:52 rootCA.pem
-rw-r--r-- 1 root root 17 Jul 21 08:52 rootCA.srl

ngrok通过bindata将ngrok源码目录下的assets目录打包到可执行文件(ngrokd和ngrok)中去,assets/client/tls和assets/server/tls下分别存放着用于ngrok和ngrokd的默认证书文件。

我们需要将它们替换成我们自己生成的,因此这一步务必放在编译可执行文件之前。

所以,执行下面拷贝命令:

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

搭建go环境

Golang官网被q,下载安装包特么慢,Golang中国提供下载站点,我使用的是1.6.3版本;

下载并解压到go的默认安装路径/usr/local/go

1
2
3
cd /work/download
wget http://www.golangtc.com/static/go/1.6.3/go1.6.3.linux-amd64.tar.gz
tar -zxvf go1.6.3.linux-amd64.tar.gz -C /usr/local

修改PATH

1
2
3
4
vi /etc/profile
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin
source /etc/profile

正确配置后,go env可以查看得到当前go的环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH=""
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GO15VENDOREXPERIMENT="1"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0"
CXX="g++"
CGO_ENABLED="1"

编译安装

进入/work/ngrok目录

1
make release-server release-client

正常的话,ngrok/bin目录下应该有ngrokdngrok两个可执行文件,前面一个是服务端的,后面一个是客户端的;

需要Windows下的ngrok.exe可以通过下面命令构建,正常的话ngrok/bin/windows_amd64下会生成的ngrok.exe文件;

1
GOOS=windows GOARCH=amd64 make release-client

编译mac客户端(没实际操作)

1
GOOS=darwin GOARCH=amd64 make release-client

make很可能出现的错误有(来自Windows下的错误信息,Ubuntu下跑一切正常)

1
2
3
4
5
6
7
8
package archive/zip: unrecognized import path "archive/zip" (import path does not begin with hostname)
package bytes: unrecognized import path "bytes" (import path does not begin with hostname)
package encoding/json: unrecognized import path "encoding/json" (import path does not begin with hostname)
package encoding/xml: unrecognized import path "encoding/xml" (import path does not begin with hostname)
package errors: unrecognized import path "errors" (import path does not begin with hostname)
package expvar: unrecognized import path "expvar" (import path does not begin with hostname)
package flag: unrecognized import path "flag" (import path does not begin with hostname)
package fmt: unrecognized import path "fmt" (import path does not begin with hostname)

启动ngrok

服务端启动

1
2
cd /work/ngrok/bin
ngrokd -domain="codz.me" -httpAddr=":8088" -httpsAddr=":8089"

-domain的内容需要与上面生成证书所填的NGROK_BASE_DOMAIN一致;

自定义http、https提供服务的端口,据此你使用的二级域名应该是 http://[自定义].codz.me:8088

其他有用的启动脚本

  • 后台启动,输出日志

    1
    nohup ngrokd -domain="codz.me" -httpAddr=":8088" -httpsAddr=":8089" >/tmp/ngrok.log 2>&1 &
  • 后台启动不输出日志

    1
    nohup ngrokd -domain="codz.me" -httpAddr=":8088" -httpsAddr=":8089" >/dev/null 2>&1 &

客户端启动

拷贝编译好的ngrok客户端可执行文件到需要的机子上,给予执行权限,创建配置文件ngrok.cfg

1
2
3
4
5
6
7
8
server_addr: "codz.me:4443"
trust_host_root_certs: false

tunnels:
www:
subdomain: "test"
proto:
http: 192.168.1.121:8000

其中,server_addr需要跟NGROK_BASE_DOMAIN填写一致,否则服务端会报证书错误;

trust_host_root_certs需要设置为false;

trust_host_root_certs参数指示建立TLS连接到服务器时ngrok信任的根证书在计算机上。默认情况下,ngrok只信任的ngrok.com根证书;

trust_host_root_certs,对于ngrok官方server一般为true,现在官网国内不能用,通常为自编译的,有自己的证书,所以要设为false

启动客户端ngrok,之后就可以使用http://test.codz.me:8088来访问本地192.168.121:8000的资源了

1
./ngrok -config ngrok.cfg start www

配置文件写法,来自ngrok.cc

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
27
28
server_addr: "server.ngrok.cc:4443"
auth_token: "" #授权token,在www.ngrok.cc平台注册账号获取
tunnels:
sunny:
subdomain: "sunny" #定义服务器分配域名前缀,跟平台上的要一样
proto:
http: 80 #映射端口,不加ip默认本机
https: 80
web:
subdomain: "web" #定义服务器分配域名前缀
proto:
http: 192.168.1.100:80 #映射端口,可以通过加ip为内网任意一台映射
https: 192.168.1.100:80
web1:
hostname: "www.sunnyos.com"
proto:
http: 80
web2:
hostname: "sunnyos.com"
proto:
http: 80
ssh:
remote_port: 50001 #服务器分配tcp转发端口,如果不填写此项则又服务器分配
proto:
tcp: 22 #映射本地的22端口
ssh1: #将由服务器分配端口
proto:
tcp: 21

至此,all done.

实测,单个用户通过ngrok访问内网速度有所提升;

多个用户同时访问效果不是很明显,可能与ECS带宽有关(→_→ 1Mbps),ngrok使用多个自定义域名目的已达成,这是重点。

参考: