最近在做微信开发,涉及到内网穿透的问题,找了一下网上 ngrok 算是最佳的在内网调试微信服务的 tunnel 工具了,但是 ngrok 免费的账号不支持自定义域名,所以萌生了自己搭建 ngrok 服务器的想法;网上也有现成的,但是世界上没有免费的午餐,要不就是收费,要不就是免费但是偶尔会出现连接失败的问题(当然大多数时间是没有问题的)。
1、必要条件
- 服务器,用来搭建 ngrok 的服务器,必须有公网 ip,并且可以正常访问(本次测试使用的是 Ubuntu_14.04_64bit 系统);
- 域名,用来生成访问域名;
2、配置域名
首先需要在域名解析网站解析域名,后续就可以通过域名远程连接内网服务器,这也是我们最终的目的;
解析主机记录为 ngrock 和 *.ngrok.xxx.com 且全部为 A 记录类型,记录值填你的 vps 外网 IP 地址。
3、服务器配置
因为我用的是阿里云一键配置 web 环境搭建的服务器环境,所以搭建 ngrok 服务器只需要再安装的软件包如下:
- git // 获取 ngrock 源码
- build-essential // C/C++ 的编译环境
- go // ngrock 是基于 Go 语言编写的
- mercurial // 同 git 一样是一个分布式版本控制系统
或者你可以参照官方文档给出的各系统需要安装的软件包,Ubuntu 软件包安装命令:
sudo apt-get install curl git mercurial make binutils bison gcc build-essential
-
安装 Git 最新稳定版,详情请见 Ubuntu 使用 PPA 源安装最新版 Git;
-
安装 C/C++ 的编译环境
Ubuntu 缺省情况下,并没有提供 C/C++ 的编译环境,因此还需要手动安装,Ubuntu 提供了一个 build-essential 软件包,方便快捷:
sudo apt-get install build-essential
-
安装 Go 最新稳定版,详情请见 使用 Gvm 管理 Go 版本;
-
安装 mercurial 分布式版本控制系统
sudo apt-get install mercurial
至此,ngrock 所需要的软件包已安装完成
- 下载源码
因为我用的是阿里云一键配置的 web 环境,所以我将 ngrok 源码放在了 /alidata/www
目录下;当然你可以放在 /usr/local/src
目录下;
# 切换至 www 目录
cd /alidata/www/
# 使用 git 将 ngrok 下载至 www/ngrok 目录下
git clone https://github.com/moovweb/gvm.git ngrok
- 生成自签名证书
使用 ngrok.com 官方服务时,我们使用的是官方的 SSL 证书。自建 ngrok 服务,如果不想买 SSL 证书,我们需要生成自己的自签名证书,并编译一个携带该证书的 ngrok 客户端。
证书生成过程需要一个 NGROK_BASE_DOMAIN
,以 ngrok 官方随机生成的地址 xxx.ngrok.com 为例,其 NGROK_BASE_DOMAIN
就是 “ngrok.com”,如果你要提供服务的地址为 “example.ngrok.xxx.com”,那 NGROK_BASE_DOMAIN
就应该是 “ngrok.xxx.com”。本次测试,由于没有多余的备案域名,直接用的二级域名 “ngrok.varcn.com”。
我们已经下载好 ngrok 源码,现在切换到 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
# 生成证书后,需要替换证书
cp rootCA.pem assets/client/tls/ngrokroot.crt
- 编译执行文件
完成以上操作后,就可以编译二进制执行文件了:
make release-server release-client
如果编辑不成功,可能是部分资源包被墙的原因,需要翻墙,我的整个安装过程都是在墙外进行的;当然也有可能是其他原因造成的,详见 [安装过程中爬的坑];
编译完成之后会在 bin 目录下生产两个二进制执行文件 ngrok
和 ngrokd
,其中 ngrok
是客户端的,需要复制到你的电脑上的,ngrokd
是服务端的;
- 启动服务器
bin/ngrokd -tlsKey=device.key -tlsCrt=device.crt -domain="ngrok.varcn.com" -httpAddr=":8000" -httpsAddr=":8001"
httpAddr、httpsAddr 分别是 ngrok 用来转发 http、https 服务的端口,可以随意指定;
ngrokd 还会开一个 4443 端口用来跟客户端通讯(可通过 -tunnelAddr=":xxx" 指定);
如果你要搞微信开发,由于微信限制不能出现端口号,因此需要使用 80、443 端口来转发 http、https 服务的端口;
- 编译客户端执行文件
在 [编译执行文件] 那一步的时候我们已经编译出了客户端的二进制文件 ngrok
,但是这个是 Linux 系统下的,如果你的客户端是 Linux 系统可以直接下载到客户端执行;如果是 Windows 系统或者是 MacOS 系统,就需要重新编译客户端执行文件:
# Windows
GOOS=windows GOARCH=amd64 make release-client
# MacOS
GOOS=darwin GOARCH=amd64 make release-client
执行对应的命令会在 bin 目录下生成相对应的 windows_amd64、darwin_amd64 目录,ngrok
就存放在对应目录下,将对应的 ngrok 下载到本地;
- 设置本地客户端
我用的是 Mac,在登录用户文件目录创建 .ngrok
目录,然后在该目录下新建 ngrok.conf 配置文件:
server_addr: "ngrok.varcn.com:4443"
ntrust_host_root_certs: false
然后在终端启动客户端:
./ngrok -config=ngrok.conf -log=ngrok.log -subdomain=web 8080
-config
是配置项,链接的是我们上面新建的 ngrok.conf 配置文件-log
是日志文件,自动生成,如果不写,则不生成日志-subdomain
设置子域名,e.g. -subdomain=web 即为 web.ngrok.varcn.com- 最后的 8080 端口,可以随意设置,指向的是本地服务端口
链接成功之后,如下图:
浏览器访问 xweb.ngrok.varcn.com 就可以看到你本地 8080 端口的网页了;
另外,ngrok 提供了一个 Web 界面可以查看客户端的请求数据:http://localhost:4040;
4、常见问题
**1). 编译文件失败,报错:ngrok make: *** [deps] Error 1
**
看了一下编译的过程,是因为 import "context": import path doesn't contain a slash
导致编译失败,但是 google 之后也没有解决掉,这个折腾了好久,甚至重装了服务器系统,还是不行,后来偶然看到 Go 语言可以使用 GVM 来进行多版本安装管理,ngrok 是使用 go 语言编写的,怀疑是不是安装的 go 版本太低导致的,因为之前我是用 apt-get install golang
来安装的,因为是阿里云的服务器所以默认使用的是阿里的镜像地址,导致安装的 Go 版本比较低,使用 GVM 重装了最新版的 go1.11.1 版本之后,果然顺利编译完成!
2). 客户端启动服务时,报错:bash: ./ngrok: Permission denied
这是因为执行文件没有可执行权限,需要对 ngrok 执行文件加上可执行权限,方法详见:Mac 下终端访问文件出现 “Permission Denied” 解决方案
3). 客户端启动服务时,报错:cannot execute binary file
Google 了一下,造成这个错误的原因一般有以下几种:
通常情况下这个错误的原因有以下几个
- 可能是没有执行权限;
- 可能是 32 位机器跑了 64 位写的程序;
- 可能用了 ARM 机器跑 X86 生成的代码;
在上个问题中我们已经解决了执行权限的问题,所以可能是第二个和第三个原因;那么很可能是在 [编译客户端执行文件] 那一步时出现了问题,或者是编译的可执行文件命令参数错误导致的,确认客户端各项参数,重新编译;
4). 服务器使用 80 端口时,提示:address already in use
上面我们提到过,在微信开发时,由于微信限制不能出现端口号,需要使用 80、443 端口来转发 http、https 服务的端口,但是当我们使用 80 端口启动服务时,会提示 panic: listen tcp :80: bind: address already in use
,无法开启服务;
我们可以通过 nginx 代理来实现微信开发的 80 端口,比如我们本地的开发端口是 6001,微信开发服务器绑定的域名是 weixin.ngrok.varcn.com,服务端我们还是使用 8000、8001 端口来转发 ngrok 的 http 和 https 服务端口:
服务端:
配置 nginx:
// 域名 weixin.ngrok.varcn.com 的 80 端口来代理 ngrok 服务器的 8000 端口
server {
listen 80;
server_name weixin.ngrok.varcn.com;
location /
{
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://weixin.ngrok.varcn.com:6001/;
}
}
重启 nginx 服务;
开启 ngrok 服务:
bin/ngrokd -tlsKey=device.key -tlsCrt=device.crt -domain="ngrok.varcn.com" -httpAddr=":8000" -httpsAddr=":8001"
客户端:
// 配置 ngrok 的二级域名为 weixin,6001 是本地开发环境的端口
./ngrok -config=ngrok.conf -log=ngrok.log -subdomain=weixin 6001
然后访问 weixin.ngrok.varcn.com 就是访问的本地开发环境了;
5、TODO
- [x] Ngrok 后台运行
- [x] Ngrok 开机启动
参考资料: