10/22 发表评论

最近在做微信开发,涉及到内网穿透的问题,找了一下网上 ngrok 算是最佳的在内网调试微信服务的 tunnel 工具了,但是 ngrok 免费的账号不支持自定义域名,所以萌生了自己搭建 ngrok 服务器的想法;网上也有现成的,但是世界上没有免费的午餐,要不就是收费,要不就是免费但是偶尔会出现连接失败的问题(当然大多数时间是没有问题的)。

1、必要条件

  1. 服务器,用来搭建 ngrok 的服务器,必须有公网 ip,并且可以正常访问(本次测试使用的是 Ubuntu_14.04_64bit 系统);
  2. 域名,用来生成访问域名;

2、配置域名

首先需要在域名解析网站解析域名,后续就可以通过域名远程连接内网服务器,这也是我们最终的目的;

解析主机记录为 ngrock 和 *.ngrok.xxx.com 且全部为 A 记录类型,记录值填你的 vps 外网 IP 地址。

3、服务器配置

因为我用的是阿里云一键配置 web 环境搭建的服务器环境,所以搭建 ngrok 服务器只需要再安装的软件包如下:

  1. git // 获取 ngrock 源码
  2. build-essential // C/C++ 的编译环境
  3. go // ngrock 是基于 Go 语言编写的
  4. mercurial // 同 git 一样是一个分布式版本控制系统

或者你可以参照官方文档给出的各系统需要安装的软件包,Ubuntu 软件包安装命令:

sudo apt-get install curl git mercurial make binutils bison gcc build-essential
  1. 安装 Git 最新稳定版,详情请见 Ubuntu 使用 PPA 源安装最新版 Git
  2. 安装 C/C++ 的编译环境

Ubuntu 缺省情况下,并没有提供 C/C++ 的编译环境,因此还需要手动安装,Ubuntu 提供了一个 build-essential 软件包,方便快捷:

sudo apt-get install build-essential
  1. 安装 Go 最新稳定版,详情请见 使用 Gvm 管理 Go 版本
  2. 安装 mercurial 分布式版本控制系统
sudo apt-get install mercurial

至此,ngrock 所需要的软件包已安装完成

  1. 下载源码

因为我用的是阿里云一键配置的 web 环境,所以我将 ngrok 源码放在了 /alidata/www 目录下;当然你可以放在 /usr/local/src 目录下;

# 切换至 www 目录
cd /alidata/www/

# 使用 git 将 ngrok 下载至 www/ngrok 目录下
git clone https://github.com/moovweb/gvm.git ngrok
  1. 生成自签名证书

使用 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
  1. 编译执行文件

完成以上操作后,就可以编译二进制执行文件了:

make release-server release-client

如果编辑不成功,可能是部分资源包被墙的原因,需要翻墙,我的整个安装过程都是在墙外进行的;当然也有可能是其他原因造成的,详见 [安装过程中爬的坑];

编译完成之后会在 bin 目录下生产两个二进制执行文件 ngrokngrokd,其中 ngrok 是客户端的,需要复制到你的电脑上的,ngrokd 是服务端的;

  1. 启动服务器
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 服务的端口;

  1. 编译客户端执行文件

在 [编译执行文件] 那一步的时候我们已经编译出了客户端的二进制文件 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 下载到本地;

  1. 设置本地客户端

我用的是 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 来安装的,然后还是使用的阿里的镜像地址,然后重装了最新版的 go1.11.1 版本之后,果然顺利编译完成!

2). 客户端启动服务时,报错:bash: ./ngrok: Permission denied

这是因为执行文件没有可执行权限,需要对 ngrok 执行文件加上可执行权限,方法详见:Mac 下终端访问文件出现 “Permission Denied” 解决方案

3). 客户端启动服务时,报错:cannot execute binary file

Google 了一下,造成这个错误的原因一般有以下几种:

通常情况下这个错误的原因有以下几个

  1. 可能是没有执行权限;
  2. 可能是 32 位机器跑了 64 位写的程序;
  3. 可能用了 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

参考资料:

  1. How to run your own ngrokd server
  2. How to setup Ngrok with a self-signed SSL cert
  3. 10分钟教你搭建自己的ngrok服务器

发表评论

回到顶端