又是折腾的一天。明明 ddl 就在眼前了,为什么……会这样呢……
这次折腾的是路由器级别的透明代理,不过形式稍微有点不同,正如之前发的那一串推所说,是以 DNS
的形式实现的透明代理,因此只要是连上主路由器的设备一般都能正常使用。
当然了,也是有例外的,比如有些设备强制使用自己的 DNS
设置,对于这种设备就没有办法了。不过我这里也没有这样的设备,而就手机平板而言,都是可以正常连接的。
当然了,前提是本机已经配置好了透明代理,这里就来简单介绍一下好了~
SNI 代理
SNI
是 Server Name Indication
的简称,其主要的用途是能让同一个端口下提供复数张证书,由此达到多个网站共用 443
端口的目的。
那 SNI 代理又是什么呢?我们先来看 SNI
。从原理来说,SNI
本身是在 TLS
的 ClientHello
的实现的,如 [RFC 3546] 定义:
In order to provide the server name, clients MAY include an extension of type “server_name” in the (extended) client hello.
我们发现,SNI
其实只是在 ClientHello
时附带的一个明文字符串(下称域名)而已,因此对于代理而言,我们就有操作的空间了。
首先,由于域名是明文的,因此我们就得到了需要访问的域名信息。在此之后,我们只要将所有的信息原封不动地转发给域名对应的服务器就可以了(当然,也包括 ClientHello
)。
由于整个过程没有任何 MiTM
的因素存在,因此我们不需要对证书作任何处理;从原理上来讲,SNI
代理其实只是做了转发处理,因此实际上客户端和服务端的交互仍然是点对点的;唯一的区别可能就在 traceroute
上了。对客户端而言,从包到达 SNI
代理服务器那一刻起,之后的路径就无法掌控了。
SNI
代理因其不需要配置证书的特性而广泛地运用于 DNS
解锁流媒体服务母猪连结解 ban 等领域。同样,因为 SNI
代理是使用 DNS
的,因此对任何网络而言,配置透明代理都变得无比简单:只需要路由器能将对应需要透明代理的域名的 IP
设置为 SNI
代理的服务器 IP
就可以了。
dnsmasq+某 list
dnsmasq
是大名鼎鼎的 DNS 客户端,而某 list
也是人尽皆知的域名列表。这里我们通过将某 list
转换为 dnsmasq
规则来实现透明代理的 DNS 服务器。
首先是规则转换,我们使用的是 [1],不过这里需要修改一下。脚本的输出默认是 server=
,即选择 DNS;而我们想要的是直接返回 IP,即 address=
。将对应 IP 设置为本机在局域网中的 IP 就可以了。
此外,dnsmasq
还需要一个上游 DNS 服务器以返回列表以外的网站 IP
。这里你可以选择任意一个没有污染的 DNS
服务器,或者使用 clash
的内置 DNS
服务器等等。我这里用的是 clash
的内置 DNS
服务器,配置的端口为 53;而 dnsmasq
配置的端口为 5353
。
在配置完 dnsmasq
之后,我们还需要将外部流量的 DNS 端口引导过来。我们使用 iptables
进行端口转发:
sudo iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-port 5353
配置 SNI 代理
接下来就是整个过程的核心了:配置 SNI Proxy
。我们使用 Nginx
的 stream
来完成这一目标:
stream {
server {
listen 443;
ssl_preread on;
resolver 127.0.0.1 ipv6=off;
proxy_pass $ssl_preread_server_name:$server_port;
}
}
我们使 stream
监听 443 端口,并开启 ssl_preread
以使用 $ssl_preread_server_name
,使得我们可以读到 SNI 信息。这里我们设置了 resolver
,即 DNS
解析服务器。ipv6=off
是针对无 ipv6
的环境的,如果有 ipv6
的话可以删去(
配置 http 代理
在 https
可以正常访问之后,我们也不能把 http
丢下。相比之下,http
的配置就相对简单了。这里的关键是 proxy_bind $remote_addr transparent;
,它实现了 http
的透明代理:
http {
# ...
server {
listen 80 default_server;
resolver 127.0.0.1 ipv6=off;
location / {
proxy_pass $scheme://$host$request_uri;
proxy_bind $remote_addr transparent;
proxy_set_header Host $host;
}
}
}
配置路由器
最后也是最简单的一步就是配置路由器了,我们将首选 DNS
设置为本机 IP
,备用 DNS
设置为其他的 DNS
,以保证在本机不可用的情况下也可以正常上网:
至此,整个旁路由透明代理配置完成。
结语
其实某种意义上我也不知道这算不算旁路由,只是感觉这个概念比较符合这种配置而已。毕竟我这电脑和路由器隔了整整一堵墙呢,而且只是把路由的 DNS
引导到了本机而已,网关之类的并没有任何改变。或许也正是由于离路由器远,我才能想到这样的路由器透明代理的方案吧。
从某种意义上来说,这个方案需要改进的地方还是挺多的。首先是规则问题,是不是应该用 dnsmasq-chinalist
以实现更广泛的非国内域名加速服务?其次是本地双 DNS
的问题,这个可能难以改变(因为 Nginx
需要查询真实 IP
),但是不是可以对调一下,省去那一步 iptables
?最后就是 meta
域名,对于顶级域,目前的 Nginx
似乎还无法正常处理,这也是一个迫切(或许)需要解决的问题。
不过从 ddl 角度来看,我大概是疯了吧,明明还有那么多事来着(
如果有帮到你/有所疑问的话,欢迎在评论区留言(