使用Docker搭建Ngrok服务器实现内网穿透

简介

ngrok是通向本地的安全隧道。主要可以实现下面几种需要:

  • 把本地的或者局域网内的web服务,暴露到一个公网ip或域名下
  • 把本地的或者局域网内的tcp端口,映射到一个公网ip的端口下

举例:

  • 需要要在本地调试微信公众号程序,但微信服务器的报文只能发到指定域名,这个时候,可以用ngrok把本地的web程序映射到指定域名。所有微信服务器的报文发到指定域名后,ngrok会把它转发到本地web程序。
  • 公司局域网内有台linux服务器,因为一某些原因,你需要从家里登录它,但你又没有公司路由的权限,无法做路由的端口映射。这个时候,要把服务器的ssh端口,用ngrok映射vps的某个端口下,回家之后直接登录vps,ssh映射的端口,就可以登录。

基本要求

  • 一台有公网ip的vps
  • 一个域名

操作步骤

我是直接拿别的写的dockerfile用的,这是地址

安装docker

1
sudo apt-get install docker.io

构建镜象

1
2
3
4
5
git clone https://github.com/hteen/docker-ngrok.git
cd docker-ngrok
sed -i '1cFROM golang:1.8.3' Dockerfile
sed -i '4cRUN apt-get install git make openssl' Dockerfile
docker build -t hteen/ngrok .

这里需要一些时间下来镜象

sed -i '1cFROM golang:1.8.3' Dockerfile这里是把第一行替换成FROM golang:1.8.3
因为原来的FROM golang:1.7.1-alpine会造成编译出来的文件无法在Ubuntu上执行。
见之前写的一篇文章记一次文件无法执行的查错过程

设置域名

假如你有 xxx.com 这个域名。
你拿出ngrok.xxx.com这个子域去做映射
需要在 dns 服务商那里把
为ngrok.xxx.com增加一条A记录,指向vps的ip。
并为*.ngrok.xxx.com增加一条A记录,也指向vps的ip。

运行镜象

1
2
3
4
docker run -idt --name ngrok-server \
-p 80:80 -p 443:443 -p 4443:4443 \
-v /data/ngrok:/myfiles \
-e DOMAIN='xxx.com' hteen/ngrok /bin/sh /server.sh

这里把容器内的端口直接按源来的映射出来了,可以实际需要要修改。
运行后,会要等一段时间,因为要编译客户端。

运行客户端

稍后会在/data/ngrok/bin/目录下生成客户端程序
每个平台的版本都有。
以windows64位来说,在windows_amd64目录下。
拷贝到自己的windows电脑上。
新建配置文件t.config

1
2
server_addr: "ngrok.xxx.com:4443"
trust_host_root_certs: false

放在和ngrok.exe相面的目录,并打开命令行窗口,并运行

1
ngrok -config=t.config -subdomain=xxx 8080

如果成功的话,会显示

1
2
3
4
5
...
Tunnel Status online
Version 1.7/1.7
Forwarding http://example.ngrok.xxx.com -> 127.0.0.1:8080
...

这样外网访问http://example.ngrok.xxx.com, 就会映射到本地的8080端中web服务器。

如果运行:

1
ngrok -config=t.config -proto=tcp 22

就会把本的22端口映射到服务器上一个随机端口。

原理

本质上也是一种隧道技术。分服务器端和客户端。
客户端发起连接(用到上面的4443端口)。
服务端接到连接的参数,另发起一个隧道。
将接收到exmaple域名的数据,加密转发到客户端。
客户端再转到指定端口

注意事项

在运行docker之后,会有一段时间cpu使用率很高,是在编译go中的内容。

因为ngrok有心跳机制,每次心跳均会产生日志,所以以docker方式运行,会产生很多日志。
实测试中,大概每个星期会产生100M的日志文件。
查年docker日志文件位置sudo docker inspect <id> | grep LogPath
查看大小sudo ls -lh /var/lib/docker/containers/<id>/<id>-json.log

常见错误

启动客户端后,发现连接失败。
检查域名映射有没有问题(nslookup)
检查那三个端口有没有在服务器上打开(telnet )
检查服务端日志
Failed to read message: read tcp 172.17.0.2:4443->119.xxx.xxx.49:51214: read: connection reset by peer

检查服务器证书是不是正确的
进入/data/ngrok目录,找到device.csr文件
运行openssl req -noout -text -in device.csr
检查Subject: CN是否为刚才设置的域名。

检查配置文件中的域名与端口有没有配置错误。

再把ngrok拷贝到linux下时,还可能会遇到这样的问题
ngrok: No such file or directory

扩展用法

服务器上的80端口和443端口上很宝贵的,如果被ngrok点用的话,别的服务就使用不了。
解决这个问题,可能在运行镜像的时候,去掉80与443端口的映射,直接用nginx转发到相应的端口。

参考

https://ngrok.com/
https://hteen.cn/docker/docker-ngrok.html
https://imququ.com/post/self-hosted-ngrokd.html