自己搭建ngrok服务器实现树莓派内网穿透

时间:2022-09-17 15:47:07

目标

实现从公网访问位于家中的树莓派,包括ssh连接和http访问

准备

  1. VPS【有公网ip】:ubuntu14.04
  2. 树莓派【可连接internet】:raspbian jessy
  3. ngrok源码【点此下载】
  4. go 语言(ARM版 for 树莓派和AMD64版 for ubuntu)点此下载,需要扶*

VPS配置

我的VPS是腾讯的,安装了ubuntu14.04版本的go语言环境。
安装方式如下:
sudo apt-get install golang-1.6
注意在14.04版本需要上述命令,而在16.04的安装命令是:
sudo apt install golang
也可以在上面的下载地址中直接下载go语言,然后复制到你喜欢的路径下即可。
然后配置环境变量:
使用vim打开/etc/profile:
sudo vim /etc/profile
在最后加入:
export GOROOT=/usr/lib/go-1.6
export GOPATH=/home/ubuntu/ngrok
export PATH=$GOROOT/bin:$PATH

其中GOROOT的位置要只想go语言安装包中的/bin文件夹的上一级目录
GOPATH是开发目录,指向ngrok的源码目录即可
PATH中需要加入go语言安装包的/bin文件夹
配置完成,使用:
source /etc/profile
使配置生效。
可以采用 go env来查看是否go语言安装状态

树莓派配置

树莓派上无法通过包管理器直接安装go语言环境,需要下载源码,注意下载arm版本的。地址如上。
下载完成后解压缩,将go文件夹放到/usr/local/下。
配置环境变量和VPS下一致,只需要注意文件夹路径即可。

下载ngrok源码并编译运行

如上所述,ngrok的源码下载好后,我将其存放在我的本地电脑【ubuntu16.04】中。

首先需要配置证书:

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

注意上述代码中的yourdomain字段。这个域名必须和你的vps域名的根域名一致,使用时注意替换解释如下:
假设我的vps域名是myvps.com,假设我在这里把证书中的yourdomain替换为myvps.com,之后ngrok服务架设完成,我的树莓派上跑了一个nginx服务器,我需要把它穿透到外网,这个时候ngrok运行需要一个subdomain参数,假设我设置为rasp,那么最终我要访问我的树莓派上的web服务时,需要输入的域名就为rasp.myvps.com。
你也可以把这个yourdomain字段设置为ngrok.myvps.com用来区分,这样的话需要你在vps的域名解析中设置子域名的解析,否则无法使用。
上述命令执行完成后,在ngrok的目录下应该生成了6个新的文件。然后,我们需要执行:
cp rootCA.pem assets/client/tls/ngrokroot.crt
cp device.crt assets/server/tls/snakeoil.crt
cp device.key assets/server/tls/snakeoil.key

ok,证书的准备工作到此结束。

编译生成linux amd64 的ngrok服务端

最终这个服务端是要运行在vps上的,所以如果你不清楚本地编译的程序能否在vps上运行的话,最好直接在vps上编译。
我的vps是ubuntu14.04 amd64,本地环境是ubuntu16.04 amd64,所以我就直接在本地编译了。

cd ./ngrok
make release-server

编译过程中可能需要从http://gopkg.in/和github上下载一些所需的文件和包,由于国内的你懂的原因,可能访问速度不是很快,会出现域名解析失败或者连接超时等,我采用的方法时浏览器访问http://gopkg.in/,在需要下载github上代码的地方,如果下载不成功,可以自己手动根据命令行提示的地址自行下载,放到/src/github文件夹下即可。
最终编译完成的话,会在./ngrok/bin/文件夹下生成一个ngrokd可执行文件,使用scp命令将其复制到VPS上。
可以将ngrokd放置在 $GOROOT/bin下,这样可以直接运行。

运行ngrok服务端

在VPS上,运行:
ngrokd -domain="yourdomain" -httpAddr=":8081" -httpsAddr=":8082" -log ./ngrokd.log &
注意domain参数要和证书文件中的yourdomain一致,-log参数是记录日志文件,&时为了让ssh断开时ngrokd不退出。

编译树莓派上的ngrok客户端

将刚才用来编译ngrok服务端的ngrok源码文件夹全部复制到树莓派上:
cd ./ngrok
make release-client

编译完成后会在./ngrok/bin文件夹下看到ngrok可执行文件
将其复制到$GOROOT/bin文件夹下
在该文件夹下创建一个ngrok.cfg配置文件:

server_addr: "yourdomain:4443"
trust_host_root_certs: false

