平时的工作生活中,访问一些国外网站是必不可少的。但在各个设备上配置代理客户端又过于繁琐,并且有可能有一些设备并没有对应架构的客户端可用。
因此可以考虑将树莓派配置为代理网关,设备的流量由树莓派负责代理,并将国内外流量进行分流以优化体验。

为什么不直接将无线路由器刷成 openwrt?

  • 无线路由器一般性能比较羸弱,跑代理软件容易出现性能问题
  • 树莓派自带系统为 debian,比较大众,容易操作、debug
  • 无线路由器刷了 openwrt 之后,需要较多的相关知识,一旦出现问题之后难以诊断,直接不能上网了;而将树莓派作为网关的方案,即使树莓派出现问题,依旧不影响基本的网络使用

内网知识基础普及

DNS

DNS 负责解析域名,比如将 www.baidu.com 解析为对应的 v4/v6 地址。这样数据包才能在网络中流动

以下为 dig 命令向 114.114.114.114 请求 www.baidu.com 的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
➜  ~ dig www.baidu.com @114.114.114.114

; <<>> DiG 9.10.6 <<>> www.baidu.com @114.114.114.114
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62279
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;www.baidu.com. IN A

;; ANSWER SECTION:
www.baidu.com. 67 IN A 120.232.145.144
www.baidu.com. 67 IN A 120.232.145.185

;; Query time: 58 msec
;; SERVER: 114.114.114.114#53(114.114.114.114)
;; WHEN: Thu Oct 05 11:34:29 CST 2023
;; MSG SIZE rcvd: 74

dns 请求是 udp 连接,会向服务器的 53 端口发送数据包

DHCP

DHCP 全称 Dynamic Host Configuration Protocol,顾名思义,这是一个负责配置设备网络配置的协议。监听 udp 67 端口

当设备通过网口或者 wifi 接入网络时,路由器会通过这个协议对设备进行配置,核心的配置有

  • IP 地址
  • 子网掩码
  • 默认网关
  • dns 服务器

这 4 项配置是必不可少的。

  • IP 地址是数据包的载体
  • 子网掩码区分了内网和外网,内网请求会直接请求对应的 mac 地址并发送
  • 默认网关:设备会将外网数据直接发向网关
  • dns 服务器:设备会向 dns 服务器的 53 端口发送查询请求,获取域名对应的地址;如果 dns 服务器配置错误,会出现直接访问 IP 地址能访问,域名却不能访问的情况

家用路由器

上文中我们提到了 DHCP、网关、dns,一般的家用路由器,搭载了所有的这些功能

  1. 设备连接时,路由器的 DHCP 服务给设备分配 IP、子网掩码、默认网关和 dns。其中默认网关和 dns 基本也是路由器的 lan 地址
  2. 设备访问域名时,向路由器发起 dns 查询,获得对应地址
  3. 设备将数据包发向网关(也就是路由器)

但 DHCP、dns,它们本质上是一个软件服务;网关负责路由流量,基本也可以视为一个软件服务。这三者可以部署在不同的设备上。因此,完全可以将 DHCP、网关、dns 独立出来,与家用的无线路由器分开

配置树莓派为网关

各个操作系统基本都支持手动设置 ipv4 地址以及 dns,但想要用树莓派作为网关,设备必须与树莓派在同一个子网中。

树莓派侧配置

树莓派默认为 DHCP 客户端,当接入上级网络 lan 口时,会自动获取地址,但是这个地址是随机的,不利于之后的操作。
因此我们可以通过修改 /etc/dhcpcd.conf eth0 的配置,确保树莓派地址是固定的
以子网为 192.168.1.1/24,树莓派地址为 192.168.1.2 为例

将 eth0 的配置为

1
2
3
4
interface eth0
static ip_address=192.168.1.2/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1

修改成功之后可以重启设备,使配置生效。并且检测树莓派是否能正常联网

