搭建自己的ngrok服务实现内网穿透

学校有台服务器上跑我们的自己用的gitlab,然而分配的ip是内网网段,在校外或者使用手机流量就没办法访问,跟信管中心申请公网ip又得看他们脸色( ∙̆ .̯ ∙̆ )。尝试了直接用ssh做反向代理,感觉效果不是很满意,不太稳定时常掉线,而且由于腾讯云的坑爹限制,没办法转发到公网80端口上。后来发现了一款神器ngrok,官网提供了收费的转发服务,国内也有不少热心童鞋提供了免费的ngrok服务。不过ngrok 1.x 的版本是开源的,干脆自力更生,搭建自己的ngrok服务。

准备

首先最重要的当然是得有一台公网主机,我的是腾讯云的vps;然后内网主机需要保证可以访问外网(废话),然后要有个域名,绑定到公网ip上。最好做下泛解析,也就是添加一条形如*.yourdomain.xxx的A记录。 公网主机有些环境要配置好,下面这几个包要安装上,为了后面能够顺利的编译ngrok源码(以ubuntu为例):

sudo add-apt-repository ppa:ubuntu-lxc/lxd-stable
sudo apt-get update
sudo apt-get install golang
sudo apt-get install build-essential mercurial git

ngrok使用go写的,所以上面安装了golang环境。有时因为友好的GFW,可能会获取不到源,可参见这个Go 安装

安装ngrok

获取源码

git clone https://github.com/inconshreveable/ngrok.git ngrok
cd ngrok
export GOPATH=~/ngrok # 也就是刚刚获取的源码位置

最后一句是go环境的配置,跳过不执行应该也可以,没试过。

生成自签名证书

注意第二步和第五步中要替换你自己的域名。

openssl genrsa -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -subj "/CN=upwzr.com" -days 5000 -out rootCA.pem
openssl genrsa -out device.key 2048
openssl req -new -key device.key -subj "/CN=upwzr.com" -out device.csr
openssl x509 -req -in device.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out device.crt -days 5000

执行完成后在当前目录下生成6个文件device.crt``device.csr``device.key``rootCA.key``rootCA.pem``rootCA.srl

ngrok通过bindata将ngrok源码目录下的assets目录(资源文件)打包到可执行文件(ngrokd和ngrok)中 去,assets/client/tls和assets/server/tls下分别存放着用于ngrok和ngrokd的默认证书文件,我们需要将它们替换成我们自己生成的:(因此这一步务必放在编译可执行文件之前)

cp rootCA.pem assets/client/tls/ngrokroot.crt
cp device.crt assets/server/tls/snakeoil.crt
cp device.key assets/server/tls/snakeoil.key

编译

编译服务端ngrokd和客户端ngrok:

make release-server release-client

编译完成之后将会在bin目录下生成ngrokd和ngrok。

启动

启动服务端:

./bin/ngrokd -domain="upwzr.com" -httpAddr=":80" -httpsAddr=":8081"

-httAddr和-httpsAddr参数指定http和ttps服务转发到公网主机的端口。ngrokd 默认还会开一个 4443 端口用来跟客户端通讯(可通过 -tunnelAddr=":xxx" 指定),我的内网主机并没有开放4443,需要指定443端口进行通讯。

正常情况下我们都是需要在后台跑服务端的,不然关闭ssh终端之后转发服务也停了。ngrokd貌似不能通过& 后台运行,使用screen代替。

sudo apt-get install screen
screen -S keepngrok
./bin/ngrokd -domain="upwzr.com" -httpAddr=":80" -httpsAddr=":8081"
#用ctrl+A+D 快捷键置入后台

启动客户端

将刚才编译的ngrok客户端下载到内网主机,可在内网主机上用scp命令完成:

# 下载到当前目录
scp -r [email protected]:/home/ubuntu/goproj/ngrok/bin/ngrok .

创建一个配置文件ngrok.cfg,这个可以随便起个名。写入如下内容(替换正确的域名):

server_addr: "upwzr.com:443"
trust_host_root_certs: false

然后可以跑了,

./ngrok -subdomain sub -config=ngrok.cfg 8001

-subdomain指定了子域名,-config指定配置文件,8001为当前主机要转发的端口。

接下来终端会显示connecting,这个要等一会儿,大概几分钟的样子(不知道是不是只有我这样,一直等到怀疑人生Σ(  ̄д ̄;))。成功之后终端大概显示这样的信息:

Version 1.7/1.7
Forwarding http://sub.upwzr.com:80 -> 127.0.0.1:8001
Forwarding https://sub.upwzr.com:80 -> 127.0.0.1:8001
Web Interface 127.0.0.1:4040
# Conn 0
Avg Conn Time 0.00ms

客户端通常也需要后台运行,这个就可以使用&来实现了,不过注意要指定-log参数,这个参数在服务端的ngrok是没有定义的。

./ngrok -subdomain sub -config=ngrok.cfg -log=stdout 8001 > /dev/null &

这时候就算是大功告成了,访问http://sub.upwzr.com就能访问到内网主机8001端口上跑的服务了。撒花~~~

多端口转发

如果有多个端口要转发,不需要分别开几个ngrok进程,只需更改ngrok.cfg配置文件,添加tunnels参数。

注意:配置文件中只能使用空格进行缩进,切记不要出现tab等字符。_ 否则ngrok将无法启动。

server_addr: upwzr.com:4443
trust_host_root_certs: false
tunnels:
    site1:
        proto:
            http: 8001
        subdomain: sub1
    site2:
        proto:
            http: 8002
        subdomain: sub2
    ssh:
        remote_port: 5000
        proto:
            tcp: 22

相应的,启动客户端的方式变为:

./ngrok -config=ngrok.cfg start site1 site2

相关链接: How to run your own ngrokd server 搭建 ngrok 服务实现内网穿透 搭建自己的ngrok服务 ngrok running in background The ngrok configuration file

comments powered by Disqus