Ngrok实现内网穿透
在一些内部环境,我们经常需要在外网访问到某些web服务。比如在自己家里的宽带网络里,想让智能家居硬件能够让手机流量网络访问到;比如参加黑客编程马拉松,手头上笔记本运行的程序想要接入外网演示;比如想要在家里开发第三方支付等等,就需要借助内网穿透来给外网访问到这些服务。
常见的商业应用有花生壳,处于安全性考虑,希望自己也能够搭建一个内网穿透的服务。Ngrok则是一个不错的内网穿透的开源软件,可以方便地自由搭建内网穿透服务。本文详细介绍了Ngrok的安装,泛解析域名的配置,以及将ngrok添加为系统服务,并使用nginx配置反向代理。
一、基本原理
ngrok分为服务端和客户端:
ngrok客户端
:在需要开启内网穿透的PC上,安装内网电脑上的终端软件,ngrok客户端来把本地端口提供给外网。ngrok服务端
:ngrok客户端启动后默认会使用ngrok官网的域名代理,由于ngrok官网的域名代理服务器在美国,很多时候需要自建ngrok服务端。- 用户客户端可以通过访问绑定在
ngrok服务端
的域名,从而访问到内网的服务。
本文章中,可能会提到一些例子,其中具体数值可以自己替换:
项目 | 说明 |
---|---|
xxx.com | 顶级域名 |
xxx.xxx.com | 二级域名 |
*.xxx.xxx.com | 二级泛解析域名 |
二、Ngrok安装方法
1、安装依赖环境
yum install -y gcc git openssl golang
2、检查环境
git --version
go version
git 版本 >= 1.7 go 版本 >= 1.8
3、下载ngrok源码
git clone https://github.com/inconshreveable/ngrok.git /usr/local/ngrok
4、生成ngrok证书
这里生成ngrok证书,与绑定ngrok服务端域名有关,所以务必认真检查域名。
方法一:自己构造SSL证书
为了方便可以配置一个系统变量
vim /etc/profile
在文件末尾添加一行
export NGROK_DOMAIN="xxx.com"
保存后,执行source使变量生效
source /etc/profile
echo $NGROK_DOMAIN
开始生成证书文件
cd /usr/local/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
# 替换新的证书 系统会提示是否覆盖,按"y"回车确定即可
cp rootCA.pem assets/client/tls/ngrokroot.crt
cp device.crt assets/server/tls/snakeoil.crt
cp device.key assets/server/tls/snakeoil.key
方法二:购买/生成第三方机构颁发的SSL证书
进入网站https://www.sslforfree.com/申请CA证书,这里会验证DNS,可以申请泛域名证书,如*.xxx.com的证书。
申请步骤可以参考:https://cloud.tencent.com/developer/news/151294
最后会下载相应的证书文件,这里可以按下面更改下文件名:
ca_bundle.crt => assets/client/tls/ngrokroot.crt
certificate.crt => assets/server/tls/snakeoil.crt
private.key => assets/server/tls/snakeoil.key
这里可以理解一下以下路径
路径 | 说明 |
---|---|
assets/client/tls/ngrokroot.crt | 这是客户端ssl连接的CA证书文件(包含根证书和中间证书) |
assets/server/tls/snakeoil.crt | 这是服务端ssl连接的Certificate证书文件 |
assets/server/tls/snakeoil.key | 这是服务端ssl私钥文件 |
5、编译ngrok服务端
这一步服务端编译,则会默认去加载上面的证书文件,生成对应的服务端。后面可以使用第三方机构颁发的CA证书,需要根据以下
GOOS=linux GOARCH=amd64 make release-server
编译成功后,放在当前目录下的bin目录中,如:bin/ngrokd
6、编译ngrok客户端
生成了服务端,客户端也需要使用服务端的证书配置,所以客户端务必在这里编译生成。 不同操作系统编译命令如下: 32位linux客户端:
GOOS=linux GOARCH=386 make release-client
64位linux客户端:
GOOS=linux GOARCH=amd64 make release-client
32位windows客户端:
GOOS=windows GOARCH=386 make release-client
64位windows客户端:
GOOS=windows GOARCH=amd64 make release-client
32位mac平台客户端:
GOOS=darwin GOARCH=386 make release-client
64位mac平台客户端:
GOOS=darwin GOARCH=amd64 make release-client
ARM平台linux客户端:
GOOS=linux GOARCH=arm make release-client
客户端生成成功后文件将放在bin目录对应的文件夹内,例如64位mac平台客户端,则放在bin/darwin_amd64/
目录下
7、启动ngrok服务端
使用默认的ssl证书启动ngrok服务
/usr/local/ngrok/bin/ngrokd -domain="$NGROK_DOMAIN" -httpAddr=":8800" -httpsAddr=":8443" -tunnelAddr=":4443"
注意这里$NGROK_DOMAIN为之前配置的环境变量。如果手动配置,注意和第4步生成证书域名一致
- domain参数:绑定的ngrok域名,需要和之前生成证书的域名保持一致,
如果是CA证书是泛域名,则可以直接写域名即可,如证书是*.xxx.com,这里参数则可以写xxx.com
- httpAddr参数:为ngrok代理的http端口
- httpsAddr参数:为ngrok代理的https端口
- tunnelAddr参数:为ngrok通道的端口,用于客户端和服务端通信的端口,所以这个端口服务端配置和客户端配置需要保持一致才能正常连接。
使用其他证书启动ngrok服务
/usr/local/ngrok/bin/ngrokd -domain="xxx.com" -httpAddr=":8800" -httpsAddr=":8443" -tunnelAddr=":4443" -tlsKey="/usr/local/ngrok/bin/snakeoil.key" -tlsCrt="/usr/local/ngrok/bin/snakeoil.crt"
- tlsKey参数:指定服务端私钥
- tlsCrt参数:指定服务端SSL证书
- domain参数:绑定的ngrok域名,需要和生成证书的域名保持一致,
如果是CA证书是泛域名,则可以直接写域名即可,如证书是*.xxx.com,这里参数则可以写xxx.com
温馨提示
如果有提示端口被占用,则可以用以下命令查出占用端口的进程号,然后kill
ps -A |grep ngrokd
控制台找到对应的进程号,杀掉进程。然后再重新尝试启动服务端
kill -9 20746
8、ngrok服务端配置为系统服务
启动ngrok后,关闭控制台,ngrok也会被关闭,我们需要把ngrok设为系统服务。
方案一:使用shell脚本
(1)创建一个start.sh 在/usr/local/ngrok目录下创建一个start.sh
vim /usr/local/ngrok/start.sh
编辑以下内容:
/usr/local/ngrok/bin/ngrokd -domain="xxx.xxx.com" -httpAddr=":8800" -httpsAddr=":8443" -tunnelAddr=":4443"
给文件可执行权限
chmod 755 /usr/local/ngrok/start.sh
(2)创建启动脚本文件
sudo vim /etc/init.d/ngrok
!/bin/sh
# ngrok Startup script By Lizhijun
# chkconfig: 345 85 15
BEGIN INIT INFO
Provides: ngrok
Required-Start:
Required-Stop:
Default-Start: 2 3 4 5
Default-Stop: 0 1 6
Short-Description: Start or stop the ngrok Proxy.
END INIT INFO
ngrok_path=/usr/local/ngrok
case "$1" in
start)
echo "start ngrok service.."
sh ${ngrok_path}/start.sh
;;
*)
exit 1
;;
esac
给文件可执行权限
chmod 755 /etc/init.d/ngrok
(3)添加启动服务 ngrok
chkconfig --add ngrok
(4) 测试启动
service ngrok start
方案二:添加service
在/etc/systemd/system/
目录下创建ngrok.service
vim /etc/systemd/system/ngrok.service
编辑以下内容:
[Unit]
Description=ngrok service
After=network.target
[Service]
PrivateTmp=true
Type=simple
Restart=always
RestartSec=1min
StandardOutput=null
StandardError=null
ExecStart=/usr/local/ngrok/bin/ngrokd -tlsKey=/usr/local/ngrok/assets/server/tls/snakeoil.key -tlsCrt=/usr/local/ngrok/assets/server/tls/snakeoil.crt -domain=xxx.xxx.com -httpAddr=:8800 -httpsAddr=:8443 -tunnelAddr=:4443 &
ExecStop=/usr/bin/killall ngrokd
[Install]
WantedBy=multi-user.target
注意,这里启动命令可以改为之前使用过的启动ngrok命令。注意需要指定SSL证书的绝对路径。
一些基本操作
# 查看运行状态
systemctl status ngrok.service
# 启动ngrok
systemctl start ngrok.service
# 停止ngrok
systemctl stop ngrok.service
#显示启动失败的服务
systemctl --failed
#重启服务
systemctl restart ngrok.service
#重新加载服务配置文件
systemctl reload ngrok.service
#查询服务是否开机启动
systemctl is-enabled ngrok.service
# 设置开机启动
systemctl enable ngrok.service
# 取消开机
systemctl disable ngrok.service
9、启动ngrok客户端
在第6步(#6、编译ngrok客户端
)生成了客户端后,可以下载到本地电脑,然后通过终端软件启动: 假设ngrok下载到了本地的~/ngrok/路径下,目录结构如下:
~/ngrok/
|-ngrok ngrok客户端
|-ngrok.cfg ngrok配置文本文件
创建配置文件ngrok.cfg,编辑以下内容:
server_addr: "xxx.com:4443"
trust_host_root_certs: false
说明:server_addr为代理的域名,可以配置之前搭建ngrok服务端的域名地址,4443为服务端启动时配置的tunnelAddr参数 注意:server_addr配置的域名需要去域名服务商解析域名 如果是一级域名,需要添加解析 @.xxx.com
和 *.xxx.com
到你的服务端ip 如果是二级域名,需要添加解析 xxx.xxx.com
和 *.xxx.xxx.com
到你的服务端ip
1)直接启动ngrok客户端
cd ~/ngrok/
./ngrok -config=ngrok.cfg -log=ngrok.txt -subdomain wechat 8010
- config参数:为我们刚创建的配置文件路径
- log参数:为ngrok写日志的文件名
- subdomain参数:为ngrok启动后我们像服务端注册的子域名的名称,如wechat,服务端将为我们把wechat.xxx.com转发到我们这台主机
- 最后一个参数8010:是指要映射本机哪个端口给服务端
客户端启动后,会显示如下信息:
Tunnel Status online
Version 1.7/1.7
Forwarding http://wechat.xxx.xxx.com:8800 -> 127.0.0.1:8010
Forwarding https://wechat.xxx.xxx.com:8800 -> 127.0.0.1:8010
Web Interface 127.0.0.1:4040
# Conn 0
Avg Conn Time 0.00ms
这时候我们就可以访问http://wechat.xxx.xxx.com:8800,来打开内网127.0.0.1:8010的服务了。 如果需要配置https客户端,请参考:https://www.jianshu.com/p/4b03fb532145
2)配置tunnels启动ngrok客户端
除了第一种直接启动ngrok的方式,还可以在ngrok.cfg
配置文件配置好tunnels方便启动。 比如我们有2个服务:
Web服务 | 本机端口 | 需要绑定的二级域名 | tunnels |
---|---|---|---|
官网项目 | 8001 | www | www |
微信项目 | 8002 |
server_addr: "*.ngrok.xxx.com:4443"
trust_host_root_certs: false
tunnels:
www:
proto:
http: 8001
subdomain: www
wechat:
proto:
http: 8002
subdomain: wechat
注意:绑定的如果是泛解析二级域名,服务端配置的域名证书应该是*.xxx.xxx.com
。这里server_addr
可以配置成x.xxx.xxx.com:4443
,实际上x.xxx.xxx.com
仍然会被当做*.xxx.xxx.com
,具体绑定的三级域名会根据启动的subdomain
决定绑定三级域名。
启动命令:
./ngrok -config ngrok.cfg start www wechat
启动成功后,外网可以通过绑定的域名访问内网的web服务了。
域名 | 本地的Web服务 |
---|---|
www.ngrok.xxx.com | 127.0.0.1:8001 |
wechat.ngrok.xxx.com | 127.0.0.1:8002 |
或者可以使用start-all
启动全部的tunnels
./ngrok -config ngrok.cfg start-all
三、配置nginx反代理
配置nginx反代理,实现域名80端口访问ngrok。 在微信公众号开发服务端配置域名,微信只支持80端口和443端口,这时候我们就需要用Nginx来转发80或443端口的数据到ngrok上。安装Nginx方法请参考其他文章,本篇文章不细讲。 添加配置nginx:
server {
listen 80;
listen 443 ssl;
server_name *.xxx.com;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host:8800;
proxy_set_header X-Nginx-Proxy true;
proxy_set_header Connection "";
proxy_pass http://127.0.0.1:8800;
}
ssl_certificate /usr/local/ngrok/bin/snakeoil.crt;
ssl_certificate_key /usr/local/ngrok/bin/snakeoil.key;
ssl_session_timeout 5m;
}
重启nginx生效。这样就可以在用户客户端直接使用80或者443端口直接访问到对应的web服务了。