引言
为了实现全屋代理,我选择 R4S 作为我家庭网络中的主路由,Linksys MX5300 作为二级路由,代理方案使用 OpenClash 插件。由于整套环境存在些许问题,所以利用周末的时间,改造下上网环境。
问题根源
当上网稳定的情况下,我一般是很少再动了。毕竟如果一旦出现问题,会导致家庭网络瘫痪从而使家中所有智能设备全部掉线,如果设备支持自动连接还好,但是不支持自动连接的问题就大了。 另外如果导致软路由断网,那基本上可以宣告 Game Over 了。
既然如此,为什么还要冒着断网的风险去重新配网呢?而且还是在身居外地的情况下,究其原因无非以下几点:
- 硬路由以二级路由配置,导致网络中存在两次 NAT 转换。
- 由于存在 NAT,软路由无法拿到网络中所有的上网设备进而无法对网络中的每个设备做监管。
- 网络由 OpenClash 统一接管,模式为 Fake—IP 增强,绕过中国大陆地址。我需要完全自定义 DNS 解析,避免全权交由 OpenClash 处理以增加解析成本。
- 作为二级路由的 Linksys 每次断电都会亮红灯且无法修复,所以不得不对软路由再次重启以解决红灯问题(虽然红灯,但是网络连接丝毫无影响,原因是该路由会定期发送心跳,而该心跳被代理返回了 Fake-IP,从而导致红灯)。
由于存在以上四点主要问题,所以就不得不优化一下网络了。
网络桥接
前两种问题是最好解决的,但是也比较麻烦。之所以好解决,是因为将硬路由的上网模式改为桥接即可。但是桥接可能丧失路由的 Mesh 组网功能,这点需要注意。
前两种问题解决了,但是会迎来另一个棘手的问题。为了保证家庭 wifi 的安全性,我们一般都会进行 MAC 绑定,然后仅允许绑定 MAC 的设备上网。但是桥接之后,这部分功能也丧失了。
软路由能解决吗?当然,而且方案有很多种。可以采用防火墙限制上网设备,但是为了追求简单干脆,我用另外一种也就是 DHCP 分发的手段限制可连接 WIFI 的上网设备。如果 DHCP 不分发 IP,那么 WIFI 自然而然就连不上了。
我们按照下图的步骤,依次打开【网络】->【接口】->【LAN】->【修改】
下划到底部,打开【高级设置】,取消【动态 DHCP】,如图:
取消之后,所有的设备只有进行静态登记之后才能连上 WIFI。我们通过下图:
【网络】->【DHCP/DNS】划到【静态地址分配】,添加 MAC 地址及 IP 即可。
在线用户
我们知道硬路由有一个功能就是可以实时显示在线设备,而软路由实现此功能需要另外一个插件:【onliner】
为了避免缺少依赖,一定要下载 all.ipk 版。
DNS 分流
目前的代理方案是使用 OpenClash 的 Fake-IP 增强模式,并开启【绕过中国大陆 IP】,这也是我一直以来常用的方式。那这有什么弊端呢?
OpenClash
首先要明白的是,该方案存在 DNS 污染吗?
回答这个问题前,我们需要了解 DNS 在 OpenClash 中的应用场景:
- 解析域名,判断是否命中 IP 类规则
- 进行直连(DIRECT)时,解析域名与远端连接
- 作为一个 DNS 调度服务器
通过以上应用分析,要想实现 DNS 污染,需要符合以下几种情况:
- 域名类规则没有命中
- 命中 IP 类或 MATCH 规则且直连
例 1
1 | Rule: |
我们看下以上例子,如果 IP 命中 US,那么走代理,否则走直连。此时如果访问 google.com 由于规则中并没有基于域名的规则,那么就需要进行 DNS 解析,此时就会得到一个经过污染了的保留 IP, 而该保留 IP 并非美国 IP,所以走直连。也就是说以上配置存在 DNS 污染问题。
例 2
1 | Rule: |
以上配置可以避免大部分的污染问题,因为为了保证国内地址的可访问性,一般是很少将国外的域名污染成国内的 IP。但是,如果呢?我们可以在 IP 规则之前加上域名规则:
1 | Rule: |
这样一来就可以解决域名污染的问题了。
DNS 解析
OpenClash 提供了 nameserver 及 fallback 来配置 DNS。我们看下具体的流程:
1、从 nameserver 和 fallback 里的 DNS 进行并发请求,并且选取 nameserver 中最先响应的结果作为基准; 2、使用 GEOIP 判断此 IP 的所属区域,如果属于国内(CN)或保留地址则直接响应给客户端; 3、其他情况则把 fallback 中的结果响应给客户端;
我们以例 2 中的规则为例子并以 Fake-IP 模式进行说明:
1 | Rule: |
当客户端发送对 google.com 的请求时,会被 OpenClash 劫持然后返回一个 Fake-IP 并做好与域名的绑定。客户端拿到 Fake-IP 并对其进行请求,该请求再次被 OpenClash 拦截, 并根据绑定关系拿到该 Fake-IP 对应的 google.com。然后根据规则查找,发现没有基于域名的规则,而仅有的 IP 规则并没有 no-resolve 选项。那么就需要对 google.com 进行 DNS 解析。 OpenClash 会对 nameserver 及 fallback 中的 DNS 进行并发请求。由于是对外请求的,那么该 DNS 查询就会被 DNS 服务器主动污染或者运营商劫持。出现这个问题我们一般是更换 DNS 以及 将 fallback 指向 国外 DNS。但是对于运营商提供的 DNS 我们就无能为力了。此时 OpenClash 就会拿到 nameserver、fallback 双方解析出来的 IP。如果该 IP 是国内的,那么就采用该 IP 进行规则判断,如果 该 IP 是国外的,那么就采用 fallback 的结果进行规则判断。如果是直连模式,那么就直接用解析出来的 IP 发送请求;如果是代理模式,就将请求域名发送给代理服务器,由代理 服务器完成 DNS 解析。
通过以上分析,其实我们发现,以上配置其实能够解决绝大部分的污染问题,对于一般的用户也基本够用了。但是如果仔细推敲其实这里是有问题的,Fake-IP 模式下的 OpenClash 对于 DNS 的解析仅仅 用于代理规则判断,如果是直连模式时才会进行实际的请求。那么解析时,由于是 nameserver、fallback 并发请求,如果 nameserver 配置为国内 DNS 时,对于国外域名的请求,国内 DNS 就会有 解析记录,此时就会涉及到隐私安全问题。那么你可能就会想,直接把 nameserver 配置为国外 DNS 不就行了,那么国内域名的解析就有问题了。
另外,对于 Fake-IP,有些客户端就会表现出异样的行为,比如我那台领势路由器就会亮红灯。另一方面,我们希望国内的域名返回真实的 IP,只有需要代理的域名返回 Fake-IP(不要说通过 Fake-IP-Filter 实现)。
对于以上问题,总结一下我们的需求: 1、对于国内的域名走国内 DNS 解析并返回真实 IP。 2、对于国外的域名走国外解析,跳过国内解析过程,避免留下印记。 3、对于国内的请求直接跳过 OpenClash 的处理,一方面加快响应,另一方面避免由于 OpenClash 崩溃导致所有设备断网。
如果你也有以上需求,那么我们可以继续往下看了!
MosDNS
为了实现以上目标,我们需要一款 DNS 分流软件,它就是 MosDNS。
我们通过以下命令进行安装,当前版本为 MosDNS v5.1.3:
- 先升级必要工具:
1 | opkg update |
- 执行安装脚本:
1 | sh -c "$(curl -ksS https://raw.githubusercontent.com/sbwml/luci-app-mosdns/v5/install.sh)" |
安装成功之后,你会在【服务】->【MosDNS】找到。然后按照如图所示,关闭转发,打开自定义配置文件:
我们添加以下配置文件:
1 | log: |
MosDNS 选用的是 V5 的版本,如果你用的是 V4,那么两者配置文件不通用,这个要注意!
配置文件
这里不会对 MosDNS 本身做详细的介绍,如果你想了解更多关于 MosDNS 的内容,请移步到 【wiki】;
DNS
DNS 部分,我们添加了 forward_local 跟 forward_remote 两个 DNS 插件,如下:
1 | # 国内 DNS 服务器 |
我们将国内的 DNS 解析转发到 forward_local,将国外的 DNS 解析转移到 forward_remote。forward_local 中,我们配置了 3 个 DNS 地址,分别是本机、阿里、腾讯,为什么添加本机呢?其实本机 DNS 由 dnsmasq 负责解析,而 dnsmasq 会拿到运营商分配的地址,通常对本地 DNS 解析有 buff 加成。3 个 DNS 并发请求,取最快的作为响应。再补充一点,dnsmasq 监听的是 53 端口,MosDNS 监听的是 5335,所以 MosDNS 并不会影响 dnsmasq。那么你可能会有疑问,DNS 解析是如何被劫持到非标准端口 5335 的 MosDNS 的,不急,我们继续往下看。
forward_remote 负责国外 DNS 的解析,这里我们将它全权交由 OpenClash 处理。也就是说由 OpenClash 解析的请求都是要经过代理转发的。问题来了,经过国内 DNS 解析的请求还会被 OpenClash 转发吗?会!
其实对于请求劫持,OpenClash 做的还远不止这些,不过这是 OpenClash 专题要讲的事情。
执行序列
对于 local_ptr、blocklist 两个列表、qtype 12(PTR)、qtype 65(针对于 iOS 14)类型的解析会以 reject 3 也就是 NXDOMAIN 响应。对于 whitelist 白名单、geosite_cn 国内域名的请求先查询缓存,如果缓存不存在再进行国内 DNS 的解析(成功解析之后自动加入缓存)。这里我们并没有对 DDNS 及代理的解析做缓存,DDNS 由于会时常变更,并且它的访问频率比较低,所以也没有太大的必要加入缓存。而代理域名的解析由于 OpenClash 会直接返回 Fake-IP,OpenClash 自身就会做 Fake-IP 的持久化,所以就没有必要再做一次缓存了。毕竟,如果 Fake-IP 过期或者清理之后,缓存还生效,那么上网就会出现问题,并且 Fake-IP 并 不慢。
如果一个域名未匹配灰名单、国内规则,也未匹配 geosite_no_cn 属于三无域名,那么就要执行 fallback。首先会进行国内 DNS 解析拿到 IP,然后对该 IP 进行判断,如果符合国内则进行响应,否则丢弃交由国外 DNS 解析。
名单
整个配置文件已经把模板定好,我们可以直接通过面板来灵活定制我们的规则:
白名单:
白名单中的域名会走国内 DNS 解析。
黑名单:
用于屏蔽某个域名的解析。
灰名单:
需要走国外 DNS 解析的域名。
DDNS 域名:
如果存在 DDNS 域名,可以添加进来,由国内 DNS 解析。
最后一项就是需要打开数据库的定期更新:
AdGuard Home
上文中,我们提到过 MosDNS 运行在 5335 端口,那么负责劫持 DNS 到 MosDNS 的就是 AdGuard Home 了。
AdGuard Home
AdGuard Home 将流量挟持到自己的 553 端口,我们可以通过配置 AdGuard Home 的【客户端配置】来指定接入的设备是否需要代理。
通过【设置】->【DNS 设置】指定它的上游 DNS 服务器为:
127.0.0.1:5335
,也就是 MosDNS 的监听端口。通过【服务】->【AdGuard Home】,重定向 53 端口到 AdGuardHome 来劫持流量到自身。如下图:
AdGuard Home 基本设置就完成了,你也可以设置广告规则、敏感内容过滤等,本文就不对其展开了。
OpenClash
基础配置
OpenClash 就是我们目标的最后一环节,我们需要按照如下步骤做好基础配置:
1、取消【绕过中国大陆 IP】
2、本地 DNS 劫持停用
3、勾选【自定义上游 DNS 服务器】,取消【追加上游 DN】、【追加默认 DNS】勾选,如下图:
4、移除 FallBack、default-NameServer 下的内容,并为 NameServer 添加国外 DNS 解析:
之所以要取消【追加上游 DNS】、【追加默认 DNS】是避免 OpenClash 将运营商分配的 DNS 添加进来,因为进入到 OpenClash 的流量一定是要走代理的,这些域名就不能被运营商的 DNS 解析。当你设置好之后一定要先【保存】,再【应用】。
否则配置不会生效,在应用之后,一定要去你的配置文件里看看最终生成的 DNS 是不是你指定的。
对于 Rule 部分,你可以配置成以下内容:
1 | - "GEOIP,LAN,🇨🇳 China" |
如果命中大陆或者本地的 IP 那么走直连,其余走代理。
请求劫持
到目前为止,好像该配的都配完了,国内的域名由 MosDNS 处理,国外的域名解析由 OpenClash 转发给 8.8.8.8 完成解析。此时其实只是完成了客户端到软路由的 DNS 解析,客户端拿到 DNS 解析记录之后会发送请求,如果只是以上的简单配置,那么流量会再次被 OpenClash 劫持。以下防火墙规则验证这一点:
我们看到源地址是 0.0.0.0/0 目标地址是 198.18.0.0/16、0.0.0.0/0 的流量都会转发到 7892 端口,也就是 OpenClash 代理端口。目标 198.18.0.0/16 为 Fake-IP 地址段,当客户端发起对该段的请求时,说明是需要代理的,所以这条规则没问题,但是下面一条规则就有问题了,我们需要将它移除:
1 | iptables -t nat -D openclash -p tcp -s 0.0.0.0/0 -d 0.0.0.0/0 -j REDIRECT --to-ports 7892 |
这样对于目标非 Fake-IP 的流量就不会流经 OpenClash 内核了。但是这会引发另外一个问题,我们知道有些 App 会直接发起对 IP 的请求,比如:Telegram,Netflix 等。该防火墙规则会导致这些流量直接走直连从而影响使用。怎么解决这个问题呢?
方法有二,我们可以根据它们的 CIDR 来选择流量劫持,但是不靠谱,因为还需要定期维护,一但对方更换,那么又会出现问题,比较被动。这里我们选择另外一个方案解决——利用 china ipset:
1 | iptables -t nat -A openclash -p tcp -m set ! --match-set china_ip_route dst -j REDIRECT --to-ports 7892 |
我们将目标地址非 China 的流量也转发到 OpenClash,就可以解决以上问题。还会有问题吗?答案是肯定的,除了客户端的请求外,宿主机也会产生请求,所以我们不得对这部分流量加以控制:
1、避免宿主机自身的出口流量全部走代理
1 | iptables -t nat -D openclash_output -p tcp -m owner ! --uid-owner 65534 -j REDIRECT --to-ports 7892 |
2、对于宿主机自身的 TCP 流量除了 Fake IP 外,对于非中国 IP 的流量且非 OpenClash 的出口流量也重定向到代理
1 | iptables -t nat -A openclash_output -p tcp -m set ! --match-set china_ip_route dst -m owner ! --uid-owner 65534 -j REDIRECT --to-ports 7892 |
3、避免来自中国的 UDP 流量经过代理
1 | iptables -t mangle -D openclash -p udp -j TPROXY --tproxy-mark 0x162/0xffffffff --on-port 7895 |
4、重定向非中国的 UDP 流量到代理
1 | iptables -t mangle -A openclash -p udp -m set ! --match-set china_ip_route dst -j TPROXY --tproxy-mark 0x162/0xffffffff --on-port 7895 |
以上关键的地方在于 china_ip_route
,它包含了中国的 IP 段,所有根据 IP 的规则都要经过它来判断,那么如何生成 china_ip_route
便成了一个问题。其实 OpenClash 有自动更新 GEO 数据库及中国大陆白名单的定时任务,也就是说它会生成我们需要的 ipset 文件。
所以我们要做的就是添加以下命令,用于将 china_ip_route
文件转换成对应的 ipset:
1 | ipset -! flush china_ip_route |
最终我们将以下内容添加到【开发者选项】中:
1 | !/bin/sh |
保存、应用!
尾声
以上就是本次家庭网络改造的流程,最终可以实现,家中所有接入设备的实时管控。可以清楚的了解到每台设备的上网信息并对其加以限制。同时对于国内网的网站访问也可以做到精确分流,提升响应速度。当然这套方案也并不完美,由于国内域名跳过了 OpenClash 也就意味着失去了对它们的限制。不过可以从 DNS 维度对它们进行屏蔽。如果是基于 IP 的,那就只能通过防火墙加以限制了。