ngrok

module
v0.0.0-...-66deda4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 2, 2017 License: BSD-3-Clause

README

原文地址来自我的博客,欢迎访问:https://www.hexcode.cn/article/show/raspberry-ngrok

场景需求:家里的几台树莓派通过家用WIFI路由器上网,虽然装了Teamviewer可以远程穿透内网控制图形界面,但远程时屏幕分辨率太小,体验不佳,于是想让树莓派上的SSH也能拥有穿透内网的功能。

前言,关于Ngrok

查阅了很多资料,发现大多数内网穿透功能的实现都与一款Go语言编写的Ngrok项目相关,Ngrok是一整套的服务器,客户端解决方案。因为其是由Go语言编写的,所以天生具备很强的跨平台能力(但目前基本上所有资料都指向在Linux上部署该服务器端)。博主使用的是阿里云的Windows VPS,并且这段时间正好也在使用Go编程,因此这篇文章将具体介绍如何在Windows上部署Ngrok项目,并且在家用树莓派上部署客户端,实现树莓派的SSH内网穿透。

关于Ngrok,值得注意的有以下几点:

  • Ngrok并不仅仅用来22端口的SSH内网穿透,通过简单配置,它可以将遵循TCP/UDP协议的多个端口进行内网穿透。
  • Ngrok是由一台服务器端和一个或者多个客户端组成的体系。
  • Ngrok需要一台部署在公网固定IP上的服务器,最好有可正常指向的域名,Ngrok的服务器端就部署在上面,如本次实验的阿里云主机。
  • Ngrok的客户端装在需要映射的各个没有公网IP的机器上,比如本次实验的树莓派。
  • Ngrok服务器会在接受到Ngrok客户端请求时分配固定或者随机的端口号,将客户端请求的端口与之映射,从而达到内网穿透的目的。
  • 用户在远程使用SSH客户端进行连接时,不需要任何Ngrok程序,对终端用户来说,Ngrok是透明的。
  • Ngrok自身有一台欧洲的公网服务器,供没有VPS的用户注册使用,该服务器并不是以分配端口的方式映射,而是以分配子域名的方式,随机子域名是免费使用的,固定子域名映射是收费的。
  • 普通玩家可以直接使用Ngrok的官网服务器,缺点就是有一点点慢,而且每次客户端重启后子域名会随之变化,无法固定使用。
  • 国内也有一些商家提供了Ngrok服务,比如Sunny-Ngrok,同样是收费的,速度可能快一点。

Ngrok源代码是开源的,部署在Git上,但是博主使用下来略觉繁琐,涉及到很多自认为没必要使用的技术,后期我重新在Git上发布了我重构后的Ngrok项目,配有码云同步镜像,以供其他朋友直接使用诸如go build或者go install简单命令进行跨平台编译和安装。

为了致敬原作者,我们对这些高端的Golang编程技术做一个简单的了解,对Go语言没有兴致的朋友可以直接跳过下面这段:

  • go-bindata 技术,该技术是一套将任意资源文件转成二进制数据反向生成到Go代码供hash调用的技术,Ngrok的源码利用这一技术将公网证书和密钥,以及站点的HTML生成到go代码中去了,使其变成了一堆byte数组,在程序中hash调用。这么做固然有它的好处,但缺点也显而易见,因为这套技术要提前用一个项目的程序生成另一个项目的Go代码,所以编译起来难免繁琐,于是Ngrok提供了makefile文件以供用户make编译,这就有点尴尬了,本来好好的跨平台Go语言项目,被make和makefile活生生憋成了只能Linux使用的了。博主初期使用时硬着头皮安装了Cygwin,以及其中的make命令,个中的苦逼无以言表,索性后来了解原理后,将官方的Ngrok重构了一遍,避免了使用几乎是专属于Linux的make指令。
  • build -tags 技术,通过Ngrok项目,我第一次知道Go的这个特性,可以在代码顶端加上// +build !release等标记,注明编译时的模式,比如前面这种表达方式,就是在非release编译模式下才会编译这个文件,然后使用go build -tags 'release'或者go build -tags 'debug'来控制是否编译这个代码文件。这个特性确实灵活,可以在项目中编写不同的编译模式下的代码,缺点呢,就是没有详细文档的情况下,第三方用户并不知道有哪些编译模式,加大了源码本地编译的复杂度,所以这也是原版Ngrok项目使用makefile文件的原因之一。
  • equinox.io 技术,这项技术可以让Go代码在编译和运行期间保持更新,代码作者更新代码后,能够在第三方用户实时更新,具体没有了解,我看原版Ngrok在进行release编译时增加了很多equinox.io令牌密钥,以连接远程仓库,保证代码更新。

总之,上面的这些特性,虽然优点多多,但使用起来难免加大了难度,并且牺牲了Go优良的跨平台特性,这是我不能忍的,于是我重新构造了Ngrok项目,旨在让更多玩家,在众多平台上简单轻松的编译Ngrok项目。

