家用网络透明代理实践

所需设备

  1. 主路由选择以下三种路由设备之一,基础版面向的普通用户(一般买的家用无线路由器都支持),进阶版与linux系列路由器可定制一些高级功能,并且能在旁路由不稳定的情况下,保证家中不断网。
  • 基础版(支持关闭DHCP功能的路由器均可)
  • 进阶版(能修改路由表、配置DHCP Option的路由器均可)
    • Openwrt/Padavan/RouterOS 等底层是linux的路由器
  1. 旁路由选择性能比较好的设备,安装linux系统或以linux为底层的路由器系统。
  • 基础版:斐讯N1、树莓派等微型计算机
  • 进阶版:x86软路由,虚拟机等

网络拓扑结构

旁路由设备与普通电脑、手机、电视设备一样接入到主路由的LAN网络中。
旁路由建议网线连接。

设计目标

  1. 访问国内IPv4/IPv6地址段直连,其余流量走旁路由设备
  2. 旁路由设备运行Clash TUN模式,由TUN网卡接管流量,交由Clash进一步分流处理。
  3. 双栈域名解析,IPv4优先(IPv6路由太差,不够稳定)
  4. 根据用户设备分为两类,一类正常上网,一类走旁路由设备分流代理。
  5. 不需要在每台设备挂代理与配置静态IP,客户端零配置,直接接入家庭网络即可。

运营商光猫与主路由默认配置

  1. 光猫改桥接
  2. 主路由PPPoE拨号

方案一(主路由为基础版)

因为主路由的功能有限,尤其是网上买的普通家用无线路由器。
这种情况下,如果不更换主路由就需要由旁路由设备来代管一些功能。
要求旁路由设备与主路由设备同样稳定,避免影响家里其他用户正常上网。

主路由配置

关闭DHCP即可,其他参照默认配置。

旁路由配置

旁路由需要开启路由转发。

DHCP配置(dnsmasq)

设计目标

根据用户设备分为两类,一类走主路由,主路由提供的DNS正常上网,一类走旁路由设备分流代理。

实施方法

对于需要透明代理的设备下发以下DHCP配置:
DHCP Options 3(默认网关):旁路由IP地址
DHCP Options 5(DNS):旁路由IP地址
对于不需要透明代理的设备默认配置即可,即网关为主路由,DNS为主路由或运营商下发的DNS。

那么问题来了,怎么做到针对不同的设备下发不同的DHCP配置呢?
这里可以通过配置dnsmasq来实现,参考底部参考文献[2]的文章
大致是通过MAC地址或者IP段来匹配,从而下发不同的DHCP配置。

缺点

  1. 需要提前搜集用户设备的MAC地址,如果用户设备的MAC地址发生变化,那么就会导致DHCP配置失效,需要重新配置。
  2. 多网卡设备,像笔记本电脑既可以插网线,又能连wifi,这是两个独立的网卡,因此两个mac地址都要配置。
  3. 不够安全,知道套路的人,可以自己配置静态ip使用代理。
  4. 不够灵活,每次修改DHCP配置,都需要等待用户设备重新获取IP地址,才能生效。

DNS服务器配置(mosdns)

mosdns的介绍和二进制可执行文件,参考项目官方地址[3].

设计目标

  1. 针对解析结果为国内IP的域名,转发至国内DNS,解析结果为海外IP的域名,转发至Clash DNS。
  2. 针对DNS解析结果分流,解析结果为国内IP的正常返回,解析结果为海外IP的返回fake-ip。
  3. 针对纯IPv6的域名解析,直接返回。(机场一般不支持v6,v6直连最好)

实施方法

我写了一份配置文件,参考了这个配置[4],有以下几点需要注意:

  1. forward_remote部分填写Clash的DNS地址。
  2. china_ip中的文件自己从网上搜索[6],主要是排除国内IP段与本地网络。是否排除本地网段[7]看自己需求,GFW有将IP污染到保留地址的先例。
  3. 纯IPv6的域名解析,我希望能直接直连,因此专门增加了规则。不需要可以删除。
  4. 转发至国内的DNS,建议填写国内的加密DNS,避免因配置防火墙53端口劫持的时候污染到结果。
  5. 添加fallback插件,当forward_remote挂了,自动回滚到forward_local。
  6. 设置ttl为1,方便clash恢复时能尽快拿到fake-IP。