运行:
ngrok -log /usr/local/go/bin/ngrok.ssh.log -config /usr/local/go/bin/ngrok.cfg -proto tcp 22
ngrok -log /usr/local/go/bin/ngrok.log -subdomain rasp -config /usr/local/go/bin/ngrok.cfg 80
第一条命令是生成一个tcp连接,穿透到树莓派的22端口,实现ssh。
这样,会在客户端下看到VPS上的一个随机端口被分配,这个端口可以连接到树莓派上。
第二条命令是将rasp.yourdomain分配到树莓派上的80端口。可以在浏览器直接访问rasp.yourdomain:8081来访问树莓派上的web服务。

关于ngrok客户端的配置文档

ngrok的1.0版本是开源的,ngrok官网给出的所有文档是针对2.0版本的,因此,完全按照官网的说明来配置客户端的配置文件可能会出现问题。下面给出我的一个配置文档,对于普通用户来说应该是够用了,如果需要其他配置可百度。

server_addr: "myvps.cn:4443"
trust_host_root_certs: false
tunnels:
ssh:
remote_port: 50022
proto:
tcp: "192.168.31.14:22"
http:
remote_port: 50080
subdomain:
mysubdomain
auth:
"user:password"
proto:
http: 80
vnc:
remote_port: 55901
proto:
tcp: "192.168.31.14:5901"

server_addr指明了ngrok服务器端的地址,默认端口是4443,注意这个server_addr也可以是其他域名,只要域名解析后服务器的ip地址是一致的即可,但是会影响http配置中的subdomain参数。
假如将server_addr设置为myvps.cn,http配置中的subdomain配置为mysub,那么,客户端连接后http的显示为http://mysub.myvps.cn:50080。可以通过nginx等反向代理来实现不加端口访问。配置文件保存后,可以在客户端终端运行:
ngrok -log=/usr/local/go/bin/ngrok.log -log-level=INFO -config=/usr/local/go/bin/ngrok.yml start http ssh vnc > ngrok.log &

树莓派客户端和VPS服务端的自启动配置

客户端自启动

在树莓派上把下面的代码写入自启动脚本ngrok.sh中:

nohup /usr/local/go/bin/ngrok -log=/usr/local/go/bin/ngrok.log -log-level=INFO -config=/usr/local/go/bin/ngrok.yml start http ssh vnc > ngrok.log &

给自启动脚本添加可执行权限。
编辑/etc/rc.local文件,在exit 0前加入/home/pi/ngrok.sh
注意不能省略自启动脚本中的ngrok的绝对路径,因为系统启动时环境变量中还未存在ngrok,省略路径会导致执行失败。

服务端自启动

将下面的代码写入ngrokd.sh

for filename in `find /var/ngrokd/ -name "ngrokd.log*"`;do rm $filename -rf; done
echo "[ INFO ] ngrok日志已删除"
echo "[ INFO ] 正在启动ngrok..."
/var/ngrokd/ngrokd -domain="myvps.cn" -httpAddr=":50080" -log /var/ngrokd/ngrokd.log -log-level INFO &
echo "[ INFO ] ngrok已启动

第一行主要是为了在每次自启动前删除掉之前生成的日志文件。
给ngrokd.sh和/etc/rc.local增加可执行权限,然后编辑/etc/rc.local,在exit 0前加入/var/ngrokd/ngrokd.sh

几个问题

  1. 使用ngrok的连接有个问题,一段时间后,访问树莓派上的web应用或者ssh连接,第一次都会很慢或者失败,但是第二次就很快了,目前没有看到如何解决。
  2. 如何让外网访问时的rasp.yourdomain:8081这样的地址中的端口号去掉?
    因为我的vps上的80端口是被占用了的,所以解决方案是使用nginx来反向代理,具体操作时编辑vps上的nginx配置文件/etc/nginx/sites-available/default:
    在其中加入这样一段:
    server {
    listen 80;
    server_name rasp.yourdomain;
    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;
    }
    }

    然后重启nginx服务,这样可以直接访问rasp.yourdomain来访问到树莓派。

  3. 我的证书文件下的域名和我的ngrok客户端运行时的配置文件中的域名一致,为什么服务器端的日志中还是出现bad certificate提示?
    这也是我遇到比较奇葩的一个问题,害得我重新下载了ngrok和go语言,重新编译了。最后我才知道,,原来我的树莓派下的时间不对,解决方法就是把vps和树莓派的时间设置为一致。

  4. 关于树莓派下使用nginx php5中,php显示空白页的问题。啥都不说了,看这个最新的解决方案,不要再被百度坑了,google大法好:http://aircheese.me/2016/typecho-lnmp-raspberry-pi3.html
  5. 将vps上编译过服务器端程序的源码文件夹直接复制到树莓派上编译客户端时,可能出现编译不成功的现象:
    自己搭建ngrok服务器实现树莓派内网穿透
    这时需要把源码目录下bin/go-bindata删除,然后执行make release-client,就会重新在github下载go-bindata,下载完成后即可编译完成。