言归正传,准备工作

下面我们言归正传,从零开始部署Ngrok,说说准备工作:

硬件部分
  • 一台公网IP服务器,本实验采用Windows系统,拥有域名hexcode.cn。
  • 一台装有RASPBIAN操作系统的树莓派,用来部署Ngrok客户端,使其可以SSH内网穿透
  • 一台Windows PC,开发环境
软件部分
  • Win32 OpenSSL v1.1.0f Light证书生成程序
  • MobaXterm几乎是最好的SSH客户端
  • go1.9.2.windows-amd64.msiGo语言编译器,我现在用得最多的语言,一股子的亲切感,交叉编译Linux,ARM,X86等平台的目标程序就跟玩一样
  • Ngrok源码这是我自己重构的Ngrok项目,原版的要想用起来还得安装Cygwin等一堆工具,现在非常纯净,拿到手直接用最简单的命令编译,各平台通吃。这份代码不需要手工download,下面会介绍直接用命令获取。
  • Git-windows-amd64这个就不赘述了,程序猿都知道。

从零开始操作

证书生成

Ngrok需要使用SSL证书确保穿透过程中的通信是加密安全的,因此需要SSL证书,值得庆幸的是并不强制去购买CA认证机构颁发的证书,普通用户直接使用OpenSSL工具就可以自行生成证书。方法如下:

  • 下载上述准备工作软件部分的SSL证书生成程序,安装后,将安装路径下的bin目录添加到环境变量,方便cmd直接运行openssl.exe
  • 任意路径打开cmd,执行下面命令:(注意,将两处MY_DOMAIN全部替换成自己的域名,比如我的hexcode.cn)
openssl genrsa -out rootCA.key 2048
openssl req -x509 -new -nodes -key rootCA.key -subj "/CN=MY_DOMAIN" -days 5000 -out rootCA.pem
openssl genrsa -out device.key 2048
openssl req -new -key device.key -subj "/CN=MY_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 ngrokroot.crt
cp device.crt snakeoil.crt
cp device.key snakeoil.key
  • 全部执行完毕后,会在当前路径下生成6个文件,我们服务器端会用到其中的 snakeoil.crt , snakeoil.key ,客户端会用到 ngrokroot.crt 文件,先不管它们,留到下面使用。
编译Windows平台下的服务器端程序

得益于Go的众多优秀的特性,让一份跨平台的代码能够快速地适配不同平台编译和安装。相比其他语言不同的版本,不同的编译方式,编译工具,甚至有些是超级重量级的,比如JAVA,各个平台下的JDK少说也要300M,Go的编译器全平台覆盖,并且只有90M左右,非常轻巧,并且只需要在某一平台下下载一次,就可以轻松靠着两个环境变量编译出全平台覆盖的可执行程序,交叉编译从未如此简单过。

一聊起Go的优点,我就差点收不住。我们来实际体验一下吧,在上方准备工作软件部分下载Go的Windows平台编译器,按照提示安装,设置Windows环境变量PATH路径添加Go安装目录下的bin文件夹,方便cmd中直接使用go命令,为了还能直接使用go get命令,我们需要安装Git,下载地址在上方,下载回来直接根据提示全部以默认选项安装即可。 另外需要配置GOPATH,GOROOT两个环境变量,这两个并不是必须的,但为了减少麻烦还是建议设置,其中GOROOT变量指向Go的安装路径,比如C:\GoRootGOPATH是我们的工作区路径,随意填写,写完了就在此路径下编写程序就行了,当然如果你仅仅用来完成这个项目,以后不会学习Go也不会编写Go语言的代码,这个路径对你来说仅仅是接下来自动下载源代码的保存路径而已。这里我们简单设置成C:\GoPath。 如图所示:

OK,接下来正式下载,编译,和安装Ngrok的服务器端。 你一定会以为这一系列操作很复杂,其实对于Go来说仅仅是一条命令行的指令:

go get github.com/newflydd/ngrok/main/ngrokd

就这么简单,就这么神奇,简单到你不管在什么路径下执行这行命令都没有问题。这行命令的背后可不简单,go首先会去下载我上传到git上的Ngrok代码,然后试着编译,编译过程中它会发现Ngrok项目还七零八落地依赖了其他各个项目,于是Go用递归的方式再去执行go get命令,直到将所有依赖通通下载回来,最终它成功编译后,将生成的目标程序存放在%GOPATH%\bin目录下,默认情况下它是根据当前机器的环境来生成可执行文件,如果做一些简单的调整,它即可以交叉编译不同平台下的可执行文件。

