老叔教你亲手搭建一个Ngrok内网穿透服务器

最近公司需要开发公众号,在调试的时候就是个老大难问题,一般是需要内网穿透方式来让外网能够访问到本地的程序,提高我们的开发调试效率,一般大家会才用natapp、向日葵、ngrok等,这些都做得不错,但是你要用得好、用得舒心,还是得花钱,刚好我之前花了三百多块钱买了3年的云主机,所以就想着要不自己折腾一个ngrok吧,所以就有这个这篇文章

认识ngrok

这次我们需要用到ngrok这个东东,那么首先需要基本了解一下它是个啥?能干啥?ngrok是一款用Golang语言打造的开源的内网穿透软件,它可以帮助我们把内网的应用提供给外网用户访问。ngrok的开源地址是https://github.com/inconshreveable/ngrok

搭建ngrok服务器

在搭建之前,我们需要准备两个东西:

  • 外网服务器
  • 域名
    如果这两个没有的话,那就不好搞咯。这里我们先在服务器上安装好Golang的编译环境,由于我这边被墙了,所以就只有访问https://studygolang.com/dl来下载了。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    [root@VM_175_142_centos golang]# wget https://studygolang.com/dl/golang/go1.14.2.linux-amd64.tar.gz
    --2020-04-23 12:57:26-- https://studygolang.com/dl/golang/go1.14.2.linux-amd64.tar.gz
    Resolving studygolang.com (studygolang.com)... 59.110.219.94
    Connecting to studygolang.com (studygolang.com)|59.110.219.94|:443... connected.
    HTTP request sent, awaiting response... 303 See Other
    Location: https://dl.google.com/go/go1.14.2.linux-amd64.tar.gz [following]
    --2020-04-23 12:57:27-- https://dl.google.com/go/go1.14.2.linux-amd64.tar.gz
    Resolving dl.google.com (dl.google.com)... 203.208.41.33, 203.208.41.41, 203.208.41.38, ...
    Connecting to dl.google.com (dl.google.com)|203.208.41.33|:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 123658438 (118M) [application/octet-stream]
    Saving to: ‘go1.14.2.linux-amd64.tar.gz’

    100%[====================================================================================================================================================================>] 123,658,438 9.72MB/s in 13s

    2020-04-23 12:57:41 (9.04 MB/s) - ‘go1.14.2.linux-amd64.tar.gz’ saved [123658438/123658438]
    go1.14.2.linux-amd64.tar.gz文件解压后会得到一个go文件,那么现在我们需要修改环境变量/etc/profile或者$HOME/.profile:
    1
    2
    export GOROOT=你的解压目录/go
    export PATH=$PATH:$GOROOT/bin
    source /etc/profile让配置文件即刻生效,这时候就可以看到我们的go命令是可执行的
    1
    2
    [root@VM_175_142_centos go]# go version
    go version go1.14.2 linux/amd64
    现在,我们继续去github上下载ngrok源码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    [root@VM_175_142_centos ngrok]# wget https://github.com/inconshreveable/ngrok/archive/master.zip
    --2020-04-23 13:09:37-- https://github.com/inconshreveable/ngrok/archive/master.zip
    Resolving github.com (github.com)... 13.250.177.223
    Connecting to github.com (github.com)|13.250.177.223|:443... connected.
    HTTP request sent, awaiting response... 302 Found
    Location: https://codeload.github.com/inconshreveable/ngrok/zip/master [following]
    --2020-04-23 13:09:38-- https://codeload.github.com/inconshreveable/ngrok/zip/master
    Resolving codeload.github.com (codeload.github.com)... 54.251.140.56
    Connecting to codeload.github.com (codeload.github.com)|54.251.140.56|:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 200090 (195K) [application/zip]
    Saving to: ‘master.zip’

    100%[====================================================================================================================================================================>] 200,090 673KB/s in 0.3s

    2020-04-23 13:09:39 (673 KB/s) - ‘master.zip’ saved [200090/200090]
    master.zip解压出来
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    [root@VM_175_142_centos ngrok]# unzip master.zip
    Archive: master.zip
    a8e7fa486348f391f4bdf241344d798297f953bc
    creating: ngrok-master/
    inflating: ngrok-master/.gitignore
    inflating: ngrok-master/.travis.yml
    inflating: ngrok-master/CONTRIBUTORS
    inflating: ngrok-master/LICENSE
    inflating: ngrok-master/Makefile
    inflating: ngrok-master/README.md
    creating: ngrok-master/assets/
    creating: ngrok-master/assets/client/
    inflating: ngrok-master/assets/client/page.html
    creating: ngrok-master/assets/client/static/
    creating: ngrok-master/assets/client/static/css/
    inflating: ngrok-master/assets/client/static/css/bootstrap.min.css
    inflating: ngrok-master/assets/client/static/css/highlight.min.css
    creating: ngrok-master/assets/client/static/img/
    inflating: ngrok-master/assets/client/static/img/glyphicons-halflings.png
    creating: ngrok-master/assets/client/static/js/
    inflating: ngrok-master/assets/client/static/js/angular-sanitize.min.js
    inflating: ngrok-master/assets/client/static/js/angular.js
    inflating: ngrok-master/assets/client/static/js/base64.js
    inflating: ngrok-master/assets/client/static/js/highlight.min.js
    inflating: ngrok-master/assets/client/static/js/jquery-1.9.1.min.js
    inflating: ngrok-master/assets/client/static/js/jquery.timeago.js
    inflating: ngrok-master/assets/client/static/js/ngrok.js
    inflating: ngrok-master/assets/client/static/js/vkbeautify.js
    creating: ngrok-master/assets/client/tls/
    inflating: ngrok-master/assets/client/tls/ngrokroot.crt
    inflating: ngrok-master/assets/client/tls/snakeoilca.crt
    creating: ngrok-master/assets/server/
    creating: ngrok-master/assets/server/tls/
    inflating: ngrok-master/assets/server/tls/snakeoil.crt
    inflating: ngrok-master/assets/server/tls/snakeoil.key
    creating: ngrok-master/contrib/
    inflating: ngrok-master/contrib/com.ngrok.client.plist
    creating: ngrok-master/docs/
    inflating: ngrok-master/docs/CHANGELOG.md
    inflating: ngrok-master/docs/DEVELOPMENT.md
    inflating: ngrok-master/docs/SELFHOSTING.md
    creating: ngrok-master/src/
    creating: ngrok-master/src/ngrok/
    creating: ngrok-master/src/ngrok/cache/
    inflating: ngrok-master/src/ngrok/cache/lru.go
    creating: ngrok-master/src/ngrok/client/
    inflating: ngrok-master/src/ngrok/client/cli.go
    inflating: ngrok-master/src/ngrok/client/config.go
    inflating: ngrok-master/src/ngrok/client/controller.go
    inflating: ngrok-master/src/ngrok/client/debug.go
    inflating: ngrok-master/src/ngrok/client/main.go
    inflating: ngrok-master/src/ngrok/client/metrics.go
    inflating: ngrok-master/src/ngrok/client/model.go
    creating: ngrok-master/src/ngrok/client/mvc/
    inflating: ngrok-master/src/ngrok/client/mvc/controller.go
    inflating: ngrok-master/src/ngrok/client/mvc/model.go
    inflating: ngrok-master/src/ngrok/client/mvc/state.go
    extracting: ngrok-master/src/ngrok/client/mvc/view.go
    inflating: ngrok-master/src/ngrok/client/release.go
    inflating: ngrok-master/src/ngrok/client/tls.go
    inflating: ngrok-master/src/ngrok/client/update_debug.go
    inflating: ngrok-master/src/ngrok/client/update_release.go
    creating: ngrok-master/src/ngrok/client/views/
    creating: ngrok-master/src/ngrok/client/views/term/
    inflating: ngrok-master/src/ngrok/client/views/term/area.go
    inflating: ngrok-master/src/ngrok/client/views/term/http.go
    inflating: ngrok-master/src/ngrok/client/views/term/view.go
    creating: ngrok-master/src/ngrok/client/views/web/
    inflating: ngrok-master/src/ngrok/client/views/web/http.go
    inflating: ngrok-master/src/ngrok/client/views/web/view.go
    creating: ngrok-master/src/ngrok/conn/
    inflating: ngrok-master/src/ngrok/conn/conn.go
    inflating: ngrok-master/src/ngrok/conn/tee.go
    creating: ngrok-master/src/ngrok/log/
    inflating: ngrok-master/src/ngrok/log/logger.go
    creating: ngrok-master/src/ngrok/main/
    creating: ngrok-master/src/ngrok/main/ngrok/
    inflating: ngrok-master/src/ngrok/main/ngrok/ngrok.go
    creating: ngrok-master/src/ngrok/main/ngrokd/
    inflating: ngrok-master/src/ngrok/main/ngrokd/ngrokd.go
    creating: ngrok-master/src/ngrok/msg/
    inflating: ngrok-master/src/ngrok/msg/conn.go
    inflating: ngrok-master/src/ngrok/msg/msg.go
    inflating: ngrok-master/src/ngrok/msg/pack.go
    creating: ngrok-master/src/ngrok/proto/
    inflating: ngrok-master/src/ngrok/proto/http.go
    inflating: ngrok-master/src/ngrok/proto/interface.go
    inflating: ngrok-master/src/ngrok/proto/tcp.go
    creating: ngrok-master/src/ngrok/server/
    inflating: ngrok-master/src/ngrok/server/cli.go
    inflating: ngrok-master/src/ngrok/server/control.go
    inflating: ngrok-master/src/ngrok/server/http.go
    inflating: ngrok-master/src/ngrok/server/main.go
    inflating: ngrok-master/src/ngrok/server/metrics.go
    inflating: ngrok-master/src/ngrok/server/registry.go
    inflating: ngrok-master/src/ngrok/server/tls.go
    inflating: ngrok-master/src/ngrok/server/tunnel.go
    creating: ngrok-master/src/ngrok/util/
    inflating: ngrok-master/src/ngrok/util/broadcast.go
    inflating: ngrok-master/src/ngrok/util/errors.go
    inflating: ngrok-master/src/ngrok/util/id.go
    inflating: ngrok-master/src/ngrok/util/ring.go
    inflating: ngrok-master/src/ngrok/util/shutdown.go
    creating: ngrok-master/src/ngrok/version/
    inflating: ngrok-master/src/ngrok/version/version.go
    进入到ngrok源码解压目录,并创建一个ssl目录,用于存放自定义证书
    1
    2
    3
    4
    5
    6
    7
    mkdir ssl&& cd ssl
    export NGROK_DOMAIN="ngrok.52fx.biz"
    openssl genrsa -out rootCA.key 2048
    openssl req -x509 -new -nodes -key rootCA.key -subj "/CN=$NGROK_DOMAIN" -days 8888 -out rootCA.pem
    openssl genrsa -out device.key 2048
    openssl req -new -key device.key -subj "/CN=$NGROK_DOMAIN" -out device.csr
    openssl x509 -req -in device.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out device.crt -days 8888
    执行完上面的命令后,会得到这些文件:
    1
    2
    3
    4
    5
    6
    7
    8
    [root@VM_175_142_centos ssl]# tree
    .
    |-- device.crt
    |-- device.csr
    |-- device.key
    |-- rootCA.key
    |-- rootCA.pem
    `-- rootCA.srl
    我们现在把这个文件复制到ngrok对应的目录并覆盖原文件
    1
    2
    3
    4
    5
    6
    [root@VM_175_142_centos ssl]# cp rootCA.pem ../assets/client/tls/ngrokroot.crt
    cp: overwrite ‘../assets/client/tls/ngrokroot.crt’? y
    [root@VM_175_142_centos ssl]# cp device.crt ../assets/server/tls/snakeoil.crt
    cp: overwrite ‘../assets/server/tls/snakeoil.crt’? y
    [root@VM_175_142_centos ssl]# cp device.key ../assets/server/tls/snakeoil.key
    cp: overwrite ‘../assets/server/tls/snakeoil.key’? y
    现在我们就来开始编译ngrok
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <!--linux服务端/客户端-->
    GOOS=linux GOARCH=386 make release-server (32位)
    GOOS=linux GOARCH=amd64 make release-server(64位)

    GOOS=linux GOARCH=386 make release-client (32位)
    GOOS=linux GOARCH=amd64 make release-client(64位)

    <!--Mac OS服务端/客户端-->
    GOOS=darwin GOARCH=386 make release-server
    GOOS=darwin GOARCH=amd64 make release-server

    GOOS=darwin GOARCH=386 make release-client
    GOOS=darwin GOARCH=amd64 make release-client


    <!--windows服务端/客户端-->
    GOOS=windows GOARCH=386 make release-server
    GOOS=windows GOARCH=amd64 make release-server

    GOOS=windows GOARCH=386 make release-client
    GOOS=windows GOARCH=amd64 make release-client
    ,我们先编译一个linux运行的服务端
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    [root@VM_175_142_centos ngrok-master]# export GOOS=linux
    [root@VM_175_142_centos ngrok-master]# export GOARCH=amd64
    [root@VM_175_142_centos ngrok-master]# make release-server
    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/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
    第一次执行命令时会下载包,所以会比较花时间,可以通过在环境变量中加入GOPROXY值为https://mirrors.aliyun.com/goproxy/的方式来添加代理。我继续编译一个windows 64的客户端
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    [root@VM_175_142_centos ngrok-master]# export GOOS=windows
    [root@VM_175_142_centos ngrok-master]# export GOARCH=amd64
    [root@VM_175_142_centos ngrok-master]# 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目录下看到这些文件
    1
    2
    3
    4
    5
    6
    [root@VM_175_142_centos bin]# tree
    .
    |-- go-bindata
    |-- ngrokd
    `-- windows_amd64
    `-- ngrok.exe
    先把服务端运行起来,默认情况下ngrok是占用的80443端口,我们可以自行配置
    1
    2
    3
    4
    5
    6
    [root@VM_175_142_centos bin]# ./ngrokd -domain="ngrok.52fx.biz" -httpAddr=":8888" -httpsAddr=":8433"
    [14:08:59 CST 2020/04/23] [INFO] (ngrok/log.(*PrefixLogger).Info:83) [registry] [tun] No affinity cache specified
    [14:08:59 CST 2020/04/23] [INFO] (ngrok/log.Info:112) Listening for public http connections on [::]:8888
    [14:08:59 CST 2020/04/23] [INFO] (ngrok/log.Info:112) Listening for public https connections on [::]:8433
    [14:08:59 CST 2020/04/23] [INFO] (ngrok/log.Info:112) Listening for control and proxy connections on [::]:4443
    [14:08:59 CST 2020/04/23] [INFO] (ngrok/log.(*PrefixLogger).Info:83) [metrics] Reporting every 30 seconds
    现在,我们新建一个客户端配置文件ngrok.cfg
    1
    2
    3
    <!--配置服务端连接地址,也就是基础域名。端口则与服务端-tunnelAddr配置相同-->
    server_addr: "ngrok.52fx.biz:4443"
    trust_host_root_certs: false
    现在就可以在客户端运行代理了,ngrok -config=ngrok.cfg -subdomain 域名 端口,如ngrok -config=ngrok.cfg -subdomain weixin 8080执行完成之后会看到这样的界面

这里我们还需要配置域名,切记,需要配置两条

  • ngrok.52fx.biz-> xxx.xx.xxx.xxx(外网IP)
  • *.ngrok.52fx.biz-> xxx.xx.xxx.xxx(外网IP) 该记录为泛解析,就是这样的域名都会解析到指定的ip然后再转发
    现在我们来访问一下看是否正常

    ok,就是这么回事。但是呢,我们这里的端口是8888,每次访问还得在域名后面加端口,真的是太不优雅了,所以得想办法处理。因为我之前的80端口被nginx给占用了,所以,我们这里再来转发一次吧。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    server {
    listen 80;
    server_name *.ngrok.52fx.biz;

    #charset koi8-r;

    #access_log logs/host.access.log main;

    location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Nginx-Proxy true;
    proxy_set_header Connection "";
    proxy_pass http://weixin.ngrok.52fx.biz:8888; #这里的weixin可以改为任意名称,因为是泛解析
    }

    location ~ .*\.(js|css)$ {
    proxy_pass http://weixin.ngrok.52fx.biz:8888; #这里的weixin可以改为任意名称,因为是泛解析

    }


    }

    这样看起来是不是更友好一些了呢?如果你的ngrok是监听的80端口,大可不必这样麻烦。
#
You forgot to set the qrcode for Alipay. Please set it in _config.yml.
You forgot to set the qrcode for Wechat. Please set it in _config.yml.
You forgot to set the business and currency_code for Paypal. Please set it in _config.yml.
You forgot to set the url Patreon. Please set it in _config.yml.
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×