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/tls
和assets/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服务,解决外网调试本地站点
[原创]搭建自己的 Ngrok 服务器, 并与 Nginx 并存
CentOS6.5下安装Docker
PS:如果开启防火墙的话,需要确保tunnelAddr的端口开放。 如何保证4443端口开放
可以参考这篇文章:CentOS安装之后的系统安全配置
有个防火墙的小细节,就是 iptables 需要放开 tunnelAddr 的端口的时候,UDP和TCP都需要放开,否则客户端连接不上。
刚刚评论说错了,应该是允许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
自己搭建内网穿透服务ngrok测试好久才成功,有点折腾,后来直接用nat123比较方便