nftables 比 iptables 更新,所以本文的网络路由配置都采用 nftables。首先确保 nftables 是开启的状态

1
2
sudo systemctl enable nftables
sudo systemctl start nftables

开启数据包转发(该条命令重启后会失效,因为这只是用来测试,没必要持久化)

1
sudo sysctl -w net.ipv4.ip_forward=1

配置 nftables 为 snat(这几条命令重启后也会失效,同样不需要持久化)

1
2
3
sudo nft add table nat
sudo nft 'add chain nat postrouting { type nat hook postrouting priority 100; }'
sudo nft add rule nat postrouting oifname eth0 masquerade

设备侧配置

在设备的 wifi 配置页面可以手动配置

  • IP:192.168.1.111(只要在子网即可,不与其他设备冲突就可以)
  • 子网掩码:255.255.255.0
  • 默认网关:192.168.1.2(配置为树莓派的 ip 地址)
  • dns 配置为 114.114.114.114

验证

这时,可以在设备上打开网页或者浏览视频,如果没什么问题的话证明以上配置都是正确的。

设备侧通过修改网络相关配置,将网络数据都发送到树莓派,由树莓派进行处理。目前的配置只是直接转发,接下来只要安装代理软件,支持分流,国内的流量直接发送,国外的流量通过代理发送。
这样就完美的达到了我们的目标。

并且这样做还有一个好处,需要访问外网的设备可以手动修改网络配置,不需要访问外网的设备不需要做任何配置,正常联网就行,不会影响连接同一无线路由器下的其他用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 +-----------------+
| wireless router |
| WAN 192.168.1.1 |
+--------+--------+
^
|
|
+--------+--------+
| raspberry pi |
| LAN 192.168.1.2 |
+--------+--------+
^
|
|
+---------+---------+
| phone |
| LAN 192.168.1.111 |
+-------------------+

配置代理软件

上一步中,设备侧已经把树莓派配置为了网关。我们只需要在树莓派上安装代理软件,对网络数据进行代理。这样对于设备来说是完全透明的。

clash 是一个很好用的代理软件

安装 clash

clash 目前没有分发到包管理器中,可以直接下载二进制文件。
https://github.com/Dreamacro/clash/releases 下载最新的二进制文件。树莓派 4B 的 cpu 架构为 armv8,可以下载 clash-linux-arm64 开头的最新版本。例如:

1
2
3
4
5
6
wget https://github.com/Dreamacro/clash/releases/download/v1.18.0/clash-linux-arm64-v1.18.0.gz
gzip -d clash-linux-arm64-v1.18.0.gz

sudo mkdir /etc/clash
sudo mv clash-linux-arm64-v1.18.0 /usr/local/bin/clash
sudo chmod +x /usr/local/bin/clash

创建专用的 clash 用户(用于下一步,以 clash 用户的身份启动 clash)

1
2
3
sudo useradd clash

sudo chown -R clash:clash /etc/clash

添加 Systemd 配置文件 /etc/systemd/system/clash.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=Clash daemon, A rule-based proxy in Go.
After=network.target

[Service]
Type=simple
User=clash
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
Restart=always
ExecStart=/usr/local/bin/clash -d /etc/clash

[Install]
WantedBy=multi-user.target

PS:其中 CapabilityBoundingSet AmbientCapabilities 两行是为了让 clash 这个普通用户也有高级网络权限,比如 bind 到小于 1024 的端口(本文需要监听 53 端口)

1
2
3
sudo systemctl daemon-reload
sudo systemctl enable clash
sudo systemctl start clash

配置 clash

下载 Country.mmdb/etc/clash,这是一个记录 ip 所在国家的数据库,可以用于我们之后的分流
/etc/clash 下添加 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
mixed-port: 7890
redir-port: 7892
tproxy-port: 7893
allow-lan: true
mode: rule
ipv6: true
dns:
enable: true
ipv6: true
listen: 0.0.0.0:53
enhanced-mode: fake-ip
fake-ip-range: 198.18.0.1/16
default-nameserver:
- 114.114.114.114
nameserver:
- https://dns.alidns.com/dns-query
- https://doh.pub/dns-query
- https://1.1.1.1/dns-query
proxies:
- name: "proxy"
...
rules:
- GEOIP,CN,DIRECT
- MATCH,proxy

