这篇教程主要是向大家介绍反向代理和内网穿透的简单概念和使用场景。并且教大家如何自己搭建一个反向代理服务器进行内网穿透。
背景
在开始前我们先来看一个场景。某一天你新学了一个Web框架,弄了一个HelloWorld后很兴奋,要给朋友show一下(因为只是临时show一下,所以你没有考虑将网站部署到服务器上)。这时候就比较尴尬了,你家宽带没有独占IP,公司是独占IP但是你又没权限配置路由器的转发表。你发现你的网页只有在局域网内才能访问得到,在外网的朋友是无法访问的。这时候有两个方法,一个方法是将网站部署到有公网IP的服务器上,另一个方法就是本文的主角——使用反向代理
进行内网穿透
。
反向代理-Reserve Proxy
反向代理其实不是什么新鲜玩意儿,它本质上和正向代理一样,都是把流量做转发。而正向代理的最典型的例子就是大家所用的梯子。假如客户端A
到服务端B
中间有个防火墙,屏蔽了所有A
到B
的流量,而有个设备C
,A
到C
,C
到B
的路由都是通的。这时候我们就可以把流量先发给C
,C
收到后再将流量发给B
,这样我们就可以访问到B
上的资源了。这就是正向代理比较直观的例子。
如果说正向代理代理的是客户端,那么反向代理代理的就是服务端。比较直观的例子是负载均衡。比如我访问百度的域名,我的请求会首先到一个服务器C
上,C
会给根据我的网络情况从很多的实例服务器中给我分配一个最快的实例服务器。这里的C
做的工作其实就是反向代理。从这里我们可以看出正向代理多是管理出去的流量的,而反向代理多是管理进来的流量的。
反向代理的作用有
- 对客户端隐藏服务器(集群)的IP地址
- 安全:作为应用层防火墙,为网站提供对基于Web的攻击行为(例如DoS/DDoS)的防护,更容易排查恶意软件等
- 为后端服务器(集群)统一提供加密和加速(如SSL终端代理)
- 负载均衡,若服务器集群中有负荷较高者,反向代理通过URL重写,根据连线请求从负荷较低者获取与所需相同的资源或备援
- 对于静态内容及短时间内有大量访问请求的动态内容提供缓存服务
- 对一些内容进行压缩,以节约带宽或为网络带宽不佳的网络提供服务
- 减速上传
- 为在私有网络下(如局域网)的服务器集群提供NAT穿透及外网发布服务
- 提供HTTP访问认证
- 突破互联网封锁(不常用,因为反向代理与客户端之间的连线不一定是加密连线,非加密连线仍有遭内容审查进而遭封禁的风险;此外面对针对域名的关键字过滤、DNS缓存污染/投毒攻击乃至深度数据包检测也无能为力)
内网穿透-Intranet Penetration
在文章的开头我们举了一个例子,在外网的设备是无法直接访问到内网的资源的。因为各种路由(运营商的、自己的)将我们的内网的设备给屏蔽了。就像你无法直接访问到百度最下层的实例服务器一样。这时候我们就可以利用反向代理实现将内网的服务暴露出去,称之为内网穿透。
具体流程是这样的。我们需要一个有公网IP的服务器C
,然后从本地A
向C
访问,和C
握手、建立稳定的信道。在外网的设备B
向C
发送请求,C
通过刚才建立的稳定信道将请求转发给A
。A
收到请求后将响应发给C
,C
再将响应返给B
。这样B
就能访问到内网A
的资源了。
比较著名的远程桌面控制软件Teamviewer
其实背后的原理就是这样。
构建自己的内网穿透服务
我们这里使用的是ngrok。ngrok 是一个开源的反向代理工具(2.0版本后闭源),通过在公共的端点和本地运行的 Web 服务器之间建立一个安全的通道。ngrok 可捕获和分析所有通道上的流量,便于后期分析和重放。
我们会在云服务器上安装ngrok服务端,并为客户端生成证书。没有什么敲代码的工作,直接拿来用就行了。
我云服务器的环境为Ubuntu18,本地环境为macOS,不同的环境接下来的步骤可能会有区别,请大家自行解决。
安装GO
在云服务器上安装GO。
$ sudo apt upgrade
$ sudo apt install golang
装完后应该可以看到go的版本。
$ go version
go version go1.10.4 linux/amd64
配置Ngrok
从Github拉取Ngrok源码。
$ git clone https://github.com/inconshreveable/ngrok.git
$ export GOPATH=~/ngrok
接下来我们需要生成自己的证书,并编译携带该证书的客户端。
接下来的mytencent为我的hostname,请换成自己的。不能直接使用IP地址,否则会有证书错误。可以在本地的hosts里把自己的云服务器加上。
$ cd ngrok
$ NGROK_DOMAIN="mytencent.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
如果在第三步报错:
139632903422400:error:2406F079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:88:Filename=/home/ubuntu/.rnd
删除/usr/lib/ssl/openssl.cnf
开头的RANDFILE=...
执行完以上命令,在ngrok目录下会生成6个新的文件
device.crt device.csr device.key rootCA.key rootCA.pem rootCA.srl
替换证书
Ngrok通过bindata将ngrok源码目录下的assets
目录打包到可执行文件(ngrokd
和ngrok
)中去,assets/client/tls
和assets/server/tls
下分别存放着用于ngrok
和ngrokd
的默认证书文件,我们需要将它们替换成我们自己生成的。
$ cp rootCA.pem assets/client/tls/ngrokroot.crt
$ cp device.crt assets/server/tls/snakeoil.crt
$ cp device.key assets/server/tls/snakeoil.key
编译
选择对应平台的编译方式,编译服务端
$ GOOS=linux GOARCH=amd64 make release-server
如果这步速度慢超时,手动拉一个文件
$ git clone https://gopkg.in/yaml.v1 : ~/ngrok/src/gopkg.in/yaml.v1
编译客户端
$ GOOS=darwin GOARCH=amd64 make release-client
编译完成后~/ngrok/bin/ngrokd
为服务端运行文件,ngrok/bin/darwin_amd64/ngrok
为客户端运行文件。将服务端文件放到/usr/bin/
下就可以直接执行了。将客户端文件下载到自己的电脑上。
$ sudo cp ~/ngrok/bin/ngrokd /usr/bin/
$ scp <your user name>@<your ip>:<your generated client path> <your save path>
运行
服务端运行
$ sudo ngrokd -domain="mytencent.com" -httpAddr=":6000"
客户端目录下放一个配置文件grok.conf
server_addr: "mytencent.mt:4443"
trust_host_root_certs: false
运行
$ ./ngrok -config=ngrok.conf -subdomain=test 8080
没问题的话应该可以看到如下界面,本地和云服务器成功建立了一个信道。当有请求云端6000端口的http请求时,请求会通过云服务器转发到我们本地的80端口。
我们在本地起个服务测试一下。用Flask快速起一个网页。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
访问localhost应该能看到如下界面
本地访问没问题后我们访问http://example.mytencent.com:6000
。如果你的云服务器只有IP地址,没有域名的话这里会有问题。因为我们在本地的hosts里把xxx.xxx.xxx.xxx和mytencent.com对应上了,但是这里又来了个subdomain,用不了。(Ngrok1的http和https协议必须带subdomain,自己服务器有域名会比较方便。Ngrok2可以去掉subdomain,但是不开源就没试了)
所以我们使用tcp协议
$ ./ngrok -config=ngrok.conf -subdomain=test -proto=tcp 8080
可以看到
访问mytencent.com:42557
可以看到
这里只是个小demo,实际使用应该有其他问题,大家可以深入研究研究。
未经允许不得转载:下一个课程 » 使用反向代理进行内网穿透