Nov 01

前面也写了一篇关于 Ngrok 搭建的文章:《树莓派上 Ngrok 的编译与使用》,但是看到《搭建 ngrok 服务实现外网访问局域网内的网站》这篇文章使用纯手工编译的步骤很详细,尤其是 go 语言的配置那部分内容,所以将文中的主要内容复制下来,以备不时之需。

事先的准备工作:添加 ngrok 服务域名的 DNS 解析。

选择支持泛解析的 DNS 服务商,如 Cloudns、DNSpod 国际版、zoneedit 等,分别添加 A 记录:

ngrok.chun.pro 记录值 1.2.3.4
*.ngrok.chun.pro 记录值 1.2.3.4

1.安装必要的工具和语言环境

sudo apt-get install build-essential golang mercurial git

2.升级 go 语言环境

# 看看是不是小于等于 1.2.1
go version
# 卸载
sudo apt-get purge golang*
#下载最新版并解压 https://golang.org/dl/
wget https://storage.googleapis.com/golang/go1.7.3.linux-386.tar.gz
tar -C /usr/local -xzf go1.7.3.linux-386.tar.gz
#创建目录
mkdir ~/.go
# 设置环境变量
vi ~/.profile
export GOROOT=/usr/local/go
export GOPATH=~/.go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
source .profile

# 升级
sudo update-alternatives --install "/usr/bin/go" "go" "/usr/local/go/bin/go" 0
sudo update-alternatives --set go /usr/local/go/bin/go
go version

3.下载 ngrok 源码并编译服务端

git clone https://github.com/tutumcloud/ngrok.git ngrok
cd ngrok

#生成并替换源码里默认的证书,注意域名要修改为你自己的,这里是一个虚拟的测试域名
NGROK_DOMAIN="ngrok.chun.pro"
openssl genrsa -out base.key 2048
openssl req -new -x509 -nodes -key base.key -days 10000 -subj "/CN=$NGROK_DOMAIN" -out base.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 base.pem -CAkey base.key -CAcreateserial -days 10000 -out server.crt

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

#开始编译,服务端客户端会基于证书来加密通讯,保证了安全性
GOOS=linux GOARCH=amd64 make release-server release-client
GOOS=linux GOARCH=386 make release-server release-client
GOOS=linux GOARCH=arm make release-server release-client
GOOS=linux GOARCH=arm64 make release-server release-client
GOOS=linux GOARCH=ppc64 make release-server release-client
GOOS=linux GOARCH=ppc64le make release-server release-client
GOOS=linux GOARCH=mips64 make release-server release-client
GOOS=linux GOARCH=mips64le make release-server release-client

GOOS=windows GOARCH=amd64 make release-server release-client
GOOS=windows GOARCH=386 make release-server release-client

GOOS=darwin GOARCH=amd64 make release-server release-client
GOOS=darwin GOARCH=386 make release-server release-client
GOOS=darwin GOARCH=arm make release-server release-client
GOOS=darwin GOARCH=arm64 make release-server release-client

GOOS=android GOARCH=arm make release-server release-client

GOOS=dragonfly GOARCH=amd64 make release-server release-client

GOOS=freebsd GOARCH=amd64 make release-server release-client
GOOS=freebsd GOARCH=386 make release-server release-client
GOOS=freebsd GOARCH=arm make release-server release-client

GOOS=netbsd GOARCH=amd64 make release-server release-client
GOOS=netbsd GOARCH=386 make release-server release-client
GOOS=netbsd GOARCH=arm make release-server release-client

GOOS=openbsd GOARCH=amd64 make release-server release-client
GOOS=openbsd GOARCH=386 make release-server release-client
GOOS=openbsd GOARCH=arm make release-server release-client

GOOS=plan9 GOARCH=amd64 make release-server release-client
GOOS=plan9 GOARCH=386 make release-server release-client

GOOS=solaris GOARCH=amd64 make release-server release-client

4.启动服务端
在服务器上运行下面的命令启动ngrok服务端

./bin/ngrokd -domain="ngrok.chun.pro" -httpAddr=":8081" -httpsAddr=":8082"

注意,这里 httpAddr 和 httpsAddr 是 ngrok 服务转发 http 和 https 请求的端口,为了避免和 Nginx/Apache 等的 80 端口冲突,使用了 8081 和 8082。

默认还会启动一个 4443 端口,用于跟活动的客户端进行通讯,如果需要更换端口,使用 -tunnelAddr=”:xxx”参数

