自建ngrok服务实现内网穿透
名词解释
内网穿透,又叫NAT穿透,是计算机用语,翻译过来就是你的电脑可以直接被你朋友访问。 通常我们的电脑是无法自己被访问的。因为我们的电脑缺少自己的独立的ip地址。现在ip稀缺,电信运营商已经不会随便分配固定ip给个人。
通常实现内网穿透,是通过路由器上端口映射来实现的。但是路由器通常不是每个人都有权限可以访问和设置,而且可能存在多级路由器较为复杂的网络结构。端口映射也无法实现。
这就需要ngrok来实现了。
原理
ngrok 建立一个隧道,将主机A的http请求 传递给 主机B,从而实现内网穿透。
ngrok分为client端(ngrok)和服务端(ngrokd),
实际使用中的部署如下:
图中内网主机上安装客户端。
公网主机 安装服务端。
client public 则代表 访问你电脑的用户或者朋友。
实践
现在都云时代,各种服务都能找到提供商。内网穿透也是如此。ngrok服务端相当麻烦,如果你只是简单的穿透,又不是什么敏感信息,可以找到很多服务提供商。例如https://ngrok.com/
- 下载客户端
根据你的个人电脑系统下载匹配的客户端。
下载地址: https://ngrok.com/download - 启动
./ngrok http 4444
ngrok by @inconshreveable (Ctrl+C to quit)
Session Status online
Session Expires 7 hours, 59 minutes
Version 2.2.8
Region United States (us)
Web Interface http://127.0.0.1:4040
Forwarding http://ada02116.ngrok.io -> localhost:4444
Forwarding https://ada02116.ngrok.io -> localhost:4444
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
如果此时访问 http://ada02116.ngrok.io
则等于访问了 localhost:4444
自建ngrok服务
使用别人的服务,存在一些限制。例如不稳定、安全问题,费用昂贵等。因此可能需要自己搭建ngrok服务端。
自建服务需要具备的条件:
1、具备独立ip的服务器。例如阿里云、腾讯云等。 我个人使用的阿里云ecs
2、需要域名。如果是国内则需要备案。我个人的域名是 xxxx.com
- 配置域名
增加2条A记录,指向你的服务器。这里我配置的是 ngrok.xxxx.com 和 *.ngrok.xxxx.com
添加安全组。
- 开放3个端口。这里我选择4443、4444、8081端口。
其中4443是ngrok自身通信使用。
4444 后面tcp端口转发使用。
8081 则是http请求转发使用。默认是80 但是这里80端口我分配给nginx了。
截图中少截图了4444的配置。
编译服务端。这里采用docker镜像来实现。
- 登录服务器,创建文件夹 ngrok-server ,包含2个文件
文件Dockerfile 内容如下
FROM golang:1.7.1-alpine
ADD build.sh /
RUN apk add --no-cache git make openssl
RUN git clone https://github.com/inconshreveable/ngrok.git --depth=1 /ngrok
RUN sed -i "109,109s/tcp/tcp4/g" /ngrok/src/ngrok/server/tunnel.go
RUN sed -i "57,57s/tcp/tcp4/g" /ngrok/src/ngrok/conn/conn.go
RUN sh /build.sh
EXPOSE 8081
VOLUME [ "/ngrok" ]
CMD [ "/ngrok/bin/ngrokd"]
文件build.sh内容如下
export NGROK_DOMAIN="ngrok.xxxx.com"
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
cp device.crt assets/server/tls/snakeoil.crt
cp device.key assets/server/tls/snakeoil.key
make release-server
GOOS=linux GOARCH=386 make release-client
GOOS=linux GOARCH=amd64 make release-client
GOOS=windows GOARCH=386 make release-client
GOOS=windows GOARCH=amd64 make release-client
GOOS=darwin GOARCH=386 make release-client
GOOS=darwin GOARCH=amd64 make release-client
GOOS=linux GOARCH=arm make release-client
需要修改的地方是export NGROK_DOMAIN="ngrok.xxxx.com"
修改为你自己的域名。
- 构建镜像。
运行
docker build -t ngrok .
注意 最后一个 .
别少了。
- 启动容器
运行
docker run -it -p 8081:8081 -p 4443:4443 -p 4444:4444 -d ngrok /ngrok/bin/ngrokd -domain="ngrok.xxxx.com" -httpAddr=":8081"
注意修改这里的ngrok.xxxx.com为你自己的域名。
到这里ngrok服务端已经完成。
- 下载客户端。
进入上面启动的容器,下载对应的客户端。
例如容器idc71b99a242fa
运行 docker inspect c71 可以查看这个容器的具体信息。
进入图中划线的目录下,再进入Bin下。
这里就是客户端。对应的不同系统的版本。
我个人电脑是mac,64位系统 ,需要选择 darwin_amd64文件夹下载。
386代表是 32系统。amd64表示 64位。
linux和window无需多言。
下载命令可以通过很多方式。例如ftp或者scp命令。
我选择的是scp命令。
scp -P 你的服务端端口 -r root@你的服务端ip地址:/var/lib/docker/volumes/6e5383924c5caf1b1b61250f31aa002308dcb371ebd9a7efad2419e20107a362/_data/bin/darwin_amd64/ngrok ./ngrok
这里要注意下权限,如果你不是用root,可能没法子拷贝。
- 配置客户端
创建配置文件 config.yml,其中内容如下
server_addr: "ngrok.xxxx.com:4443"
trust_host_root_certs: false
tunnels:
webapp:
proto:
http: 8081
subdomain: test
tcp12345:
remote_port: 4444
proto:
tcp: 12345
注意修改ngrok.xxxx.com为自己的域名。
这里配置了2个隧道。一个http的请求,一个tcp转发。
- 启动客户端
./ngrok -config=config.yml start-all
在浏览器访问 上图中地址即可看到本地内容。
增加tcp的转发截图。
测试下12345端口的功能。
- 监听12345端口。
执行
nc -l 12345
- 在其他电脑或者本地执行
nc ngrok.xxxx.com 4444
这样就可以了。输入和输出都会显示。
如图
最后
docker真得降低了很多问题的难度。
补充
微信公众号和小程序的开发中,需要http和https。 这里的请求都携带了端口,不符合要求。
由于我的服务器 80和443都分配给了nginx容器,因此这里无法测试。
下面是本人没测试的方案,主要是替换证书。
- 启动容器的时候指定证书路径,映射80和443端口
docker run -it -p 80:80 -p 443:443 -p 4443:4443 -d ngrok /ngrok/bin/ngrokd -domain="ngrok.xxxx.com" -tlsKey="/ngrok/assets/server/tls/snakeoil.key" -tlsCrt="/ngrok/assets/server/tls/snakeoil.crt"
这里使用是自建证书,是不被浏览器认可的。建议去申请免费证书。
然后将证书 挂载到镜像,再启动服务端。
已知的问题
因为ngrok会默认监听IPv6端口,考虑到国内的网络环境,我们需要修改一下代码,否则将无法使用。主要修改两个地方,一个是src/ngrok/server/tunnel.go的109行net.ListenTCP后面的"tcp"改成"tcp4",另一处是src/ngrok/conn/conn.go的57行的net.Listen后的"tcp"同样改成"tcp4"。