执行过程可能会有些漫长,跟网络环境相关,建议使用全局科学上网工具。 成功编译后,在%GOPATH%\bin目录下会出现ngrokd.exe可执行程序,这就是你要上传到Windows服务器的程序,大小在8M左右,同时你需要上传第一步生成SSL证书中的 snakeoil.crt , snakeoil.key 两份文件到同目录。ngrokd.exe双击直接运行是没有意义的,它需要配有参数,因此我们编写bat脚本来方便运行(将下面的MY_DOMAIN替换成自己的域名,比如我的hexcode.cn):

ngrokd.exe -domain="MY_DOMAIN" -log ./ngrokd.log &

将上面脚本保存为run.bat,双击运行是一个命令行界面,同目录下会出现ngrokd.log,打开看一眼,如果出现以下字样,就说明服务器端程序已经正常运行了:

Listening for public http connections on [::]:80
Listening for public https connections on [::]:443
Listening for control and proxy connections on [::]:4443

运行时的画面如下:

编译Linux ARM平台下的树莓派客户端

试想一下,如果没有交叉编译,我们该如何在树莓派上安装Ngrok客户端:我们必须同样在树莓派上安装Go的Linux ARM版的编译器;RASPBIAN系统已经安装了git,所以我们不需要独立安装git;我们还得用go get指令下载和编译Ngrok的源码;openssl指令不用装了,因为证书已经生成了。

虽然也不是特别复杂,但还是挺费时间的,而且还会占用树莓派的资源,平白无故地要去安装接近90M的Go编译环境。得益于Go的强大,交叉编译发挥了巨大作用,我们在Windows X86 AMD64上就能编译出能在Linux ARM上跑的可执行文件。

打开一个新的cmd命令行,任意路径,执行下面命令:

set GOOS=linux
set GOARCH=arm
go get github.com/newflydd/ngrok/main/ngrok

OK,就这么简单,就这么神奇,Windows AMD64上的Go利用简单的两个环境变量,直接构造出了Linux ARM上的可执行文件,多么伟大的语言。。。 GOOS GOARCH的组合可以参照:

GOOS GOARCH
darwin 386
darwin amd64
freebsd 386
freebsd amd64
linux 386
linux amd64
linux arm
windows 386
windows amd64
在树莓派中运行Ngrok客户端程序

Go将生成的Linux平台下的程序保存在%GOPATH%\bin\linux_arm目录下,名为ngrok,我们需要将其copy到树莓派中。 我们使用超级强大的终端工具MobaXterm来连接树莓派并上传ngrok,MobaXterm的下载地址在本文上方,下载后按照提示就能安装,因为目前树莓派的SSH还不具备内网穿透功能,因此下面操作必须在局域网中完成。

使用MobaXterm建立与树莓派的SSH连接,这里并不是重点,相关操作如果不清楚可以自行百谷。 MobaXterm具有强大的文件上传功能,直接将使用下面图例中的按钮将Windows本地的ngrok客户端程序上传到指定目录,同时将我们第一步SSL证书环节生成的 ngrokroot.crt 文件上传进去。

我这里操作的目录为:/home/pi/workspace/go-apps/ngrok/,此时上传上去的ngrok文件不具备执行能力,需要使用下面命令为其添加执行权限:

chmod a+x ngrok

同样,ngrok程序不可以直接运行,也需要参数,并且还需要一个配置文件,我们执行下面操作新建配置文件,并编写sh脚本,以便直接运行。

nano ngrok.cfg

新建ngrok.cfg配置文件,并用nano文本编辑器打开,输入以下文本(将MY_DOMAIN替换成自己的域名,比如我的hexcode.cn):

server_addr: "MY_DOMAIN:4443"
trust_host_root_certs: false

tunnels:
    ssh:
        remote_port: 50000
        proto:
            tcp: 22

我这里仅仅需要让SSH的22端口具备穿透能力,因此我只配置了ssh任务,指定了远程端口固定为50000,映射本地的SSH22端口。Ctrl+O并回车保存,Ctrl+X退出文本编辑器。

nano run.sh

新建run.sh脚本文件,并用文本编辑器打开,输入以下文本:

nohup ./ngrok -log ./ngrok.ssh.log -config ./ngrok.cfg start ssh &

Ctrl+O并回车保存,Ctrl+X退出文本编辑器。

chmod a+x run.sh

为run.sh添加可执行权限。OK,大功告成,运行./run.sh即可建立树莓派与公网服务器的通信隧道,将其22端口映射到了公网服务器的50000端口,我们接下来就可以在世界任何角落,使用任何SSH工具,连接自己公网服务器的50000端口,访问家中的树莓派了,下图用MobaXterm做一个测试:

Directories

Path Synopsis
The implementation borrows heavily from SmallLRUCache (originally by Nathan Schrenk).
The implementation borrows heavily from SmallLRUCache (originally by Nathan Schrenk).
mvc
views/term
shared internal functions for handling output to the terminal
shared internal functions for handling output to the terminal
views/web
interactive web user interface
interactive web user interface
main

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL