自建ngrok服务实现内网穿透

什么是ngrok?

ngrok是一个ddns服务,为内网机器绑定一个公网域名之后,可以将内网机器的端口暴露在公网上,方便内网程序的调试和开发。比如,在内网机器上,想开HTTP服务80端口,那么就可以使用ngrok,而不需要借助路由器的端口转发和端口映射功能。当然这样做的前提是,你需要一台可以从内网访问到的公网服务器,比如阿里云的云主机,或者便宜的搬瓦工VPS(一年20美金即可)。

ngrok的使用条件

  1. 域名 (很多域名如.top,.online域名首年都是0.88美金,具体访问namecheap.com或者namesilo.com)
  2. 拥有独立IP的VPS或云主机(阿里云或者搬瓦工)

ngrok服务器端编译

我使用的搬瓦工VPS,装的CentOS 6.5 64位系统。

安装go的编译环境

  • 环境安装
1
yum -y install zlib-devel openssl-devel perl hg cpio expat-devel gettext-devel curl curl-devel perl-ExtUtils-MakeMaker hg wget gcc gcc-c++ build-essential  mercurial
  • 安装go
1
2
3
4
wget http://www.golangtc.com/static/go/1.7rc6/go1.7rc6.linux-386.tar.gz
tar -zxvf go1.7.6.linux-386.tar.gz
mv go /usr/local/
ln -s /usr/local/go/bin/* /usr/bin/

检查是否安装成功,命令 go env

如果看到下面的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
➜  ~ go env
GOARCH="386"
GOBIN=""
GOEXE=""
GOHOSTARCH="386"
GOHOSTOS="linux"
GOOS="linux"
GOPATH=""
GORACE=""
GOROOT="/usr/lib/golang"
GOTOOLDIR="/usr/lib/golang/pkg/tool/linux_386"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build672964581=/tmp/go-build"
CXX="g++"
CGO_ENABLED="1"

说明go环境安装成功

下面开始进行go的编译,在参考其他帖子的过程中,推荐go的编译需要git版本>=1.9,而我阿里云上的git是1.7.1因此需要升级git,我没有将步骤写在这里,因为觉得和主题不是很相关,如果你的git版本较低,可以参考这个帖子将较低版本的git升级到新版

开始编译服务器端

声明编译的路径和必要的域名(域名改成你自己的)

1
2
3
4
5
6
7
#这是原版代码,编译过程会从github下载依赖包,国内网络坏境可能会有问题,下面使用的是阿里云的OSS提供的地址
#git clone https://github.com/inconshreveable/ngrok.git ~/ngrok
#推荐直接下载完整包,一次性编译无需下载
wget http://tinyoss.oss-cn-qingdao.aliyuncs.com/ngrok.zip
unzip ngrok.zip
export GOPATH=~/ngrok/ #指定GO语言所在位置
export NGROK_DOMAIN="ngrok.xuebin.me" #这个需要改成你的域名

生成证书

1
2
3
4
5
6
cd ~/ngrok
openssl genrsa -out base.key 2048
openssl req -new -x509 -nodes -key base.key -days 10000 -subj "/CN=$NGROK_DOMAIN" -out rootCA.pem
openssl genrsa -out server.key 2048
openssl req -new -key server.key -subj "/CN=$NGROK_DOMAIN" -out server.csr
openssl x509 -req -in server.csr -CA rootCA.pem -CAkey base.key -CAcreateserial -days 10000 -out server.crt

生成的3个文件,分别是rootCA.pem、server.csr和server.crt,各有用处。

下面将这3个文件复制到指定位置

1
2
3
cp rootCA.pem assets/client/tls/ngrokroot.crt -i
cp server.crt assets/server/tls/snakeoil.crt -i
cp server.key assets/server/tls/snakeoil.key -i

不同的编译类型

如果要为不同的服务端编译,需要根据系统类型,更改go env中的变量。主要是GOOSGOARCH这两项。

为linux-x86-64系统编译

比如我的阿里云主机,系统是CentOS 6.5 64位,就属于这种类型,分别指定变量并且执行服务器端编译:

1
2
3
export GOOS=linux 
export GOARCH=amd64 #如果是64位系统,则修改为386
make release-server #如果是客户端,这里是make release-client

编译完成后,在路径~/ngrok/bin/linux_amd64下,会看到一个名为ngrokd的文件,这个即为服务端运行文件。

为树莓派系统编译

1
2
3
export GOOS=linux 
export GOARCH=arm
make release-server #如果是客户端,这里是make release-client

编译完成后,在路径~/ngrok/bin/linux_arm下,会看到一个名为ngrokd的文件,这个即为服务端运行文件。

为windows系统编译服务端文件

1
2
3
export GOOS=windows  
export GOARCH=386 #如果是64位windows,这里修改为amd64
make release-server #如果是客户端,这里是make release-client

编译完成后,在路径~/ngrok/bin/windows_386下,会看到一个名为ngrok.exe的文件,这个即为服务端运行文件。

为Mac OS编译服务端文件

1
2
3
export GOOS=darwin
export GOARCH=amd64 #如果是64位windows,这里修改为amd64
make release-server

编译完成后,在路径~/ngrok/bin/darwin_amd64下,会看到一个名为ngrokd的文件,这个即为服务端运行文件。

. GOOS参数可以指定为windows、linux、freebsd、darwin (Mac OS X 10.5 or 10.6) 和nacl(Chrome 的Native Client 接口) . GOARCH可以指定为amd64 (64-bit x86)、386 (32-bit x86)、和arm (32-bit ARM)。

绑定域名

将编译配置时的域名ngrok.xuebin.me,解析到服务器IP

xuebin.me添加值为ngrok*.ngrok的A记录即可。

服务器端部署

将编译好的可执行文件移至/usr/bin/下

1
cp ~/ngrok/bin/ngrokd /usr/bin/

为ngrokd的运行单独开一个screen

如果你发现screen执行失败,说明你没有装,CentOS可以通过yum install -y screen来安装screen服务,具体可以参见我的另外一个帖子《screen的安装和使用》,讲述了强大的screen的安装和使用。

1
screen -S ngrokd

运行ngrokd

1
2
3
4
#http
./bin/linux_amd64/ngrokd -domain="$NGROK_DOMAIN" -httpAddr=":6060" -httpsAddr=":6061" -tunnelAddr=":6062"
#https设置了tls
#./bin/linux_amd64/ngrokd -domain="$NGROK_DOMAIN" -httpAddr=":6060" -httpsAddr=":6061" -tunnelAddr=":6062" -tlsKey=./device.key -tlsCrt=./device.crt

这里详细解释一下上面这句话中参数的含义

  • httpAddr 是访问普通的http使用的端口号,用后面用 ngrok.xuebin.me:6060 来访问服务
  • httpsAddr 是访问的https使用的端口号,同上,只不过是需要https的服务访问才用这个端口
  • tunnelAddr 是通道的端口号,这个端口是Ngrok用来通信的,所以这个端口在服务器上和客户端上设置必须要对应才可以正常的链接,默认不填写好像是4433。

防火墙呢

ngrok客户端编译

Windows客户端

好了,你现在服务器上,已经有一个ngrokd服务在运行了,那么客户端与服务器端如何连接呢?

也没啥难的,就是继续修改go环境的编译参数,继续编译客户端就好了

1
2
3
GOOS=windows
GOARCH=amd64
make release-client

我是在64位的windows 10环境下连接服务器,其他参数,大家可以参考服务端程序的参数,自行进行相应的修改。

编译完成后,在路径~/ngrok/bin/windows_amd64下,会看到一个名为ngrok.exe的文件,这个即为服务端运行文件。使用scp命令或通过ftp,将其下载到你的本地电脑上即可。

然后,创建一个ngrok.cfg文件来保存配置,内容很简单

1
2
server_addr: "ngrok.xuebin.me:6062"
trust_host_root_certs: false

然后,记得要采用cmd命令的方式来运行客户端程序,一句话

1
./ngrok -subdomain resiliosync -proto=http -config=ngrok.cfg 8000

上面的8000是你想暴露的http服务端口号。如果还想暴露更多诸如ssh或者其他更多服务的端口,自行添加即可。

树莓派或者linux客户端

在ngrokd文件的同一级目录下,vim或者touch创建ngrok.cfg文件(yml语言格式)

这里假设你想用ssh端口管理树莓派或者你的linux服务器,ssh的默认端口是22,但是你想让这台树莓派将外网的10086端口转发到内网的22,那么设置如下:

1
2
3
4
5
6
7
8
9
10
11
server_addr: ngrok.xuebin.me:6062
trust_host_root_certs: false
tunnels:
http:
proto:
http: 80 #这里是你想暴露的http服务端口号
subdomain: pi #这里给你想暴露的http服务起个名字
ssh:
remote_port: 10086
proto:
tcp: 22

这里我把需要转发的httpssh都写入配置文件,其他tcp服务语法格式和ssh相似,remote_port为远程端口,等下外网连接的时候用的就是这个remote_port的端口号。tcp:22为需要转发的本地端口。

在树莓派上运行

1
./ngrok-for-arm -config=ngrok.cfg start http ssh

这时候,处于内网的树莓派就可以通过连接你的服务器(运行ngrokd的外网服务器)来提供802210086等端口的服务了。

比如,你的外网机器,就可以使用ssh来连接和管理处于内网环境的树莓派了。

1
ssh -p 10086 pi@ngrok.xuebin.me

结语

ngrok确实是非常强大,没啥想说的了。

参考文章

  1. 自搭Ngrok实现树莓派内网穿透
  2. 关于 ngrok 使用上的注意事项
  3. 编译ngrok的Windows、Mac的客户端
  4. 内网穿透 ngrok 服务器和客户端配置
  5. 利用ngrok完成内网穿透
坚持原创技术分享,您的支持将鼓励我继续创作!