现在你可以在浏览器里访问 http://ngrok.chun.pro:8081了,如果有一行提示,表示 ngrok 的服务端已经运行起来了

Tunnel ngrok.yourdomain.com:8081 not found

然后再访问 http://pi.ngrok.chun.pro:8081,如果有下面的提示,表示 A 记录也已经生效了。

Tunnel pi.ngrok.chun.pro:8081 not found

5.配置客户端参数

vi ngrok.cfg
# 填写如下信息,server_addr 指定了服务端的域名和与客户端通信的端口
server_addr: ngrok.chun.pro:4443
trust_host_root_certs: false

6.启动客户端

./ngrok -config=./ngrok.cfg -subdomain pi 127.0.0.1:80

如果连接正常,会有提示:

ngrok                                                        (Ctrl+C to quit)
Tunnel Status                 online
Version                       1.7/1.7
Forwarding                    http://pi.ngrok.chun.pro:8081 -> 127.0.0.1:80
Web Interface                 127.0.0.1:4040
# Conn                        5
Avg Conn Time                 192.70ms

7.nginx 反向代理 8081 端口

server {
        listen 80;
        server_name ngrok.chun.pro;
        location / {
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host:8081;
                proxy_set_header X-Nginx-Proxy true;
                proxy_set_header Connection "";
                proxy_pass http://127.0.0.1:8081;
        }
}

现在可以直接在浏览器访问 pi.ngrok.chun.pro,而不需要加 :8081 端口号。

另外,使用 Docker 搭建 Ngrok 服务器可以参考这篇文章:https://hteen.cn/docker/docker-ngrok.html

Jul 23

1. 配置 ngrok 的编译环境

sudo apt-get install git bison build-essential mercurial
bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-

installer)
echo "[[ -s "$HOME/.gvm/scripts/gvm" ]] && source "$HOME/.gvm/scripts/gvm"" >> ~/.bashrc
source ~/.bashrc
gvm install go1.4
gvm use go1.4
export GOROOT_BOOTSTRAP=$GOROOT

参考资料:https://medium.com/@unnikked/how-to-compile-ngrok-on-a-raspberry-pi-d5a78ce4b15c#.vdw95rslb

2. 编译 ngrok

git clone https://github.com/inconshreveable/ngrok.git ngrok
cd ngrok

NGROK_DOMAIN="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 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 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

编译 linux 64 bit 版本的 ngrok

GOOS=linux GOARCH=amd64 ./make.bash
GOOS=linux GOARCH=amd64 make release-client

编译 linux 32 bit 版本的 ngrok

cd ~/.gwm/gos/go1.4/src
GOOS=linux GOARCH=amd64 ./make.bash

cd ~/ngrok
GOOS=linux GOARCH=amd64 ./make.bash

GOOS=linux GOARCH=arm ./make.bash
GOOS=linux GOARCH=arm make release-client

GOOS=windows GOARCH=amd64 ./make.bash
GOOS=windows GOARCH=amd64 make release-client

GOOS=windows GOARCH=386 ./make.bash
GOOS=windows GOARCH=386 make release-client

参考资料:http://www.mamicode.com/info-detail-1230154.html
http://tonybai.com/2014/10/20/cross-compilation-with-golang/

3. 测试 ngrok 服务端和客户端

sudo cp ngrok /usr/local/ngrok/
sudo cp ngrokd /usr/local/ngrok/
sudo vim /usr/local/ngrok/ngrok.cfg

服务端

sudo ./ngrok -tlsKey=device.key -tlsCrt=device.crt -domain="$NGROK_DOMAIN" -httpAddr=”:8000" -httpsAddr=”:8001"

客户端

#ngrok.cfg
server_addr: "zisu.org:4443"
trust_host_root_certs: false
inspect_addr: disabled

tunnels:
    web:
        proto:
            http: 8081
    app:
        proto:
            http: 8765
    ssh:
        remote_port: 222
        proto:
            tcp: 22
./ngrok -subdomain web -proto=http -config=ngrok.cfg 8081
./ngrok -config=ngrok.cfg start web

如果服务端和客户端测试没有问题,可以设置为自启动

服务端:

sudo vim /etc/init.d/ngrokd

#!/bin/sh
### BEGIN INIT INFO
# Provides:          ngrokd
# Required-Start:    $local_fs $remote_fs $network
# Required-Stop:     $local_fs $remote_fs $network
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: ngrokd
# Description:
#
### END INIT INFO

