使用Ghostunnel建立TLS加密隧道

我一直感到很奇怪的一点是,TLS在当今各种工具中被大量使用,但却很少看到单纯的tls隧道的搭建的文章,甚至连这类工具的介绍都很少。

找了一圈最后找到了ghostunnel,其实他是个stunnel的替代品,基于go语言开发。为什么不写stunnel的?因为很多很多年前就有人写过了。

证书管理部分

其实tls隧道最复杂的并不是隧道本身的搭建,而是一大堆证书的生成和管理。你可以直接选择购买ssl证书拿来用,但我个人认为这没有必要。使用私有CA授权的证书和过期证书的网站和服务器在网络上比比皆是,单从伪装角度讲,商业证书未必有什么优势,墙不会因为你花钱买了EV级证书就对你网开一面。

证书管理工具我选择了certstrap,下面是几个关键步骤。

生成根证书

certstrap init --common-name "CA" 

这里的common-name是必须的,具体名字你可以自己定,这个命令会生成CA.crt、CA.csr、CA.key这三个文件,

合并根证书为pem文件

cat CA.crt CA.key >CA.pem

很简单,就是把key和cert直接拼起来生成一个包含了二者的pem文件,因为ghostunnel需要使用这样的格式的CA。

为节点(客户端、服务器)生成key

certstrap request-cert --domain *.ssr.org --expires "100 years"

这里根据实际域名填写,分别是客户端和服务器端的域名,这会生成_.ssr.org.key 和_.ssr.org.csr 两个文件。expires就是过期时间我们设定为100年(反正不要钱),默认的话是两年有效期。

用根证书为节点签发证书

certstrap sign _.ssr.org --CA CA 

这里的CA就是我们刚才生成的根证书的名字,这会生成_.ssr.org.crt

生成合并的pkcs12格式keystore文件

其实到这里本来就够了,为了方便使用,我们可以生成一个合并的包含了公私钥的keystore文件:

openssl pkcs12 -export -out ssr.p12 -inkey _.ssr.org.key -in _.ssr.org.crt -certfile CA.crt

这样会得到一个ssr.p12的keystore文件。

最终的文件

假定我们的国内节点是cn.ssr.org,国外节点是x.ssr.org,因为我们申请域名用的是泛域名*.ssr.org,所以两边可以用同一个证书。我们最终需要的就是CA.pem和ssr.p12。

测试框架

我们在国外的服务器(x.ssr.org)上运行一个简单的http服务器,但只监听localhost的8001端口并不开放给公网。

python3 -m http.server 8001 --bind 127.0.0.1

在国外服务器上运行ghostunnel的服务器端,从服务器端的0.0.0.0:8002端口转发到服务器的localhost:8001端口。 在国内服务器运行ghostunnel的客户端,从客户端的0.0.0.0:8003转发到服务器的0.0.0.0:8002端口。 本来如果没有隧道直接访问,路径应该是这样的: client--HTTP-->x.ssr.org:8001 现在有了TLS隧道后,最终路径是这样的: client--HTTP-->cn.ssr.org:8003-->TLS TUNNEL-->x.ssr.org:8002--HTTP-->x.ssr.org:8001 也就是说,对客户端(浏览器)和最终的服务端(http server)来说,完全不需要做任何改变,依然可以使用它原来不安全的http协议明文传送,但从cn.ssr.org到x.ssr.org的这个跨国链路,就是完全TLS加密的了,为什么这样做我想不用解释了。

Ghostunne服务器配置

假设我们生成的证书全部放在一个out目录下,服务器运行参数如下:

ghostunnel server \
    --listen 0.0.0.0:8002 \
    --target localhost:8001 \
    --key out/_.ssr.org.key \
    --cert out/_.ssr.org.crt \
    --cacert out/CA.pem \
    --allow-all \
    --quiet all

上面这是分别制定key和cert文件的,我们也可以使用合并的keystore文件,命令就变成这样了:

ghostunnel server \
    --listen 0.0.0.0:8002 \
    --target localhost:8001 \
    --keystore out/ssr.p12 \
    --cacert out/CA.pem \
    --allow-all \
    --quiet all

这里的--allow-all表示允许所有客户端连接,也可以通过指定域名证书的方式,限制特定IP(域名)来源的连接,配合自己的私有CA证书,非授权的机器几乎不可能连上服务器。 指定域名的用法是: 把--allow-all替换成:

--allow-dns *.ssr.org

这样如果客户端的证书签发的时候不是签给了我们指定的域名,就无法连接到服务端。

Ghostunnel客户端配置

同样的,分别使用key和cert文件:

ghostunnel client \
    --listen 0.0.0.0:8003 \
    --target x.ssr.org:8002 \
    --key out/_.ssr.org.key \
    --cert out/_.ssr.org.crt \
    --cacert out/CA.pem \
    --unsafe-listen \
    --quiet all

使用合并的keystore文件:

ghostunnel client \
    --listen 0.0.0.0:8003 \
    --target x.ssr.org:8002 \
    --keystore out/cn.p12 \
    --cacert out/CA.pem \
    --unsafe-listen \
    --quiet all

打完收工。平时如果直接从国内访问国外机器的http端口并且传大量文件,很快就会被rst了,现在我们可以通过国内的cn.ssr.org:8003端口+TLS隧道来访问,实测下来下载几个G的文件也没什么发生,速度还挺稳定。