实现内网穿透紫ngrok无法通过天墙之后,国内也出现了一批成熟的商业化实现方案,诸如花生壳、net123、Sunny-ngrok等。不过免费的极不稳定还有流量带宽限制,最后还是决定自己搭一个。本文利用ngrok搭建一个用于内网穿透的环境。需求是通过一层反向代理,实现通过一个外网域名访问一个部署在局域网上的服务。
准备
步骤
GO语言环境搭建
ngrok项目是用GO语言实现的,需要先安装GOLANG开发环境,系统不限,因为GO语言是跨平台的!安装过程很简单,参考官网的教程即可!获取ngrok源码
1
2
3
4
5# 下面是直接去ngrok的github地址下载(待会儿make的时候还会需要装几个其它的依赖,可能会出现很多问题)
git clone https://github.com/inconshreveable/ngrok.git
# 下面是笔者将代码clone下来,并添加了相应依赖之后的地址,如果用上面的方式出现错误,可以clone下面的地址
git clone https://github.com/SunnyQjm/ngrok.git解析域名
因为我们自己搭建,需要使用自己的域名(以 test.j.cn )为例,我们需要做以下解析:
1
2
3
4test.j.cn ------------> A记录到你的VPS服务器的IP
# ngrok可以指定子域名,下面的解析方式可以让任意子域名都能得到正确的解析
*.test.j.cn ------------> CNAME记录到 test.j.cn生成签名证书
- 因为我们是自己搭建,就不能用ngrok官方的SSL证书,需要自己生成
- 下面生成的证书在编译项目的时候要用到,所以务必要在编译之前生成
- 需要注意的是,客户端和服务器的证书必须是同一份,这样在程序在认证的时候才能正确解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# 首先导出环境变量,将下面的值替换成你的域名
export NGROK_DOMAIN="test.j.cn"
#先进入到ngrok的根目录,生成证书的操作需要在根目录下进行
cd ngrok
# 下面的命令用于生成证书
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默认的证书
cp rootCA.pem assets/client/tls/ngrokroot.crt
cp device.crt assets/server/tls/snakeoil.crt
cp device.key assets/server/tls/snakeoil.key编译运行
服务端(VPS服务器一端)
首先指定一下环境变量,在不同的操作系统下需要指定不同的环境变量,才能正确编译(默认是Linux 64位的配置,如果你的服务器是64位的Linux系统,也可以不指定,直接用默认的就行)
1
2
3GOOS=linux GOARCH=amd64
#如果是32位系统,这里 GOARCH=386
#如果是windows系统,GOOS=windows然后make出服务端程序
1
make release-server
如果编译成功,你会在bin目录下看到ngrokd程序
1
2
3
4cd bin
# 查看使用帮助
./ngrokd -h查看使用帮助
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18# 查看使用帮助
./ngrokd -h
-domain string
Domain where the tunnels are hosted (default "ngrok.com")
-httpAddr string
Public address for HTTP connections, empty string to disable (default ":80")
-httpsAddr string
Public address listening for HTTPS connections, emptry string to disable (default ":443")
-log string
Write log messages to this file. 'stdout' and 'none' have special meanings (default "stdout")
-log-level string
The level of messages to log. One of: DEBUG, INFO, WARNING, ERROR (default "DEBUG")
-tlsCrt string
Path to a TLS certificate file
-tlsKey string
Path to a TLS key file
-tunnelAddr string
Public address listening for ngrok client (default ":4443")启动服务端
1
2
3
4
5# 如果不能执行,你可能需要用 sudo chmod +x ngrokd 给它执行权限
# domain域输入之前生成证书时指定的域名
# httpAddr 指定转发http协议的哪个端口
# httpAddrs 指定转发https协议的哪个端口(如果不需要可以省略)
./ngrokd -domain="$NGROK_DOMAIN" -httpAddr=":8000" -httpsAddr=":4433"如果执行成功,你会看到类似以下界面:
1
2
3
4
5[16:23:40 CST 2018/03/19] [INFO] (ngrok/log.(*PrefixLogger).Info:83) [registry] [tun] No affinity cache specified
[16:23:40 CST 2018/03/19] [INFO] (ngrok/log.Info:112) Listening for public http connections on [::]:9748
[16:23:40 CST 2018/03/19] [INFO] (ngrok/log.Info:112) Listening for public https connections on [::]:443
[16:23:40 CST 2018/03/19] [INFO] (ngrok/log.Info:112) Listening for control and proxy connections on [::]:4443
[16:23:40 CST 2018/03/19] [INFO] (ngrok/log.(*PrefixLogger).Info:83) [metrics] Reporting every 30 seconds自此,服务端算是配置好了
客户端(部署了服务,需要内网穿透访问的主机)
客户端要做的事情就是指定把本机的那个端口暴露给VPS服务器,客户端要在该端口上部署了服务(web服务或者tomcat服务等),这样VPS就能够将请求转发到该端口对应的服务上了
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
29
30
31
32
33
34# 因为前面说过,服务端和客户端的证书要是同一份,这样认证才能通过,所以好的解决方案是在服务端上把客户端程序也编译出来,然后通过scp命令拷贝到客户端
# 假设我要在mac上运行客户端,需要在编译命令前加上一些参数(如果客户端服务器也是Linux 64位,则不用指定环境变量)
GOOS=darwin GOARCH=amd64 make release-client
make release-client
# 编译好后scp到本地
scp xxx xxx
# 下面开始本地配置(下面的配置在客户端进行)
# 新建一个配置文件(在ngrok/bin目录下)
vim ngrok.cfg
# 添加一下两行
# 第一行是将要绑定的域名+4443端口(因为ngrok服务端默认有一个服务是坚挺在4443端口的,客户端会通过这个端口与之相连)==> 记得将域名换成自己在生成证书时指定的
server_addr: "test.j.cn:4443"
trust_host_root_certs: true
# 帮助信息
./ngrok -h
Examples:
ngrok 80
ngrok -subdomain=example 8080
ngrok -proto=tcp 22
ngrok -hostname="example.com" -httpauth="user:password" 10.0.0.1
# 80就是我们要转发的端口了
./ngrok -config=./ngrok.cfg 80
# 指定协议和端口,不指定默认是 http+https
./ngrok -config=./ngrok.cfg -proto=tcp 22
# 指定子域名,不指定就会随机生成
./ngrok -config=./ngrok.cfg -subdomain=test 80连接成功会显示如下状态:
注意:
- 上文中所有出现域名的地方都要统一,客户端和服务端的证书要是同一份,否则在连接的时候会出现bad certification
参考: