基于树莓派4B构建支持透明代理的辅助路由器

基础环境

  • Raspberry Lite OS: 基于ARM V7,32位操作系统
  • dnsmasq: 自建域名服务器 + DHCP服务器, 192.168.0.8
  • hostapd: 提供AP服务器,SSID=xxx-PI, 192.168.11.x/24
  • clash: 作为v2ray节点的客户端,自带Dashboard

主路由器: 192.168.0.1

从路由器:
- eth0: 192.168.0.8
- waln0:192.168.11.1
- DHCP Range: 192.168.11.100-150
- DHCP Default Gateway: 192.168.11.1

Clash:
- 7890: http/https代理端口
- 7891: socks5代理端口
- 7892: 流量重定向端口
- 9090: Dashboard端口
- 53: 内建DNS端口(UDP)

Clash控制面板:http://192.168.0.8:9090/ui

安装步骤

一、安装树莓派并启动

Raspbian是树莓派的官方操作系统,是官基于Debain的深度定制,软件升级策略偏保守,强调稳定第一。
桌面版下载链接:http://downloads.raspberrypi.org/raspbian_latest
Lite 版(无桌面)下载链接:https://downloads.raspberrypi.org/raspbian_lite_latest

树莓派4B的BCM2711芯片采用四核Cortex A72架构,28nm工艺,主频1.5GHz,GPU 500MHz,因此是armv7的技术架构,32位操作系统。
虽然树莓派4B宣称已支持64位操作系统,但还在测试阶段;也可以采用Ubuntu、Centos等第三方系统,但软件依赖还是比较麻烦,因此本次安装采用的是Raspberry Lite OS,下载镜像文件是:2020-05-27-raspios-buster-lite-armhf.img

采用Etcher刻录CF卡,将写好的CF卡插入树莓派,连接有线网卡并加电后,就可以正常启动了。

为避免安全问题,Raspberry OS默认关闭了ssh登录。解决方法是:在烧录SD卡成功后,手工mount并在根目录下touch一个空文件,名称是ssh

树莓派首次启动时,默认采用DHCP方式获取IP地址,在接入路由器上找到相应IP地址,通过ssh远程登录。
默认帐号:Username: pi Password: raspberry
当然,如果连接了显示器和键盘,就不需要处理ssh登录的问题了。

二、设置树莓派的运行环境

  1. 设置网络参数,并重新启动

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # 激活无线网卡
    sudo rfkill unblock wlan

    # 设置两张网卡的网络参数
    sudo tee /etc/network/interfaces > /dev/null << EOF
    auto lo
    iface lo inet loopback

    auto eth0
    iface eth0 inet static
    address 192.168.0.8
    netmask 255.255.255.0
    gateway 192.168.0.1

    allow-hotplug wlan0
    iface wlan0 inet static
    address 192.168.11.1
    netmask 255.255.255.0
    EOF

    网络参数设置成功后,使用sudo reboot重启树莓派。
    ssh pi@192.168.0.8从新的IP地址登录,并使用ip a检查网络情况,如果获得以下输出就OK了!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    pi@raspberrypi:~ $ ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
    valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
    valid_lft forever preferred_lft forever
    2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether dc:a6:32:ad:ad:78 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.8/24 brd 192.168.0.255 scope global eth0
    valid_lft forever preferred_lft forever
    inet6 fe80::dea6:32ff:fead:ad78/64 scope link
    valid_lft forever preferred_lft forever
    3: wlan0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
    link/ether dc:a6:32:ad:ad:79 brd ff:ff:ff:ff:ff:ff
    inet 192.168.11.1/24 brd 192.168.11.255 scope global wlan0
    valid_lft forever preferred_lft forever
  2. 设置软件源,并安装基础软件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 关闭默认源
    sudo sed -i 's/^deb http/#deb http/g' /etc/apt/sources.list

    # 设置Aliyun国内源
    sudo sh -c 'echo "deb https://mirrors.aliyun.com/raspbian/raspbian/ buster main non-free contrib" >> /etc/apt/sources.list'
    sudo sh -c 'echo "deb-src https://mirrors.aliyun.com/raspbian/raspbian/ buster main non-free contrib" >> /etc/apt/sources.list'

    # 更新源仓库,并安装基础软件
    sudo apt update
    sudo apt install net-tools dnsutils ntp hostapd git iptables-persistent netfilter-persistent dnsmasq -y
    • 基础软件安装都在此步骤完成!!! 因为后续启动Clash内建DNS,将域名解析到伪地址,会影响本机Terminal的外网访问
  3. 关闭不需要的Systemd自启动服务

    1
    2
    3
    4
    5
    6
    # 关闭avahi自动网络发现功能,类似Bonjure
    sudo systemctl disable avahi-daemon
    sudo systemctl stop avahi-daemon

    # 暂时关闭dnsmasq服务,避免影响Clash安装
    sudo systemctl stop dnsmasq
    • dsnmasq安装时会自动修改/etc/resolv.conf,将默认DNS修改为127.0.0.1