/etc/mosdns/config.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
log:
level: debug

plugins:
# 中国与保留IP
- tag: china_local_ip
type: ip_set
args:
ips:
- "192.168.0.0/16"
- "10.0.0.0/8"
- "172.16.0.0/12"
- "127.0.0.0/8"
- "100.64.0.0/10"
- "::1/128"
- "fc00::/7"
- "fe80::/10"
- "fd00::/8"
files:
- ./all_cn.txt
- ./all_cn_ipv6.txt

# 重定向域名
- tag: redirect
type: redirect
args:
rules:
- www.cnbeta.com www.cnbeta.com.cdn.cloudflare.net

# 转发至国内DNS,并发查询
- tag: forward_local
type: forward
args:
concurrent: 2
upstreams:
- addr: "https://223.5.5.5/dns-query"
idle_timeout: 86400
- addr: "https://120.53.53.53/dns-query"
idle_timeout: 86400

# 转发至国外DNS,并发查询
- tag: forward_remote
type: forward
args:
concurrent: 1
upstreams:
- addr: udp://198.18.0.1:1053

# 如果forward_remote挂了,可以自动回滚
- tag: "final"
type: fallback
args:
primary: forward_remote
secondary: forward_local
threshold: 250
always_standby: true

# 主运行序列
- tag: main_sequence
type: sequence
args:
- exec: $redirect

# 在执行查询前先检查查询域名是否匹配直连列表
- matches: qname &./direct-list.txt
exec: $forward_local
- matches: has_resp
exec: accept

- exec: prefer_ipv4

# 执行查询后匹配解析结果是中国或保留地址
- exec: $forward_local
- matches:
- "has_resp"
- "resp_ip $china_local_ip"
exec: accept

# 接受所有的IPv4 A记录为空的本地查询结果
- matches:
- "!has_wanted_ans"
- "qtype 1"
exec: accept

# 接受所有的IPv6 AAAA记录的本地查询结果
- matches:
- "has_wanted_ans"
- "qtype 28"
exec: accept

# fallback
- exec: $final
- exec: ttl 1


# 启动监听服务
- tag: udp_server
type: udp_server
args:
entry: main_sequence
listen: :53

- tag: tcp_server
type: tcp_server
args:
entry: main_sequence
listen: :53

Clash

参考Clash的官方项目[5],下载二进制文件并使用。注意下载Permium版本的。
配置文件参考项目的示例文件,根据自己的需求修改即可。
大体上就是增加dns的配置
TUN模式的auto-route需要关闭,原因是:auto-route会默认添加默认路由到TUN网卡。这样国内直连的IP就会走TUN网卡,被Clash接管。对于性能弱的设备不好,影响网速。
所以也就带来了个缺点:对于不用域名解析只用IP访问的软件,会连不上,像Telagram,所以我们要单独加静态路由,让Telegram的IP[8]走TUN网卡。

/etc/clash/config.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
mixed-port: 7890
socks-port: 7891
redir-port: 7892
tproxy-port: 7893
allow-lan: true
mode: rule
log-level: warning
external-controller: :9090
external-ui: ui
profile:
store-selected: true

dns:
enable: true
enhanced-mode: fake-ip
listen: :1053
nameserver:
- "https://223.5.5.5/dns-query"
- "https://120.53.53.53/dns-query"
fake-ip-filter:
- +.stun.*.*
- +.stun.*.*.*
- +.stun.*.*.*.*
- +.stun.*.*.*.*.*
- "*.n.n.srv.nintendo.net"
- +.stun.playstation.net
- xbox.*.*.microsoft.com
- "*.*.xboxlive.com"
- "*.msftncsi.com"
- "*.msftconnecttest.com"
- WORKGROUP

tun:
enable: true
stack: system
dns-hijack:
- tcp://8.8.8.8:53
- 8.8.8.8:1053
auto-detect-interface: true
auto-route: true # 根据需要开启


模拟访问过程

设备为透明代理用户

访问国内网站(百度)

  1. baidu.com的域名,发送查询请求给DHCP下发的DNS(旁路由)。
  2. mosdns查询国内公共DNS后,发现baidu.com是国内IP,直接返回此IP。
  3. 发送访问请求到baidu.com的真实IP,此IP不是局域网IP,将数据包发给网关(旁路由)。
  4. 旁路由收到数据包,检查本机路由表,此IP不属于TUN网段198.18.0.0/16,走默认路由即物理网卡(主路由)。
    全程没有经过Clash,但流量会从旁路由饶一下,最终旁路由还是会把数据包原封不动(指网络层以上)交给主路由。

