平常我们搭建一个网站通常是在一个远程服务器上,域名解析,在服务器上搭建网站,然后就可以访问了,过程很简单。但是如果我们在自己电脑上搭建一个网站并且想让公网可以访问的话,由于我们的电脑没有固定的公网ip地址,就要多进行一些操作了。 这篇文章记录一下如何使用ngrok进行内网穿透,以在公网中访问我们在内网电脑上的资源。

Ngrok 就是一种实现内网穿透的开源软件。Ngrok 借用在公网的服务端和局域网内部的服务端构建了一个代理通道。当有服务请求时,会先将请求达到公网服务端,再由公网服务端转发给内网服务端,以达到内网暴露公网的目的。

流程

你需要准备一台有公网IP的服务器,在服务器上运行ngrok服务端,在内网电脑上运行ngrok客户端。当有请求访问内网电脑的资源时,由于解析的设置,此请求会被送到ngrok服务端,公网服务端会与内网的客户端进行连接以将内网的资源暴露给公网。

准备

使用ngrok进行内网穿透你需要准备:

  • 一个域名(本文假设为ngrok.zkk.me)
  • 一个有公网ip的服务器(本文为CentOS 7.6系统)
  • 一个内网电脑(本文为windows 10系统)

域名解析

你需要将域名ngrok.zkk.me添加A类解析至服务器,由于内网电脑上不同网站是用域名ngrok.zkk.me的不同子域名来区分的,所以还需要将泛域名*.ngrok.zkk.me添加A类解析至服务器。解析情况如下表

Type Host Value
A Record ngrok XXX.XXX.XXX.XXX
A Record *.ngrok XXX.XXX.XXX.XXX

申请SSL证书

由于有些同学会需要用到以https的形式访问内网网站,所以我们需要申请SSL证书。这里以在https://freessl.cn 申请证书为例

  1. 首先在https://freessl.cn 注册账户
  2. 注册并登陆后来到首页,在申请证书的框内填写泛域名*.ngrok.zkk.me,品牌选择Let's Encrypt V2,然后点击“创建免费的SSL证书” 申请证书
  3. 然后来到创建SSL证书的设置页面,上面输入你的邮箱,在这里你可以进行一些设置。这里CSR生成我们选择“浏览器生成”,然后点击创建,点击创建后浏览器会下载一个zip压缩包,里面是证书的私钥private.key创建SSL证书
  4. 然后来到域名验证页面。只要按照要求对域名进行了TXT解析,然后两个都检测通过的话基本上就可以验证成功。 域名验证
  5. 验证成功会得到证书内容如下 下载证书文件 点击下载文件会下载一个zip压缩包,里面是申请到的SSL证书和私钥,这个私钥与刚才下载的那个文件相同。保存好这两个文件,后面需要用到 SSL证书

编译ngrok服务端

我是在CentOS 7.6系统上安装的ngrok服务端。 ngrok服务端需要Golang语言环境,先安装必要工具

yum -y install build-essential golang mercurial git
  • 下载ngrok源码 找一个你喜欢的地方进行ngrok服务端的安装,我用的是/home 进入到准备安装ngrok的目录,下载ngrok源码包
    cd /home
    git clone https://github.com/inconshreveable/ngrok.git
    

    下载完源码包后需要用我们刚刚申请的证书和密钥来替换自带的证书和私钥

  • 删除证书 删除自带的证书和密钥
    cd ngrok
    rm -f assets/server/tls/snakeoil.crt
    rm -f assets/server/tls/snakeoil.key
    rm -f assets/client/tls/ngrokroot.crt
    
  • 上传证书 上传我们申请的证书和密钥,将full_chain.pemprivate.key上传到ngrok/assets/server/tls/目录下,并重命名为snakeoil.crtsnakeoil.key,如图 server证书替换full_chain.pem上传到ngrok/assets/client/tls/目录下,并重命名为ngrokroot.crt,如图 ngrokroot.crt上传
  • 编译服务端 来到ngrok根目录下,执行编译服务端指令
    cd /home/ngrok
    make release-server
    

    编译结果如下表示编译成功(此步骤等待时间可能比较长)

    GOOS="" GOARCH="" go get github.com/jteeuwen/go-bindata/go-bindata
    bin/go-bindata -nomemcopy -pkg=assets -tags=release \
          -debug=false \
          -o=src/ngrok/client/assets/assets_release.go \
          assets/client/...
    bin/go-bindata -nomemcopy -pkg=assets -tags=release \
          -debug=false \
          -o=src/ngrok/server/assets/assets_release.go \
          assets/server/...
    go get -tags 'release' -d -v ngrok/...
    github.com/inconshreveable/mousetrap (download)
    github.com/rcrowley/go-metrics (download)
    get "gopkg.in/inconshreveable/go-update.v0": found meta tag get.metaImport{Prefix:"gopkg.in/inconshreveable/go-update.v0", VCS:"git", RepoRoot:"https://gopkg.in/inconshreveable/go-update.v0"} at //gopkg.in/inconshreveable/go-update.v0?go-get=1
    gopkg.in/inconshreveable/go-update.v0 (download)
    github.com/kardianos/osext (download)
    github.com/kr/binarydist (download)
    get "gopkg.in/inconshreveable/go-update.v0/check": found meta tag get.metaImport{Prefix:"gopkg.in/inconshreveable/go-update.v0", VCS:"git", RepoRoot:"https://gopkg.in/inconshreveable/go-update.v0"} at //gopkg.in/inconshreveable/go-update.v0/check?go-get=1
    get "gopkg.in/inconshreveable/go-update.v0/check": verifying non-authoritative meta tag
    get "gopkg.in/yaml.v1": found meta tag get.metaImport{Prefix:"gopkg.in/yaml.v1", VCS:"git", RepoRoot:"https://gopkg.in/yaml.v1"} at //gopkg.in/yaml.v1?go-get=1
    gopkg.in/yaml.v1 (download)
    github.com/inconshreveable/go-vhost (download)
    github.com/alecthomas/log4go (download)
    github.com/nsf/termbox-go (download)
    github.com/mattn/go-runewidth (download)
    github.com/gorilla/websocket (download)
    go install -tags 'release' ngrok/main/ngrokd
    

    编译出的服务端程序的路径为bin/ngrokd,我们先尝试运行一下,这里我们尝试使用8888端口来进行http访问,9999端口进行https访问,请确保你所要使用的端口没有被占用

    ./bin/ngrokd -domain="ngrok.zkk.me" -httpAddr=":8888" -httpsAddr=":9999"
    

    输出内容如下表示运行成功

    [00:29:01 CST 2020/04/18] [INFO] (ngrok/log.(*PrefixLogger).Info:83) [registry] [tun] No affinity cache specified
    [00:29:01 CST 2020/04/18] [INFO] (ngrok/log.Info:112) Listening for public http connections on [::]:8888
    [00:29:01 CST 2020/04/18] [INFO] (ngrok/log.Info:112) Listening for public https connections on [::]:9999
    [00:29:01 CST 2020/04/18] [INFO] (ngrok/log.Info:112) Listening for control and proxy connections on [::]:4443
    [00:29:01 CST 2020/04/18] [INFO] (ngrok/log.(*PrefixLogger).Info:83) [metrics] Reporting every 30 seconds
    

