SJ's Here
SJ's Here

简单实现路由器级别的 HTTP/HTTPS 代理

前言

如果要实现路由器级别的透明代理,免不了的便是修改 iptables ,固不可避免的需要魔改现有的路由器。

可是如果我不想折腾,而且大多数情况并不需要全局的透明代理,只需要代理 80 443 两个端口 (HTTP 与 HTTPS) 即可。那么有没有什么折中的方案呢?有的。

原理

因为只需要代理 80 与 443 两个端口上的 HTTP 与 HTTPS 服务,那么只需要修改路由器的 DNS ,让被 GFW 屏蔽的域名解析到内网地址,再在这个地址上进行对目标网站的 “反向代理” 即可。

注意到 “反向代理” 是加了引号的——这并不是传统意义上的反向代理。传统意义上的反向代理是一对一的,而在这个例子中内网地址要代理所有被 GFW 屏蔽的网站,那怎么办呢?

对 HTTP (明文)流量的代理

对 HTTP 协议的代理很简单,只要从客户端发出的 HTTP 请求中读取 Host 请求头,再将流量转发给域名对应的服务器即可。

对 HTTPS (TLS)流量的代理

乍一看,HTTPS 在传输层使用了 TLS 协议,我们似乎无从下手。其实不然,在 TLS 协议栈中有一个叫做 SNI(Server Name Indication) 的扩展协议。支持 SNI 特性的浏览器在 TLS 握手时会把访问的域名以明文的形式发给服务器,利用这个特性我们便可以获取到客户端正在访问的域名,从而把流量正确的转发给对应的服务器。

TLS ClientHello 中的 SNI
TLS Client Hello 中的 SNI

SNI 设计之初的目的是让一个服务器的 443 端口能提供多个证书,以达到多个网站共用 443 端口的目的——如果客户端在 TLS 握手的时候没有向服务器提供要访问的域名,服务端就无法从多个证书中选择正确的证书,导致 TLS 握手失败或是用户收到不安全警告。后来,SNI 也在 GFW 中被用于屏蔽特定网站流量(被称作 SNI 阻断技术)。现在,我们同样利用这个特性来绕过 GFW 的阻断。bksw

实现

DNS 配置的实现

dnsmasq,当然是 dnsmasq,还能是什么(笑

用一个特定的脚本把 gfwlist 转换成 dnsmasq 的配置文件,再将服务器的默认 DNS 设置成这个地址就好了,没什么需要特别说明的地方。

“反向代理” 的实现

当然是自己写代码

事实上,有现成的软件可以做到这一点——V2Ray。作为一个超级复杂,超级难用的工具,如果连这一点都不支持,就愧对它的名声了。

我们利用 V2Ray 的 dekodemo-door inbound 协议作为入口。乍一看这个协议只能将流量转发到特定的地址,似乎没法满足我们的需求。仔细想想,V2Ray 还有一个叫 Sniffing 的特性,嗯,这样就没有问题了。来看具体的配置文件:

"inbounds": [
    {
        "port": 80,
        "protocol": "dokodemo-door",
        "settings": {
            "address": "0.0.0.0",
            "port": 80,
            "network": "tcp",
            "followRedirect": false
        },
        "sniffing": {
            "enabled": true,
            "destOverride": ["http"]
        }
    },
    {
        "port": 443,
        "protocol": "dokodemo-door",
        "settings": {
            "address": "0.0.0.0",
            "port": 443,
            "network": "tcp",
            "followRedirect": false
        },
        "sniffing": {
            "enabled": true,
            "destOverride": ["tls"]
        }
    }
],
...

我们指定了两个 inbound ,分别用来代理 80 端口的明文 HTTP 流量与 443 端口的 HTTPS 流量。配置文件大部分看名字便可以知道意思。值得一提的是,sniffingObjectdestOverride 中,http 便是上文提到的 “从 HTTP 请求头中读取域名”,tls 便是上文提到的“通过 SNI 获取域名”。

写在后面

这个代理方法并不是笔者自创的,几乎是完全从某昨的博客里照搬过来的,只是在软件实现上有一些区别。

此外,SNI 协议的安全问题也正在逐渐受到重视,CloudFlare 与 Mozilla 就在推行名为 ESNI(Encrypted SNI)的协议,因此,也许在几年之后,这种代理方式就不适用了。

没有标签
首页      未分类      简单实现路由器级别的 HTTP/HTTPS 代理

发表回复

textsms
account_circle
email

SJ's Here

简单实现路由器级别的 HTTP/HTTPS 代理
前言 如果要实现路由器级别的透明代理,免不了的便是修改 iptables ,固不可避免的需要魔改现有的路由器。 可是如果我不想折腾,而且大多数情况并不需要全局的透明代理,只需要代理…
扫描二维码继续阅读
2020-05-02