2017 年的时候写过关于 x86 软路由透明代理构建方案,当时的方案是基于 overture + redsocks + shadowsocks 做的。当时用的还挺好的。
但是随着时间的推移,整个社区在变化,方案也在变化。主要遇到的几个想要迁移修改的想法,一个是 redsocks 比较老,bug 多,不怎么稳定,而且不维护了。虽然有人自己改了 redsocks2 的方案,但也不那么完美吧。二个是 shadowsocks 的问题,这个众所周知了,就不描述背景了。三个是 clash 的崛起,确实挺好用,主要是一体化解决问题,还有节点维护管理能力。所以起了换 clash 的想法。
所以,目前新的方案是 overture + clash。其实 clash 自带 dns server,但是我觉得不够稳,也不够好用,还是继续用了 overture。部署新方案的过程中,也踩了几个坑,下面主要是想记录一下这几个坑。
基本架构
网络配置:
双网卡 + NAT(iptable)+ DHCP
DNS 解析防污染:
overture(解析)+ dnsmasq(缓存)
代理:
clash
透明代理:
iptable + ipset(国内 IP 段)
网络配置
这部分不再赘述,可以继续参考 x86 软路由透明代理构建方案 中的 网络配置 部分。
DNS 解析防污染
这部分和 x86 软路由透明代理构建方案 中的 DNS 解析防污染 部分一样,只是升级了一下 overture 的配置文件。
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
| { "BindAddress": ":54", "DebugHTTPAddress": "127.0.0.1:5555", "PrimaryDNS": [ { "Name": "DNSPod", "Address": "119.29.29.29:53", "Protocol": "udp", "SOCKS5Address": "", "Timeout": 6, "EDNSClientSubnet": { "Policy": "disable", "ExternalIP": "", "NoCookie": true } } ], "AlternativeDNS": [ { "Name": "cloudflare", "Address": "1.1.1.1:53", "Protocol": "tcp", "SOCKS5Address": "127.0.0.1:10080", "Timeout": 6, "EDNSClientSubnet": { "Policy": "disable", "ExternalIP": "" } }, { "Name": "Google", "Address": "8.8.8.8:53", "Protocol": "tcp", "SOCKS5Address": "127.0.0.1:10080", "Timeout": 6, "EDNSClientSubnet": { "Policy": "disable", "ExternalIP": "" } } ], "OnlyPrimaryDNS": false, "IPv6UseAlternativeDNS": false, "RedirectIPv6Record": false, "WhenPrimaryDNSAnswerNoneUse": "PrimaryDNS", "IPNetworkFile": { "Primary": "/etc/overture/ip_network_sample", "Alternative": "/etc/overture/ip_network_alternative_sample" }, "DomainFile": { "Primary": "/etc/overture/domain_sample", "Alternative": "/etc/overture/domain_alternative_sample" }, "HostsFile": "/etc/overture/hosts_sample", "MinimumTTL": 0, "DomainTTLFile" : "/etc/overture/domain_ttl_sample", "CacheSize" : 0, "RejectQType": [255] }
|
主要是 DNS 上游解析走 tcp,并且使用 SOCKS5Address
将 DNS 解析走代理。当然也可以用 tcp-tls 类型,就是 DNS over TLS,和 tcp 走代理差别不大。
代理
clash
安装 clash,就是从 github 上下载已经编译好的二进制包。
1 2 3 4 5
| mkdir -p /opt/clash && cd /opt/clash wget https://github.com/Dreamacro/clash/releases/download/v0.17.1/clash-linux-amd64-v0.17.1.gz gzip -d clash-linux-amd64-v0.17.1.gz mv clash-linux-amd64-v0.14.0 clash chmod +x clash
|
一个大坑是,clash 的 direct 规则出来的请求,到下面的 iptable 规则的时候,会被重新 redirect 回来给 clash,形成回环。这种时候,socket fd 会爆掉。
所以解决方案是,给 clash 单独建一个新的 user,在 iptable 加个针对 user 的规则。主要是参考了这里的方案。通过 adduser clash
新建一个叫 clash 的用户,创建了其主目录 /home/clash/
。
通过 id clash
得知其 uid 是 1001
。下面 iptable 规则需要用。
下面的配置文件可以放在 /home/clash/.config/clash/
目录下。
配置文件,前若干行
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
| port: 8888 socks-port: 10080 redir-port: 12345 allow-lan: true mode: Rule log-level: warning external-controller: '0.0.0.0:6170' secret: '' cfw-latency-url: 'http://www.gstatic.com/generate_204'
dns: enable: false ipv6: false listen: 0.0.0.0:54
nameserver: - 1.2.4.8 - 114.114.114.114 - 223.5.5.5
fallback: - tls://1.0.0.1:853 - tls://dns.google:853
Proxy: 以下省略
|
主要是说明一下我用的几个端口号,方便后面使用。
其中的坑一,allow-lan: true
必须为 true。
clash 的启动,我还是习惯用 suprvisor,习惯了。用 github 上 clash 自己用的 pm2 也行。或者自己配置 systemd 都行。只要确定启动 clash 进程的时候,必须使用 clash
这个用户。在 supervisor 里的配置是加一个 user=clash
的配置。
1 2 3 4 5
| [program:clash] user=clash priority=1 command=/usr/local/bin/clash -d /home/clash/.config/clash/ autorestart=true
|
上面路径自行替换。
透明代理
这部分和 x86 软路由透明代理构建方案 中的 透明代理 部分基本一样。只是加了几条规则。
最终的 iptable 表如下:
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
| Chain PREROUTING (policy ACCEPT) target prot opt source destination SHADOWSOCKS tcp -- anywhere anywhere
Chain INPUT (policy ACCEPT) target prot opt source destination
Chain OUTPUT (policy ACCEPT) target prot opt source destination RETURN all -- anywhere anywhere owner UID match clash SHADOWSOCKS tcp -- anywhere anywhere
Chain POSTROUTING (policy ACCEPT) target prot opt source destination MASQUERADE all -- anywhere anywhere
Chain SHADOWSOCKS (2 references) target prot opt source destination RETURN tcp -- anywhere anywhere tcp dpt:10144 RETURN udp -- anywhere anywhere udp dpts:4096:65535 RETURN all -- anywhere 0.0.0.0/8 RETURN all -- anywhere 10.0.0.0/8 RETURN all -- anywhere 127.0.0.0/8 RETURN all -- anywhere link-local/16 RETURN all -- anywhere 172.16.0.0/12 RETURN all -- anywhere 192.168.0.0/16 RETURN all -- anywhere base-address.mcast.net/4 RETURN all -- anywhere 240.0.0.0/4 RETURN tcp -- anywhere anywhere match-set chnroute dst REDIRECT tcp -- anywhere anywhere redir ports 12345
|
加了两条规则
1 2
| iptables -t nat -A OUTPUT -m owner --uid-owner 1001 -j RETURN iptables -t nat -A OUTPUT -p tcp -j SHADOWSOCKS
|
第一条是避免形成回环,第二条是让网关本身可以翻墙。
然后还是用 iptables-persistent 持久化,基本就可以用了。
内核参数
这里我又被坑了一把。之前不知道啥时候,内核参数里加了一个 net.ipv4.tcp_tw_recycle = 1
,然后就坑了。
导致的问题是,设备发送的 tcp 的 syn 包,被网关默默的 drop 掉了。通过 tcpdump 发现,发送出去的 tcp syn,但是没有收到任何回复。局域网内会间歇性的无法翻墙,http connect 经常超时,请求并没有走到 clash,clash 没有相应的日志。最终在 这里 的第三部分,关于 net.ipv4.tcp_tw_recycle
部分,把 1 改回 0 就好了。
网上看了一下,这个内核参数,不要乱改,基本没啥用,改了有问题,到 linux 4.14 还是什么版本来着,这个参数直接被干掉了。