详细的配置可以查看 clash 文档,其中 rules 的配置是按顺序匹配的,也就是如果 IP 认定是在国内的话,直接连接;
兜底的规则会把其他流量送到名为 proxy 的节点中(已在 proxies 中定义)。

dns 的配置会监听 53 端口应答 dns 查询,这里用了 fake-ip 的模式,简单来说就是遇到 dns 查询,都以 198.18.0.1/16 内的 ip 应答,具体可以查看 文档

nameserver 的字段用了几个 doh 的 dns 服务器,这样能稍微保护一些隐私。如果觉得慢的,并且不太在意隐私的话可以加上 - 114.114.114.114

PS:配置完成之后可以重启 clash 使配置生效 sudo systemctl restart clash

配置网络路由

1
2
3
4
5
+---------------------+     +-------------------+
| dns:53 <-----+ phone |
| clash | | LAN 192.168.1.111 |
| tproxy:7893 <-----+ |
+---------------------+ +-------------------+

至此,clash 监听本机 53 和 7893 端口,53 端口负责 dns 请求,7893 负责接收流量。我们只需要配置 nftables 以及路由将外部以及本机流量代理过去即可

nftables

添加文件 /etc/nftables.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/sbin/nft -f

flush ruleset

define RESERVED_IP = {
127.0.0.0/8,
192.168.0.0/16,
172.16.0.0/12
}

table ip clash {
chain prerouting {
type filter hook prerouting priority mangle;
ip daddr $RESERVED_IP return
ip protocol { tcp, udp } tproxy to 127.0.0.1:7893 meta mark set 555
}
chain output {
type route hook output priority mangle;
ip daddr $RESERVED_IP return
meta skuid clash return
ip protocol { tcp, udp } meta mark set 555
}
}

其中 chain prerouting 负责将外部流量路由到 7893 端口
chain output 负责将本机流量路由到 7893 端口,这样树莓派本身也能访问外网了。
meta skuid clash return 这一行与 Systemd 配置中 User=clash 对应。clash 用户启动的 clash 进程的流量应该直接放行,不应该再路由回本身造成永远循环
RESERVED_IP 里有必要的话可以把更多保留地址加进去

策略路由及路由表

/etc/rc.local 中添加以下配置,这样在启动的时候就会自动执行

1
2
ip rule add fwmark 555 lookup 555
ip route add local 0.0.0.0/0 dev lo table 555

这两行配置与上面的配置照应,是为了在路由阶段将流量路由到 clash

配置 dns 为树莓派

设备侧:修改 dns 配置为树莓派的 IP 地址,本例中即为 192.168.1.2

树莓派本身:树莓派本身的 dns 也需要请求 clash,可以修改 /etc/resolvconf.conf

1
name_servers=127.0.0.1

到此为止配置完成,可以重启树莓派使配置生效

验证

如果 dns 配置无误,不管什么域名,返回的 ip 应该都在 fake-ip-range 范围内,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
➜  ~ dig baidu.com

; <<>> DiG 9.16.44-Debian <<>> baidu.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22254
;; flags: qr aa rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; MBZ: 0x0001, udp: 4096
; COOKIE: eec74fc20bfedcdb (echoed)
;; QUESTION SECTION:
;baidu.com. IN A

;; ANSWER SECTION:
baidu.com. 1 IN A 198.18.1.59

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Mon Oct 09 13:30:20 CST 2023
;; MSG SIZE rcvd: 66

接下来访问一些国外网站,并且可以实时查看 clash 的日志 journalctl -u clash -f 看是否成功代理