访问海外网站(Google)

  1. google.com的域名,发送查询请求给DHCP下发的DNS(旁路由)。
  2. mosdns查询国内公共DNS后,发现google.com是双栈海外IP。
  3. mosdns查询Clash的DNS,获得Clash的fake-ip,IP属于TUN网段198.18.0.0/16
  4. Google域名是双栈IP,mosdns设置了ipv4_prefer,会忽略IPv6结果。避免系统拿到IPv6走物理网卡出。
  5. 发送访问请求到google.com的fake-ip,此IP不是局域网IP,将数据包发给网关(旁路由)。
  6. 旁路由收到数据包,检查本机路由表,此IP属于TUN网段198.18.0.0/16,走Clash的TUN网卡。
  7. Clash根据配置的规则,选择一个合适的代理或直连,发送数据包。
    流量经过clash,会经过设备CPU处理,代理也有加解密的部分,会比直连的转发性能差。

方案二(主路由为进阶版或类linux系统)

主路由配置

添加TUN网段198.18.0.0/16的静态路由,让TUN网段的流量走旁路由。
对于不适用域名解析,直接使用IP联网的应用,如:Telegram[8],需要进一步添加静态路由到旁路由。
DHCP下发的DNS根据需要,分两种情况:

  • 正常上网用户:运营商下发的DNS
  • 走透明代理用户:旁路由的IP
    实际上就是将旁路由的DHCP功能放到主路由上,旁路由只做透明代理。

旁路由配置

mosdns和Clash部分跟方案一一样。
因为主路由能够控制DHCP了,所以旁路由不在需要是DHCP服务器了。
对于不适用域名解析,直接使用IP联网的应用,如:Telegram,需要打开auto-route,让旁路由自动添加默认路由到Clash的TUN网卡。

模拟访问过程

设备为透明代理用户

访问国内网站(百度)

与方案一不同的地方是,拿到国内IP会走主路由直接出去,进一步减轻了旁路有的负担。
唯有拿到TUN网段198.18.0.0/16的IP才会走旁路由。(因为设置了静态路由)
全程没有旁路由,跟正常上网一模一样,缺点是依赖旁路有的DNS,即mosdns。
如果旁路由挂了,域名解析不了。

访问海外网站(Google)

与方案一不同的地方是,数据包会先到主路由,做一次路由选择,再到旁路由。

附录

systemd配置

Clash

/etc/systemd/system/clash.service
1
2
3
4
5
6
7
8
9
10
11
[Unit]
Description=Clash daemon, A rule-based proxy in Go.
After=network-online.target

[Service]
Type=simple
Restart=always
ExecStart=/usr/local/bin/clash -d /etc/clash

[Install]
WantedBy=multi-user.target

mosdns

/etc/systemd/system/mosdns.service
1
2
3
4
5
6
7
8
9
10
11
[Unit]
Description=MosDNS DNS Server
After=network-online.target

[Service]
Type=simple
Restart=always
ExecStart=/usr/local/bin/mosdns start -c /etc/mosdns/config.yaml -d /etc/mosdns

[Install]
WantedBy=multi-user.target

自动更新订阅并下载分流规则

/root/sub.sh
1
2
3
4
5
6
7
#!/bin/bash
mv /etc/clash/config.yaml /etc/clash/config.yaml.bak
wget "订阅地址" -O /etc/clash/config.yaml
wget "https://ispip.clang.cn/all_cn.txt" -O /etc/mosdns/all_cn.txt
wget "https://ispip.clang.cn/all_cn_ipv6.txt" -O /etc/mosdns/all_cn_ipv6.txt
systemctl restart clash
systemctl restart mosdns

查看Clash中WARN级别漏网之鱼日志

当设置漏网之鱼为直连时,可能会有一些域名无法正常解析,这时候就需要查看日志,找到这些域名,然后添加到自定义代理域名中。

/root/log.sh
1
2
3
#!/bin/bash
journalctl -u clash > /tmp/clash.log
grep -P 'WRN(?=.*漏网之鱼)' /tmp/clash.log | less

参考文献