编译并配置ngrok客户端

编译

由于ngrok客户端可能运行于不同平台,所以我们需要针对不同平台执行不同的编译指令。

# 32位linux客户端
GOOS=linux GOARCH=386 make release-client

# 64位linux客户端
GOOS=linux GOARCH=amd64 make release-client

# 32位windows客户端
GOOS=windows GOARCH=386 make release-client

# 64位windows客户端
GOOS=windows GOARCH=amd64 make release-client

# 32位mac平台客户端
GOOS=darwin GOARCH=386 make release-client

# 64位mac平台客户端
GOOS=darwin GOARCH=amd64 make release-client

# ARM平台linux客户端
GOOS=linux GOARCH=arm make release-client

我使用的是64位windows,所以使用如下指令编译客户端

GOOS=windows GOARCH=amd64 make release-client

编译输出内容如下

bin/go-bindata -nomemcopy -pkg=assets -tags=release \
        -debug=false \
        -o=src/ngrok/client/assets/assets_release.go \
        assets/client/...
bin/go-bindata -nomemcopy -pkg=assets -tags=release \
        -debug=false \
        -o=src/ngrok/server/assets/assets_release.go \
        assets/server/...
go get -tags 'release' -d -v ngrok/...
go install -tags 'release' ngrok/main/ngrok

编译成功之后,在bin/windows_amd64目录下会有一个ngrok.exe文件,这就是运行在内网电脑上的客户端

配置

把编译好的ngrok.exe下载到内网的电脑上,放到一个你喜欢的位置 在与ngrok.exe同级目录下新建一个文件,名为ngrok.cfg,并写入以下内容

server_addr: "ngrok.zkk.me:4443"
trust_host_root_certs: false

tunnels:
  http:
    proto:
      http: 80
    subdomain: "test"

  https:
    proto:
      https: 80
    subdomain: "test"

解释一下,这段配置的意思是说在本地新建两个隧道,子域名为test.ngrok.zkk.me,当通过http形式访问test.ngrok.zkk.me时,会转到本地80端口,当通过https形式访问时,也转到本地80端口。这里说一下,本地网站Nginx不要配置ssl使用的端口,也就是不区分http和https,只配置http访问的一个端口就可以。如果你本地使用ssl配置的端口的话,浏览器以https形式访问时会出现400错误,错误内容是”The plain HTTP request was sent to HTTPS port”。因为无论是http还是https,本地ngrok客户端都会以http形式转发。

运行并测试

首先内网电脑上应该先建立一个网站,关于如何在windows系统上使用Nginx和PHP建立网站,请看这一篇笔记:Windows配置Nginx + PHP

  • 运行服务端
    ./bin/ngrokd -domain="ngrok.zkk.me" -httpAddr=":8888" -httpsAddr=":9999"
    
  • 运行客户端 在CMD中切换到ngrok.exe所在目录,执行以下命令
    ngrok -config=ngrok.cfg start-all
    

    执行后如果看到如下结果说明非常成功 部署成功 上面输出的内容意思是:当在浏览器访问http://test.ngrok.zkk.me:8888 时会以http形式转到本地80端口,当在浏览器访问https://test.ngrok.zkk.me:9999 时也会以http形式转到本地80端口。这也是之前我为什么说如果你在ngrok.cfg中的本地使用的端口配置了ssl就会出现400错误。 接着浏览器使用上述地址访问一下,进行验证 验证 成功访问,大功告成!

如果你感觉带着端口访问很不舒服,那你可以看一下这篇文章 Nginx反代理ngrok,实现80和443端口访问

REFERENCE