Raspberry Pi as transparent proxy
November 06, 2022
记录使用树莓派搭建透明代理的过程
Setup Clash service
配置 docker compose 驱动的 Clash 实例
在 $HOME 目录下创建项目:
mkdir -p $HOME/Services/pi-gateway
mkdir -p $HOME/Services/pi-gateway/clash
cd $HOME/Services/pi-gateway如下为 pi-gateway 项目结构:
.
├── clash
│ ├── config.yaml # clash configuration
│ └── dashboard # clash control plane (https://github.com/haishanh/yacd)
├── docker-compose.yaml
├── naive-1 # naiveproxy instance 1 related folder
│ └── config.json # naiveproxy instance 1 configuration
└── naive-2 # naiveproxy instance 2 related folder
└── config.json # naiveproxy instance 2 configuration创建 docker compose 配置文件:
touch docker-compose.yaml使用如下配置样例:
version: "3.9"
services:
clash:
container_name: "pi-gateway_clash"
# image: dreamacro/clash-premium:2021.07.03
# image: dreamacro/clash-premium:2022.07.07
image: dreamacro/clash-premium:latest
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun
restart: unless-stopped
volumes:
- ./clash/dashboard:/root/.config/clash/dashboard
- ./clash/config.yaml:/root/.config/clash/config.yaml:ro
# When your system is Linux, you can use `network_mode: "host"` directly.
network_mode: host
naive1:
container_name: "pi-gateway_naive_1"
image: pocat/naiveproxy:client
volumes:
- ./naive-1/config.json:/etc/naiveproxy/config.json:ro
restart: unless-stopped
network_mode: host
naive2:
container_name: "pi-gateway_naive_2"
image: pocat/naiveproxy:client
volumes:
- ./naive-2/config.json:/etc/naiveproxy/config.json:ro
restart: unless-stopped
network_mode: host
# we could add move container here follow the format listed above
# ...创建 clash 配置文件:
touch clash/config.yaml使用如下配置样例:
# Port of HTTP(S) proxy server on the local end
# port: 7890
# Port of SOCKS5 proxy server on the local end
# socks-port: 7891
# Transparent proxy server port for Linux and macOS (Redirect TCP and TProxy UDP)
redir-port: 7892
# Transparent proxy server port for Linux (TProxy TCP and TProxy UDP)
# tproxy-port: 7893
# HTTP(S) and SOCKS4(A)/SOCKS5 server on the same port
mixed-port: 7890
# authentication of local SOCKS5/HTTP(S) server
# authentication:
# - "user1:pass1"
# - "user2:pass2"
# Set to true to allow connections to the local-end server from
# other LAN IP addresses
allow-lan: true
# This is only applicable when `allow-lan` is `true`
# '*': bind all IP addresses
# 192.168.122.11: bind a single IPv4 address
# "[aaaa::a8aa:ff:fe09:57d8]": bind a single IPv6 address
# bind-address: '*'
# Clash router working mode
# rule: rule-based packet routing
# global: all packets will be forwarded to a single endpoint
# direct: directly forward the packets to the Internet
mode: rule
# Clash by default prints logs to STDOUT
# info / warning / error / debug / silent
log-level: silent
# When set to false, resolver won't translate hostnames to IPv6 addresses
# ipv6: false
# RESTful web API listening address
external-controller: 0.0.0.0:9090
# A relative path to the configuration directory or an absolute path to a
# directory in which you put some static web resource. Clash core will then
# serve it at `http://{{external-controller}}/ui`.
external-ui: dashboard
# Secret for the RESTful API (optional)
# Authenticate by spedifying HTTP header `Authorization: Bearer ${secret}`
# ALWAYS set a secret if RESTful API is listening on 0.0.0.0
secret: ""
# DNS server settings
# This section is optional. When not present, the DNS server will be disabled.
dns:
enable: true
listen: :1053
# These nameservers are used to resolve the DNS nameserver hostnames below.
# Specify IP addresses only
default-nameserver:
- 223.5.5.5
- 223.6.6.6
- 114.114.114.114
- 1.1.1.1
- 8.8.8.8
enhanced-mode: redir-host
# Supports UDP, TCP, DoT, DoH. You can specify the port to connect to.
# All DNS questions are sent directly to the nameserver, without proxies
# involved. Clash answers the DNS question with the first result gathered.
nameserver:
- 114.114.114.114 # default value
- 8.8.8.8 # default value
- tls://dns.rubyfish.cn:853 # DNS over TLS
- https://1.1.1.1/dns-query # DNS over HTTPS
# - dhcp://en0 # dns from dhcp
fallback:
- 208.67.220.220:5353
- 208.67.222.222:5353
- 101.6.6.6:5353
proxies:
# local NaïveProxy 1
- name: "🇺🇸 NaïveProxy-1"
type: socks5
server: localhost
port: 1081
udp: true
# local NaïveProxy 2
- name: "🇺🇸 NaïveProxy-2"
type: socks5
server: localhost
port: 1082
udp: true
proxy-groups:
# select is used for selecting proxy or proxy group
# you can use RESTful API to switch proxy, is recommended for use in GUI.
- name: "🕹️ Controller"
type: select
proxies:
- "🛸 Autopilot"
- "🚀 Load-Balance_CH"
- "🚚 Load-Balance_RR"
# url-test select which proxy will be used by benchmarking speed to a URL.
- name: "🛸 Autopilot"
type: url-test
proxies:
- "🇺🇸 NaïveProxy-1"
- "🇺🇸 NaïveProxy-2"
# tolerance: 150
url: "http://cp.cloudflare.com/generate_204"
interval: 30
# load-balance: The request of the same eTLD will be dial on the same proxy.
- name: "🚀 Load-Balance_CH"
type: load-balance
proxies:
- "🇺🇸 NaïveProxy-1"
- "🇺🇸 NaïveProxy-2"
url: "http://www.google.com/generate_204"
interval: 30
strategy: consistent-hashing
- name: "🚚 Load-Balance_RR"
type: load-balance
proxies:
- "🇺🇸 NaïveProxy-1"
- "🇺🇸 NaïveProxy-2"
url: "http://www.google.com/generate_204"
interval: 30
strategy: round-robin
rules:
- GEOIP,CN,DIRECT
- IP-CIDR,10.0.0.0/8,DIRECT
- IP-CIDR,17.0.0.0/8,DIRECT
- IP-CIDR,100.64.0.0/10,DIRECT
- IP-CIDR,127.0.0.0/8,DIRECT
- IP-CIDR,172.16.0.0/12,DIRECT
- IP-CIDR,192.168.0.0/16,DIRECT
- MATCH,🕹️ Controller这里可以通过 🕹️ Controller 选择器控制三种代理组,分别是自动选择和两种不同的负载均衡策略。
三种代理组分别拥有两个代理,两个代理对应容器编排里的两个 NaïveProxy 实例,分别运行在 1081、1082 端口。
关于 NaiveProxy 实例
创建对于配置文件:
touch naive-1/config.json
touch naive-2/config.json关于 NaïveProxy 的代理配置详见:https://github.com/klzgrad/naiveproxy
- 代理监听在 1081 端口 (
naive-1/config.json)
{
"listen": "socks://0.0.0.0:1081",
"proxy": "https://<YOUR-NP-USERNAME>:<YOUR-NP-PASSWORD>@<YOUR-NP-DOMAIN>",
"log": ""
}- 代理监听在 1082 端口 (
naive-2/config.json)
{
"listen": "socks://0.0.0.0:1082",
"proxy": "https://<YOUR-NP-USERNAME>:<YOUR-NP-PASSWORD>@<YOUR-NP-DOMAIN>",
"log": ""
}关于 Clash 实例
- Clash 会监听在 7890 端口,能同时提供 HTTP/SOCKS 代理功能
- Clash 会监听在 7892 端口,提供透明代理功能,用于接受并代理来自 iptables 转发过来的流量
启动容器编排
先使用交互式查看运行日志、确认是否能正常运行:
docker compose up使用另一终端测试代理是否可用:
curl https://ifconfig.co -x http://localhost:7890
curl https://ifconfig.co -x socks5://localhost:7890结果应该返回代理 IP 地址
回到原来阻塞的终端,使用 CTRL + c 结束命令,确认容器可以正常工作后:
docker compose up -dInstall PiVPN
安装 PiVPN 服务: https://www.pivpn.io/
curl -L https://install.pivpn.io | bash安装过程中选择 OpenVPN、WireGuard 均可,需要留意 VPN 服务监听端口,后续需要在路由器上配置对应的端口转发。
如果安装 OpenVPN,启用的虚拟网卡为 tun0
如果安装 WireGuard,启用的虚拟网卡为 wg0
后续需要让 Pi-Hole 启动也绑定在对应的虚拟网卡上,让通过 VPN 连接的设备也能使用内网中的 Pi-Hole DNS。
Install Pi-Hole
安装 Pi-Hole 服务:https://docs.pi-hole.net/main/basic-install/
主要用于解决 DNS 污染,统计、监控网络内的 DNS 请求
为 WireGuard 客户端提供 DNS
让 Pi-Hole 监听 wg0 网卡服务通过 WireGuard 接入的设备:
sudo echo "interface=wg0" > /etc/dnsmasq.d/100-interfaces.conf重启 Pi-Hole 服务:
sudo systemctl restart pihole-FTL.service其他
让 Pi-Hole 监听树莓派以太网接口 eth0 网卡:
sudo echo "interface=eth0" > /etc/dnsmasq.d/90-interfaces.conf让 Pi-Hole 监听树莓派无线网接口 wlan0 网卡:
sudo echo "interface=wlan0" > /etc/dnsmasq.d/99-interfaces.conf重启 Pi-Hole 服务生效
Enable transparent proxy
使用 iptables 转发流量实现透明代理
启用透明代理脚本: tproxy_enable.sh
#!/bin/sh
proxy_port=7892
# -*- TCP --------------------------------------------------------------------
# create user-defined chain `clash` in `nat` table
iptables -t nat -N clash
# add rules into user-defined chain `clash` from `nat` table
# ignore Reserved IP addresses
iptables -t nat -A clash -d 0.0.0.0/8 -j RETURN
iptables -t nat -A clash -d 10.0.0.0/8 -j RETURN
iptables -t nat -A clash -d 127.0.0.0/8 -j RETURN
iptables -t nat -A clash -d 169.254.0.0/16 -j RETURN
iptables -t nat -A clash -d 172.16.0.0/12 -j RETURN
iptables -t nat -A clash -d 192.168.0.0/16 -j RETURN
iptables -t nat -A clash -d 224.0.0.0/4 -j RETURN
iptables -t nat -A clash -d 240.0.0.0/4 -j RETURN
# redirect TCP traffic to port 7892
iptables -t nat -A clash -p tcp -j REDIRECT --to-port "$proxy_port"
# add three rules into `PREROUTING` chain from nat table
iptables -t nat -I PREROUTING -p tcp -j clash
iptables -t nat -I PREROUTING -p tcp -d 8.8.8.8 -j REDIRECT --to-port "$proxy_port"
iptables -t nat -I PREROUTING -p tcp -d 8.8.4.4 -j REDIRECT --to-port "$proxy_port"
# -*- UDP --------------------------------------------------------------------
ip rule add fwmark 1 table 100
ip route add local default dev lo table 100
# create user-defined chain `clash` in `mangle` table
iptables -t mangle -N clash
# add rules into user-defined chain `clash` from `mangle` table
# ignore Reserved IP addresses
iptables -t mangle -A clash -d 0.0.0.0/8 -j RETURN
iptables -t mangle -A clash -d 10.0.0.0/8 -j RETURN
iptables -t mangle -A clash -d 127.0.0.0/8 -j RETURN
iptables -t mangle -A clash -d 169.254.0.0/16 -j RETURN
iptables -t mangle -A clash -d 172.16.0.0/12 -j RETURN
iptables -t mangle -A clash -d 192.168.0.0/16 -j RETURN
iptables -t mangle -A clash -d 224.0.0.0/4 -j RETURN
iptables -t mangle -A clash -d 240.0.0.0/4 -j RETURN
# add rules info user-defined chain `clash` from `mangle` table match protocal udp
iptables -t mangle -A clash -p udp -j TPROXY --on-port "$proxy_port" --tproxy-mark 1
# add rules into `PREROUTING` chain from `mangle` table
iptables -t mangle -A PREROUTING -p udp -j clash禁用透明代理脚本: tproxy_disable.sh
#!/bin/sh
# clash (Tproxy TCP+UDP)
ip rule del fwmark 1 table 100
ip route del local default dev lo table 100
# delete rules into `PREROUTING` chain from `mangle` table
iptables -t mangle -D PREROUTING -p udp -j clash
# flush the rules under user-defined chain `clash`
iptables -t mangle -F clash
# delete user-defined chain `clash` from `mangel` table
iptables -t mangle -X clash
# --------------------------------------------------------------------------
# delete three rules in `PREROUTING` chain from nat table
iptables -t nat -D PREROUTING -p tcp -d 8.8.8.8 -j REDIRECT --to-port 7892
iptables -t nat -D PREROUTING -p tcp -d 8.8.4.4 -j REDIRECT --to-port 7892
iptables -t nat -D PREROUTING -p tcp -j clash
# flush the rules under user-defined chain `clash`
iptables -t nat -F clash
# delete user-defined chain `clash` from `nat` table
iptables -t nat -X clash相关命令
chmod +x tproxy_enable.sh
chmod +x tproxy_disable.sh启用透明代理:
sudo ./tproxy_enable.sh禁用透明代理:
sudo ./tproxy_disable.shPort forwarding
在路由器上设置端口转发到树莓派 WireGuard 监听的端口
获取公网 IP
curl ifconfig.co公网 IPv4 地址需要向 ISP 申请,将 VPN 配置里 endpoint 改为获取到的 IPv4 地址通过 VPN 客户端连接即可
公网 IPv6 将 VPN 配置里 endpoint 改为对应树莓派 IPv6 地址通过 VPN 客户端连接即可
Written by nnfewl, a noob. Follow me on Twitter