三、安装并设置Clash

Clash的Github地址是https://github.com/Dreamacro/clash, 技术文档位于https://lancellc.gitbook.io/clash

基于众所周知的原因,建议采用手工下载并通过sftp传入树莓派,主要包括以下文件:

文件名 说明
clash-linux-armv7-v1.0.0.gz clash的可执行文件
Country.mmdb 各个国家的IP地址数据文件, clash启动时会自动下载
yacd-gh-pages.zip 第三方控制面板插件,比官方的好看一些
config.yaml VPS客户端的配置文件
  1. 传入文件默认存放在/home/pi/,解压并手工安装

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # 建立clash的home目录
    cd $HOME
    mkdir .config
    mkdir .config/clash

    # 配置/usr/bin/clash,并添加绑定低位端口的权限
    sudo gunzip clash-linux-armv7-v1.0.0.gz

    sudo mv clash-linux-armv7-v1.0.0 /usr/bin/clash
    sudo chmod a+x /usr/bin/clash
    sudo setcap cap_net_bind_service=+ep /usr/bin/clash
    ls -l /usr/bin/clash

    # 安装配置文件
    unzip yacd-gh-pages.zip

    mv yacd-gh-pages /home/pi/.config/clash/yacd-dashboard
    mv Country.mmdb /home/pi/.config/clash/
    mv config.yaml /home/pi/.config/clash/
    ls -l /home/pi/.config/clash/
  2. 输入sudo clash -d /home/pi/.config/clash,启动clash

    正常情况下屏幕将显示如下内容,就表示启动成功并打开了相应的侦听端口

    1
    2
    3
    4
    5
    6
    7
    8
    pi@raspberrypi:~/.config/clash $ sudo clash -d /home/pi/.config/clash
    INFO[0000] Start initial compatible provider auto
    INFO[0000] Start initial compatible provider Proxy
    INFO[0000] HTTP proxy listening at: :7890
    INFO[0000] SOCKS proxy listening at: :7891
    INFO[0000] DNS server listening at: 0.0.0.0:53
    INFO[0000] Redir proxy listening at: :7892
    INFO[0000] RESTful API listening at: 0.0.0.0:9090

    也可以自行检查端口占用情况

    1
    2
    3
    4
    5
    6
    7
    8
    pi@raspberrypi:~ $ sudo netstat -tunpl |grep clash
    tcp6 0 0 :::7890 :::* LISTEN 3001/clash
    tcp6 0 0 :::7891 :::* LISTEN 3001/clash
    tcp6 0 0 :::7892 :::* LISTEN 3001/clash
    tcp6 0 0 :::9090 :::* LISTEN 3001/clash
    udp6 0 0 :::53 :::* 3001/clash
    udp6 0 0 :::7891 :::* 3001/clash
    udp6 0 0 :::7892 :::* 3001/clash

    vmess等协议对Server和CLient的时钟同步有严格要求,如果没有NTP服务,可能导致无法连通

  3. 输入curl -x http://localhost:7890 google.com, 检查KX上网的效果

    1
    2
    3
    4
    5
    6
    7
    pi@raspberrypi:~ $ curl -x http://localhost:7890 google.com
    <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
    <TITLE>301 Moved</TITLE></HEAD><BODY>
    <H1>301 Moved</H1>
    The document has moved
    <A HREF="http://www.google.com/">here</A>.
    </BODY></HTML>
  4. 浏览器输入地址http://192.168.0.8:9090/ui,打开控制面板

