使用docker和wstun实现隧道和反向隧道

需求及方案确定

这次的需求是因为一个服务器的网络配置有问题,只能走WEB接入,不能访问外网,但是应用又需要访问第三方的服务,被迫寻找解决方案。

因为只能走WEB(这里是Nginx代理的HTTPS),所以唯一的方法就是WebSocket,好在WebSocket代理工具还挺多,但是具体到这种需要反向隧道的情况,目前只找到MDSLab的wstun,一个基于node.js实现的WSTunnel。

又因为不想装node.js,所以用了官方的docker镜像。

顺便说一句,这个解决方案感觉不错,可以用来作为一种内网穿透的工具。

系统架构

先以比较简单的正向隧道来说:

client => docker wstun client(bind client port) =Internet=> nginx => docker wstun server => server port

反向隧道其实差不多,不过需要理解一下连接和传输的方向是相反的——连接还是从客户端连到服务端,但是隧道的传输是服务端连到指定的绑定端口,然后通过隧道到达客户端的相应端口:

client port => docker wstun client =Internet=> nginx => docker wstun server(bind server port) => server

区别在于,docker wstun client建立隧道以后,是连到客户端的端口,然后docker wstun server绑定服务端的端口,把服务端发往这个端口的请求转发到客户端。

部署

由于docker的存在,所以两边的网络实际上会多一层,配置上要注意,不然很容易就配不通。

另外,官方镜像有一点比较坑的是,他们用了ENTRYPOINT,导致运行时会强制执行这个ENTRYPOINT,这里应该用CMD的。

解决方法有两个,一个是运行时加上:--entrypoint='',另一个是派生一个新的镜像,在Dockerfile里加上ENTRYPOINT []

我是用后者,所以命令里省去了--entrypoint='',如果用官方镜像的话,记得自己加上这个。

正向隧道

以连接服务器的SSH为例。

首先在服务端运行一个容器:

docker run -d -p 127.0.0.1:1808:1808 --name wstun_server mywstun wstun -s 1808

其中mywstun是自己派生的镜像名,后面的容器运行命令:wstun -s 1808表示启动一个正向隧道,监听1808端口的WebSocket。

前面的参数中:127.0.0.1:1808:1808是把容器里这个1808映射出来供Nginx代理用。

Nginx的配置如下:

# 在http段加上全局配置
map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}
# 在server段加上代理配置
    location /wstun/ {
        proxy_pass http://127.0.0.1:1808/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
    }

再来配置客户端,同样是运行一个wstun容器:

docker run -d -p 127.0.0.1:2222:2222 --name wstun_client mywstun wstun -t 2222:192.168.x.x:22 https://x.x.x.x/wstun

同样用自己派生的镜像运行命令:wstun -t 2222:192.168.x.x:22 https://x.x.x.x/wstun,其中那个URL就是nginx代理出来的WebSocket地址,-t参数则表示要建立一个隧道,从本地的2222端口到远程的192.168.x.x:22。

注意,远程的127.0.0.1并不是远程服务器,而是远程wstun容器,不要搞错了。

然后还要用docker把容器里的2222映射到客户端的127.0.0.1:2222。

现在就可以在客户机用ssh通过127.0.0.1:2222连接到远程的192.168.x.x:22了。

反向隧道

这个就是最初的需求了,把本地的HTTP代理服务器映射到服务器上供服务端上网用。

首先在客户端装一个HTTP代理服务器,比如privoxy绑定8118端口。注意:绑定地址不能为127.0.0.1,因为需要给docker wstun client访问,所以必须是本地局域网IP或用0.0.0.0。

然后在服务端运行一个容器:

docker run -d -p 127.0.0.1:1808:1808 -p 127.0.0.1:8118:8118 --name wstun_server mywstun wstun -r -s 1808

与正向隧道的配置差不多,只是多了一个-r参数。另外就是docker的参数里增加了一个8118的映射。

nginx代理配置跟正向代理一样,略过。

再看客户端的容器:

docker run -d --name wstunr_client mywstun wstun -r8118:192.168.x.x:8118 https://x.x.x.x/wstun

主要就是一个-r参数,其中192.168.x.x是本机的局域网地址,不可以用127.0.0.1,因为这个是容器内的,不会连到privoxy。

这样就建立了一个反向的隧道:

客户端容器把192.168.x.x:8118映射到远程端的8118,远程的服务端容器监听的8118被docker映射到服务器的127.0.0.1:8118,这样服务端只要使用127.0.0.1:8118做代理即可通过这个隧道转发到客户端的privoxy的8118端口,再出去互联网。

推送到[go4pro.org]