NAME=ngrokd
DAEMON=/usr/local/ngrok/$NAME
KEY=/usr/local/ngrok/server.key
CRT=/usr/local/ngrok/server.crt
DOMAIN="example.com"
HTTPADDR=":8080"
HTTPSADDR=":8081"

[ -x "$DAEMON" ] || exit 0

case "$1" in
  start)
    echo "Starting $NAME..."
    start-stop-daemon --start --chuid root --exec $DAEMON --quiet --oknodo --background -- -tlsKey=$KEY -tlsCrt=$CRT -domain=$DOMAIN -httpAddr=$HTTPADDR -httpsAddr=$HTTPSADDR || return 2
    ;;
  stop)
    echo "Stopping $NAME..."
    start-stop-daemon --stop --exec $DAEMON --quiet --oknodo --retry=TERM/30/KILL/5 || return 2
    ;;
  restart)
    $0 stop && sleep 2 && $0 start
    ;;
  *)
    echo "Usage: $0 {start|stop|restart}"
    exit 1
    ;;
esac
exit 0
sudo update-rc.d ngrokd defaults

客户端:

sudo vim /etc/init.d/ngrok

#!/bin/sh
### BEGIN INIT INFO
# Provides:          ngrok
# Required-Start:    $local_fs $remote_fs $network
# Required-Stop:     $local_fs $remote_fs $network
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: ngrok
# Description:
#
### END INIT INFO

NAME=ngrok
DAEMON=/usr/local/ngrok/$NAME
CONFIG=/usr/local/ngrok/ngrok.cfg
TUNNELS="web ssh"

[ -x "$DAEMON" ] || exit 0

case "$1" in
  start)
    echo "Starting $NAME..."
    start-stop-daemon --start --chuid pi --exec $DAEMON --quiet --oknodo --background -- -config $CONFIG start $TUNNELS || return 2
    ;;
  stop)
    echo "Stopping $NAME..."
    start-stop-daemon --stop --exec $DAEMON --quiet --oknodo --retry=TERM/30/KILL/5 || return 2
    ;;
  restart)
    $0 stop && sleep 2 && $0 start
    ;;
  *)
    echo "Usage: $0 {start|stop|restart}"
    exit 1
    ;;
esac
exit 0
sudo update-rc.d ngrok defaults

参考资料:http://www.tuicool.com/articles/MRJjmuv

4. 使用 supervisor 后台运行 ngrok

sudo apt-get install supervisor

在 supervisord.conf 添加如下配置