四、配置并启动AP热点

  1. 输入sudo vi /etc/dnsmasq.conf, 也可以根据本次环境自行修改网络参数

    注意:dnsmasq仅作为DHCP服务器,不启动DNS,因为Clash有自建的DNS

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # DHCP
    interface=wlan0
    listen-address=192.168.11.1
    dhcp-range=192.168.11.100, 192.168.11.150, 12h

    # set default route
    dhcp-option=3,192.168.11.1

    # set default nameserver
    dhcp-option=6, 192.168.11.1

    # without DNS
    port=0
  2. 输入sudo vi /etc/default/hostapd, 设置hostapd启动配置文件。
    将其中包含#DAEMON_CONF=""的行,修改为

    1
    DAEMON_CONF="/etc/hostapd/hostapd.conf"
  3. 输入sudo vi /etc/hostapd/hostapd.conf, 根据本地网络环境自行修改配置AP接入参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    interface=wlan0
    driver=nl80211
    hw_mode=g
    ssid=< Your SSID >
    channel=6
    wpa=2
    wpa_passphrase=< Your Passeword >
    wpa_key_mgmt=WPA-PSK
    wpa_pairwise=CCMP
    rsn_pairwise=CCMP
    auth_algs=3
    wmm_enabled=1
    max_num_sta=10
    logger_stdout=-1
    logger_stdout_level=2

    注意:将< Your SSID >< Your Passeword > 修改为用户定义数据

  4. 通过systemd,启动AP热点

    1
    2
    3
    4
    5
    6
    # hostapd启动解锁
    sudo systemctl unmask hostapd

    # 重启服务
    sudo systemctl restart hostapd
    sudo systemctl restart dnsmasq

    启动完成后,另外找一台PC设备,寻找SSID并连接登录,此时应该分配一个198.168.11.x的地址。
    检查该PC的网络参数,默认路由是192.168.11.1,DNS设置为198.19.0.1,但是并不能上网(DNS是个fake-ip的假地址),因为下面还有一个关键步骤。

五、为从路由器设置透明代理

  1. 为AP热点设置动态NAT转发,只出不进

    1
    2
    3
    4
    # 为 wlan0--->eth0 设置动态NAT出口转换
    sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
    sudo iptables -A FORWARD -i eth0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT
    sudo iptables -A FORWARD -i wlan0 -o eth0 -j ACCEPT
  2. 为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
    # 新建两条链,clash用于数据访问,clash_dns用于内建DNS
    sudo iptables -t nat -N clash
    sudo iptables -t nat -N clash_dns

    # 为内建DNS设置NAT转换,从198.18.0.1--->198.19.x.x,同时支持UDP和TCP,但当前版本实际只使用UDP
    # 重要:192.168.0.8是你的Clash出口地址,应根据网络环境自行修改!!!
    sudo iptables -t nat -A PREROUTING -p tcp --dport 53 -d 198.19.0.0/24 -j clash_dns
    sudo iptables -t nat -A PREROUTING -p udp --dport 53 -d 198.19.0.0/24 -j clash_dns
    sudo iptables -t nat -A clash_dns -p udp --dport 53 -d 198.19.0.0/24 -j DNAT --to-destination 192.168.0.8:53
    sudo iptables -t nat -A clash_dns -p tcp --dport 53 -d 198.19.0.0/24 -j DNAT --to-destination 192.168.0.8:53

    # 为Clash数据访问建立NAT转换
    sudo iptables -t nat -A PREROUTING -p tcp -j clash

    # 为本机网段、私有网段、保留网段等提供直通车
    sudo iptables -t nat -A clash -d 0.0.0.0/8 -j RETURN
    sudo iptables -t nat -A clash -d 10.0.0.0/8 -j RETURN
    sudo iptables -t nat -A clash -d 127.0.0.0/8 -j RETURN
    sudo iptables -t nat -A clash -d 169.254.0.0/16 -j RETURN
    sudo iptables -t nat -A clash -d 172.16.0.0/12 -j RETURN
    sudo iptables -t nat -A clash -d 192.168.0.0/16 -j RETURN
    sudo iptables -t nat -A clash -d 224.0.0.0/4 -j RETURN
    sudo iptables -t nat -A clash -d 240.0.0.0/4 -j RETURN

    # 上述条件之外的数据流量,一律转发给7892端口,实现免代理的透明网关
    sudo iptables -t nat -A clash -p tcp -j REDIRECT --to-ports 7892
  3. 设置iptables路由表的持久化

    执行完上面的 iptables 命令之后,就完成了旁路由的路由功能了,但是此时 iptables 并没有永久保存,下次开机上面的配置就会丢失。
    为了使得重启之后 iptables 命令仍然存在,我们需要安装iptables-persistent软件来实现(前面步骤二已完成):
    执行以下命令,就即可将最新的 iptables 规则保存下来。

    1
    2
    # sudo apt install iptables-persistent
    sudo sh -c 'iptables-save > /etc/iptables/rules.v4'

    安装的过程中会提示你是否需要保存 iptables 配置,直接选是就行。这时候即使电脑重启了也会应用这些路由规则。

六、设置Clash开机自启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
sudo tee /etc/systemd/system/clash.service > /dev/null << EOF
[Unit]
Description=clash service
After=network.target

[Service]
Type=simple
User=pi

# Set port permissions capability without root user
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE

