部署自己的Ngrok内网穿透服务

ngrok是一个反向代理,可以将本地的web或tcp服务通过公共端口和外部建立一个安全通道,这样就可以通过外网直接访问本地对应的服务,在进行微信公众号等测试开发的时候非常有用。其原理图如下:

官方的ngrok和国内的一些ngrok服务免费版的时候,生成的域名都是随机的,在调试开发的时候非常不方便,好在ngrok是开源的(据说v1开源,v2不开源,但v1对于普通开发足够了),可以自己在服务器上进行部署。

域名解析

将需要外网访问的域名解析到所部署的服务器上,为了支持多级域名需要进行泛解析。比如ngrok的域名使用 ngrok.example.com 的话,需要将 ngrok.example.com*.ngrok.example.com 域名的A记录解析到所部署的服务器IP上,这样客户端就可以使用如 jet.ngrok.example.com 这样的域名进行访问了。

编译安装ngrok

安装GO环境

ngrok是使用GO进行开发的,所以首先需要安装GO环境,在CentOS中非常方便,直接使用 yum install golang -y 进行安装,安装完成之后在命令行中执行 go version 命令,如果出现版本号即表示安装成功。可以通过 go env 命令查看GO的环境信息。

下载ngrok

ngrok项目的官方地址是:https://github.com/inconshreveable/ngrok,可以通过 git clone https://github.com/tutumcloud/ngrok.git ngrok 进行安装,但发现在服务器上下载非常慢,也可以直接下载其源码的zip包,命令行如下(以安装到/usr/local/ngrok目录为例):

wget https://github.com/inconshreveable/ngrok/archive/master.zip -O /usr/local/ngrok.zip
unzip /usr/local/ngrok.zip -d /usr/local/
mv /usr/local/ngrok-master/ /usr/local/ngrok/
export GOPATH=/usr/local/ngrok/

生成自签名证书

因为单独安装自己的ngrok,所以需要使用openssl生成自签名的SSL证书,使用下面的命令(需要将NGROK_DOMAIN替换成自己的域名):

cd /usr/local/ngrok
NGROK_DOMAIN="ngrok.example.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
\cp rootCA.pem assets/client/tls/ngrokroot.crt
\cp device.crt assets/server/tls/snakeoil.crt
\cp device.key assets/server/tls/snakeoil.key

最后的复制命令是将生成的证书分别替换到 assets/client/tlsassets/server/tls 目录中,这两个目录分别存放着ngrok和ngrokd的默认证书。

编译并启动服务端

ngrokd 为服务端的执行文件,ngrok为客户端的执行文件,我们先来编译ngrok的服务端程序,在ngrok目录下面执行下面的命令:

make release-server

首次编译的时候需要从远程下载一些依赖文件,所以时间有一些长。需要注意的是,第一次不能执行 make clean 命令,否则会出现错误,对GO不是很熟悉,有一些资料说是跟使用yum方式安装GO有关。

编译成功之后会在项目目录的bin目录下面生成一个 ngrokd 的文件,使用下面的命令启动服务:

bin/ngrokd -domain="ngrok.example.com" -httpAddr=":8088" -httpsAddr=":8089" -tunnelAddr=":4000"

上面的参数中,domain表示服务的域名,httpAddr和httpsAddr分别表示HTTP和HTTPS的端口,tunnelAddr用来设置通道的端口,在客户端和服务端建立连接的端口,需要在防火墙中开放次端口(默认为4443)。

可以将命令加入到 /etc/rc.local 中使其开机启动,命令如下:

/usr/local/ngrok/bin/ngrokd -domain="ngrok.example.com" -httpAddr=":8088" -httpsAddr=":8089" -tunnelAddr=":4000" > /dev/null 2>&1 &

其中 /usr/local/ngrok 是ngrok的安装目录,最后不显示运行的日志,并且在后台运行。

使用客户端调用

编译客户端

接下来要编译专用的客户端,首先进入项目的根目录,使用下面的命令编译专用的客户端文件:

GOOS=windows GOARCH=386 make release-client

其中GOOS用乱设置操作系统,GOARCH用来设置对应的架构(386,amd64或arm),其含义如下:

Linux 平台 32 位系统:GOOS=linux GOARCH=386
Linux 平台 64 位系统:GOOS=linux GOARCH=amd64

Windows 平台 32 位系统:GOOS=windows GOARCH=386
Windows 平台 64 位系统:GOOS=windows GOARCH=amd64

MAC 平台 32 位系统:GOOS=darwin GOARCH=386
MAC 平台 64 位系统:GOOS=darwin GOARCH=amd64

ARM 平台:GOOS=linux GOARCH=arm

上面的命令表示编译windows下使用的32位的客户端程序,编译好的文件在bin目录下对应平台的目录下面。

启动客户端

接下来就可以使用客户端了,首先创建一个ngrok的配置文件ngrok.cfg:

server_addr: "ngrok.example.com:4000"
trust_host_root_certs: false

域名后面的端口是设置的通道端口。

接下来使用下面的命令运行客户端:

./ngrok -subdomain jet -config=ngrok.cfg 8080

其中jet是自定义的域名前缀,ngrok.cfg是上面的配置文件,8080是映射的本地到外网的端口,这样就可以使用 http://jet.ngrok.example.com:8088 访问本地8080的端口了。

另外,还可以使用 -proto 来指定协议的类型(默认是http协议),支持http、tcp等多种协议:

./ngrok -subdomain jet -config=ngrok.cfg -proto=tcp 8080

使用Nginx反向代理到80端口

上面通过子域名访问的时候不是默认的80端口,可以使用Nginx进行反向代理,配置信息如下:

upstream tdt_99tone_ngrok_server {
    server 127.0.0.1:8088 weight=1 max_fails=3 fail_timeout=30s;
    keepalive 64;
}

server {
    listen 80;
    server_name *.ngrok.example.com ngrok.example.com;
    charset utf-8;

    location / {
        proxy_next_upstream http_502 http_504 error timeout invalid_header;
            proxy_set_header Host  $host:8088;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://tdt_99tone_ngrok_server;
            proxy_redirect off;
    }

    access_log off;
    log_not_found off;
}

上面例子中的8088即ngrok设置的httpAddr参数设置的端口,有两处。这样就可以直接使用域名的80端口访问了。

PS:如果开启防火墙的话,需要确保tunnelAddr的端口开放。

参考资料:
VPS自搭建Ngrok内网穿透服务
从零教你搭建ngrok服务,解决外网调试本地站点

4 thoughts to “部署自己的Ngrok内网穿透服务”

  1. 有个防火墙的小细节,就是 iptables 需要放开 tunnelAddr 的端口的时候,UDP和TCP都需要放开,否则客户端连接不上。

  2. 刚刚评论说错了,应该是允许TCP但是不能加这个筛选条件『-m state --state ESTABLISHED,RELATED』

    iptables里边写这个就可以了:
    -A INPUT -p tcp -m tcp --dport 8088 -j ACCEPT
    -A INPUT -p tcp -m tcp --dport 8089 -j ACCEPT
    -A INPUT -p tcp -m tcp --dport 4000 -j ACCEPT

发表评论

电子邮件地址不会被公开。 必填项已用*标注