用ngrok搭建属于自己的内网穿透教程(附错误处理)

本文最后更新于:2022年7月21日 下午

用ngrok搭建属于自己的内网穿透教程(附错误处理)

ngrok示意图

ngrok是一个开源的反向代理,它可以创建一个隧道(tunnel),让互联网上的用户访问你的本地资源。

可以用来Minecraft等游戏的局域网联机本地网站通过外网访问等。若用在另外一台服务器,还可以作为防火墙。(假如你要用来给游戏局域网联机,你可以把ngrok看作它打通了你和别人的局域网)

ngrok用go语言编写,需要go1.1+Mercurial SCM

ngrok分为两部分,ngrokngrokd,ngrokd是服务端,也就是代理服务器,用来接收外网的信息,然后通过隧道传到客户端的网络中;ngrok是客户端,哪台主机的局域网要暴露,就在哪台主机上运行。

GitHub:inconshreveable/ngrok: Introspected tunnels to localhost (github.com)

正向代理与反向代理的区别:

正向代理:让服务器不知道他服务的对象是谁(用户请了代理),常见的应用就是VPN,假如大家想在大学校外访问校园内网,通常要使用学校提供的VPN,让校内的服务器给代理服务,然后代理再把收到的信息转给你。

反向代理:让用户不知道谁在为他服务(服务器请了代理)。以前给10086打电话,它的客服有很多,你打进来的电话会随机转接到某个客服上,这个转接过程就是反向代理,用户知道的就是10086这个“代理”,而实际给你服务的客服是不知道的。

准备

  • 一台云主机(Linux系统为例 测试用ubuntu 18系统)
  • 一个域名(可以不用备案)(可选)

环境配置

域名解析(可选)

打开域名管理页面,添加*.ngrok.example.com和ngrok.example.com两条记录

到时候你将通过 xxx.ngrok.example.com:端口 来访问代理服务器

安装git

1
2
3
4
5
apt-get update  # 更新软件列表
apt-get upgrade # 更新软件
apt install git # 获取git

git --version # 检查已安装git版本

安装、配置go语言环境

打开go语言中文网找到最新的go版本(这里以1.14为例),然后用wget下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
wget https://studygolang.com/dl/golang/go1.14.linux-amd64.tar.gz
# 默认下载到当前目录,使用 -P path 参数可以指定下载路径
tar -zxvf go1.14.linux-amd64.tar.gz
# 默认解压到当前目录
mv go /usr/local
# 将go挪到另一个目录,方便管理

cd ~
# 到主目录
vi .bashrc
# 打开配置文件,在末尾添加如下
export GOROOT=/usr/local/go
# 这个目录是go的解压目录
export GOPATH=/home/gosrc/ngrok
# 这个目录是go的工作目录,即等下要编译ngrok的目录
export PATH=$GOROOT/bin:$PATH:$GOPATH/bin
# 设置bin目录
:wq
# 保存并退出
source .bashrc
# 生效

go version
# 查看go是否成功安装

安装ngrok

下载ngrok

1
2
3
4
cd /home/gosrc/ngrok
# 进入你自己准备用来编译的ngrok工作区
git clone https://github.com/inconshreveable/ngrok.git
# 把库clone下来

生成签名证书

这一步很重要,也很容易错,我们要生成自己的SSL证书。

由于我计划最终提供服务的地址是xxx.ngrok.example.com所以我把NGROK_DOMAIN设置如下

注意把域名设置成你自己的域名

1
2
3
4
5
6
7
8
9
10
11
NGROK_DOMAIN="ngrok.example.com"

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 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 rootCA.key -CAcreateserial -out server.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

最后三个cp命令是将生成的证书覆盖原来ngrok的证书

编译ngrok

最容易出问题的一步

编译Linux服务端(本机)

1
2
3
4
GOOS=linux GOARCH=386 make release-server
# 32位
GOOS=linux GOARCH=amd64 make release-server
# 64位

编译windows客户端

1
2
GOOS=windows GOARCH=amd64 make release-client
# 64位,没人用32了吧。。。

编译Linux客户端

1
2
3
4
GOOS=linux GOARCH=386 make release-client
# 32位
GOOS=linux GOARCH=amd64 make release-client
# 64位

go-bindata的错误解决

go get github.com/go-bindata/go-bindata/

按道理会出现在GOPATH,然后把go-bindata复制到ngrok/bin下面

其他错误解决

检查你的云主机是否能正常访问网络

检查GOPATH的设置,看看是否正确

echo $GOPATH

假如一切正常,那么在bin目录下会出现ngrokd(Linux客户端),还可能有存有exe文件的目录

启动服务端

在ngrok目录运行

1
./bin/ngrokd -tlsKey=server.key -tlsCrt=server.crt -domain="ngrok.example.com" -httpAddr=":8090"

前两个参数是指定证书;第三个参数是域名;第四个参数是用来转发http的端口,可以随便写;还可以写httpsAddr用来指定转发https的端口

启动客户端

用ftp或者其他方法,将客户端的ngrok或ngrok.exe放到要被访问的机器上

  • 新建一个ngrok.cfg,打开写入如下内容
1
2
3
4
5
6
7
8
9
10
11
server_addr: "ngrok.example.com:4443"
trust_host_root_certs: false
tunnels:
a:
remote_port: 12345
proto:
tcp: "127.0.0.1:25565"
tcp: "127.0.0.1:25566"
b:
proto:
http: "127.0.0.1:80"

此文件位YAML格式,缩进用空格

server_addr后填写你的域名,要和之前写的一模一样。

4443是固定端口,一般不改,但也可以在服务端更改。

tunnels允许配置文件配置多个隧道,可以同时启动多个隧道

a和b是隧道名字。

a中remote_port是远程端口,即访问 xxx.ngrok.example.com:端口 时要输入的端口,假如是http协议则此项无效

proto是隧道协议,在之下可以同时用不同协议暴露不同局域网ip地址和端口

  • 新建一个 启动.bat ,打开写入如下内容
1
ngrok -subdomain test -config=ngrok.cfg start a

-subdomain是确定你要用什么三级域名来访问,就是上面xxx的内容。

-config确定配置文件。

start启动隧道,可以start a b c d

ngrok help可以查看帮助

  • 双击bat文件运行

假如出现绿色的online就开启成功。

闪退错误解决

  • bat和cfg是否写错
  • 云主机响应端口是否开放(阿里云服务器要在安全组中设置端口)
  • 观察服务端的ngrokd输出,假如出现bad certificate,那么就是证书错误,检查你的客户端版本以及服务端用的证书是否统一

Linux后台运行服务端

由于断开ssh连接后就导致穿透关闭,所以要用screen来后台运行

安装screen

1
sudo apt install screen

screen命令

1
2
3
4
5
6
7
8
9
10
11
12
13
screen -S name
# 新建一个名字叫name的screen
screen
# 新建一个没有名字的screen
screen -ls
# 查看当前有多少个screen以及信息
# 结果有Dead:死了 Detached:独立的screen Attched:
screen -r name|id
# 通过name或者id来恢复screen
ctrl+a d
# 先按ctrl+a 再按d 分离screen,退出到主窗口
ctrl+a k
# 杀死当前screen

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!