ExecStart=/usr/bin/clash -d /home/pi/.config/clash
Restart=on-failure # or always, on-abort, etc

[Install]
WantedBy=multi-user.target

EOF

sudo systemctl enable clash

到此为止,Clash透明网关就算是大功告成了,现在可以重启树莓派,检查科学成果了!!!

当然,从系统安全的角度,做一些扫尾的工作也很重要,包括:

  • 编辑树莓派的公匙文件mkdir $HOME/.ssh && vi $HOME/.ssh/authorized_keys,将各台需要管理PC的 RSA 公匙加入
  • 在树莓派上,设置文件权限并重启ssh服务
1
2
3
4
sudo chmod 600 $HOME/.ssh/authorized_keys
sudo chmod 700 $HOME/.ssh/

sudo systemctl restart sshd
  • 在管理PC上,尝试ssh登录,如果免密码成功,就可以关闭口令登录了
  • 编辑树莓派的sudo vi /etc/ssh/sshd_config,将PasswordAuthentication设为no,以禁止采用口令登录

这样一来,树莓派就在指定PC上就可以pi用户实现免密码登录,而其它用户、其它PC都无法登录了

Clash透明网关的使用方法

首先找到相应SSID,连接AP热点后自动设置默认网关和DNS,浏览器打开直接上网,再也不用 HTTP 或 SOCK5 代理了。
如果想要观察Clash运行情况,可以在浏览器打开http://192.168.0.8:9090/ui

Clash的配置是一件比较复杂的工作,准备下次专门讨论,这里就不啰嗦了。


补充问题

附录1: Clash的Home目录结构

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
pi@raspberrypi:~ $ tree $HOME/.config/clash/
/home/pi/.config/clash/
├── config.yaml
├── Country.mmdb
└── uacd-dashboard
├── 9.e5ebc45c57147750a8b7.js
├── app.2710ac87312332833aa4.js
├── app.2710ac87312332833aa4.js.LICENSE.txt
├── app.bb773b8bdd4263de9ffd.css
├── CNAME
├── core-js~app.4e0935a458133ffa9ca3.js
├── _headers
├── index.html
├── open-sans-latin-400.woff2
├── open-sans-latin-700.woff2
├── proxies.1e31d885e6fe278da534.css
├── proxies.beaddeef3e0db08db953.js
├── react~app.10618449a8e9b56a1845.js
├── react~app.10618449a8e9b56a1845.js.LICENSE.txt
├── report.html
├── roboto-mono-latin-400.woff2
├── rules.3ba17dd53c9d8bd8b221.css
├── rules.90faad053acc32c74a88.js
├── runtime.30479c2b09f71505cee3.js
├── vendors~chartjs.e4543f10556636d64b75.js
├── vendors~chartjs.e4543f10556636d64b75.js.LICENSE.txt
├── vendors~proxies.34b4cb2526b2e8ed766c.js
├── vendors~rules.b2e93c21da80f9dc4a80.js
├── yacd-128.png
├── yacd-64.png
└── yacd.ico

附录2:关于avahi服务

Zeroconf(Zero configuration networking)零配置网络服务规范,是一种用于自动生成可用IP地址的网络技术,不需要额外的手动配置和专属的配置服务器。

Zeroconf规范的提出者是Apple公司,目标是让非专业用户也能便捷的连接各种网络设备,例如计算机,打印机等。整个搭建网络的过程都是通过程式自动化实现。如果没有 zeroconf,用户必须手动配置一些服务,例如DHCP、DNS,计算机网络的其他设置等。这些对非技术用户和新用户们来说是很难的事情。

Avahi 是Zeroconf规范的开源实现,常见使用在Linux上。包含了一整套多播DNS(multicastDNS)/DNS-SD网络服务的实现。它使用 的发布授权是LGPL。Zeroconf规范的另一个实现是Apple公司的Bonjour程式。AvahiBonjour相互兼容(废话,都走同一个 规范标准嘛,就象IE,Firefox,chrome都能跑HTTP1.1一样)。

Avahi允许程序在不需要进行手动网络配置的情况 下,在一个本地网络中发布和获知各种服务和主机。例如,当某用户把他的计算机接入到某个局域网时,如果他的机器运行有Avahi服务,则Avahi程式自 动广播,从而发现网络中可用的打印机、共享文件和可相互聊天的其他用户。这有点象他正在接收局域网中的各种网络广告一样。

Linux下系统实际启动的进程名,是avahi-daemon。除非你有兼容的设备或使用 zeroconf 协议的服务,否则应该关闭它。

参考目录

核心参考文献

关于网络协议

关于Clash