[include]
files = supervisord.d/*.conf

sudo vim ngrok.conf

[program:ngrok]
directory               = /usr/local/ngrok
command                 = /usr/local/ngrok/ngrok -config=ngrok.cfg -log=stdout
process_name            = %(program_name)s_%(process_num)s
numprocs                = 1 
autostart               = true
autorestart             = true

stdout_logfile          = /var/log/supervisor/ngrok_stdout.log
stdout_logfile_maxbytes = 10MB
stderr_logfile          = /var/log/supervisor/ngrok_error.log
stderr_logfile_maxbytes = 10MB

启动 supervisor

sudo supervisorctl reread 
sudo supervisorctl add ngrok 
sudo supervisorctl update ngrok 
sudo supervisorctl status

启动和停止 ngrok

sudo supervisorctl start ngrok 
sudo supervisorctl stop ngrok

参考资料:https://natapp.cn/article/supervisor

5. 修改 DNS 解析

同时将 example.com 和 *.example.com 解析到 VPS 上,可直接设置将其 A 记录解析到 VPS 的 IP 上,使用二级域名也是可以,但是需要选择支持泛解析的 DNS 服务商,像 DNspod。

6. 常见问题

编译成功的 ngrok 在 Linux 和 Windows 上都可以正常运行,但是 arm 版本在 Raspberry Pi 上会出现内存泄漏的问题,一般运行一段时间后内存耗尽后就会自动退出。

https://github.com/inconshreveable/ngrok/issues/109

解决方法就是在配置文件 ngrok.cfg 下添加 inspect_addr: disabled,关闭管理界面。

#sudo vim ngrok.cfg

server_addr: "domain.com:4443"
trust_host_root_certs: false
inspect_addr: disabled

同时将记录以正常文本的形式输出,即在启动命令后面加入 -log=stdout 选项。

./ngrok -config=ngrok.cfg -log=stdout start web

在程序能正确运行无错误时,甚至可以将记录输出到 /dev/null 文件,ngrok 正常输出的 log 实在太多了。

./ngrok -config=ngrok.cfg -log=stdout start web >/dev/null

7. ngrok 与 nginx 配合使用
上面的配置是针对单独的 VPS 用来搭建 ngrok 的情况,但是,如果 VPS 上的 80 端口还要用于其它服务的话,就需要修改 nginx 的配置。

server {    
    listen  80;  
    server_name *.domain.com;  
      
    root   html;
    index  index.html index.htm index.php;

    location / {
        proxy_pass  http://127.0.0.1:8000;
        #Proxy Settings
        proxy_redirect     off;
        #proxy_set_header Host downloads.openwrt.org;
        proxy_set_header   Host             $host:8000;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_max_temp_file_size 0;
        proxy_connect_timeout      90;
        proxy_send_timeout         90;
        proxy_read_timeout         90;
        proxy_buffer_size          4k;
        proxy_buffers              4 32k;
        proxy_busy_buffers_size    64k;
        proxy_temp_file_write_size 64k;
   }
}  

server {
    listen 443;
    server_name *.*.domain.com;
 
    ssl on;
    ssl_certificate /etc/fullchain.pem;
    ssl_certificate_key /etc/privkey.pem;

    root   html;
    index  index.html index.htm index.php;
 
    location / {
        proxy_pass  https://127.0.0.1:8001;
        #Proxy Settings
        proxy_redirect     off;
        #proxy_set_header Host downloads.openwrt.org;
        proxy_set_header   Host             $host:8001;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_max_temp_file_size 0;
        proxy_connect_timeout      90;
        proxy_send_timeout         90;
        proxy_read_timeout         90;
        proxy_buffer_size          4k;
        proxy_buffers              4 32k;
        proxy_busy_buffers_size    64k;
        proxy_temp_file_write_size 64k;
   }
}

上述配置中需要修改的是 server_name 处的域名,proxy_pass 和 proxy_set_header 处的端口号,共计6处。然后测试 nginx 能否通过。

sudo nginx -t
sudo nginx -s reload

参考资料:https://segmentfault.com/q/1010000004277269

Oct 16

Ngrok可以使外网能够安全的访问内网Web主机,还支持SSH访问内网,上传或者下载文件比较方便,支持Mac OS X,Linux,Windows平台。

1.Ngrok下载运行
官网下载后直接解压得到一个二进制文件,在shell中执行./ngrok 80即可,默认会分配随机的二级域名来访问,转发到本机的80端口。可以通过-help参数来查看详细的说明,运行后如下提示:

Tunnel Status		online
Version			1.6/1.5
Forwarding		http://xxxxx.ngrok.com -> 127.0.0.1:8080
Forwarding		https://xxxxx.ngrok.com -> 127.0.0.1:8080
Web Interface		127.0.0.1:4040
# Conn			16
Avg Conn Time		558ms

这个随机的二级域名不便于记忆,可以修改成一个短小好记的,这是可以使用命令:

./ngrok -subdomain chu 80
./ngrok -subdomain=chu 80

2.tcp端口转发

意思就是可以在外网ssh到本机了,但是外网端口是随机分配的。

./ngrok -proto=tcp 22

外网登录命令为:

ssh root#chu.ngrok.com -p 34567

绑定顶级域名需要付费,有这个需求的可以购买,一年25美金。命令为:

./ngrok -hostname test.dorole.com 80

当然,还需要修改DNS记录才能正常访问。

3.Ngrok配置文件

有时候,不但需要访问内网网站,还需要SSH登录,这样的话命令就比较长,写一个配置文件比较方便。默认是放在~/.ngrok

auth_token: Cel40I5xxxxxxx
  tunnels:
    client:
       subdomain: "chu"
       proto:
         http: 80
         https: 80
     ssh:
       proto:
        tcp: 22

每一个隧道的配置节点都有五个参数,proto,subdomain,auth,hostname和remote_port,每个隧道必须有proto参数来指定本地地址和端口。auth参数用于在http(s)中身份认证,而remote_port用于在tcp隧道中指定远程服务器端口。如果没有配置subdomain参数,ngrok会默认一个二级域名与隧道节点一样的名字。

以上输入命令需要在前面加上./,如果觉得麻烦可以直接将ngrok文件放到/usr/bin文件夹,这样就不需要添加./了。当然,如果需要开机运行,还可以将它放到启动项里。

参考资料:
https://ngrok.com/
https://ngrok.com/usage
http://dorole.com/tag/ngrok/
http://lanvige.github.io/2013/12/02/test-the-web-locally-with-ngrok/