Transparent proxy with clash on OpenWRT

最近在Netgear WNDR4300v2上重装了openwrt 22,以下记录重新安装clash的过程

Mechanism

我们使用clash作为透明代理,通过iptables + ipset实现国外流量走clash,国内直连。

对于DNS污染的问题,我们使用dnsmasq + 国内域名白名单,让国内的域名解析使用国内DNS服务器,并且将这些域名的IP地址保存在ipset,通过iptables规则直接连接。

而不在国内域名列表里的域名,使用dnscrypt-proxy2解析,结合iptables将流量转发到clash

Prerequisite

  • OpenWRT 22.03:路由器版本
  • dnsmasq-full:用来配置国内域名,并且将解析的IP地址保存在ipset,而dnsmasq不支持
  • dnscrypt-proxy2:用来防止DNS污染
  • clash:支持多种协议
  • iptables-nft:openwrt 22上使用nf-tables代替了iptables

安装Clash

下载并上传 clash

github下载路由器相应的版本,Netgear WNDR4300v2选择clash-linux-mips-softfloat

curl -LO https://github.com/Dreamacro/clash/releases/download/v1.12.0/clash-linux-mips-softfloat-v1.12.0.gz

tar zxvf clash-linux-mips-softfloat-v1.12.0.gz
cd clash-linux-mips-softfloat-v1.12.0

# 增加可执行权限
chmod +x clash-linux-mips-softfloat
scp clash-linux-mips-softfloat [email protected]:/usr/bin/clash

配置

OpenWRT路由器上配置clash

config.yaml

从服务商下载clash的配置文件,我用的是shadowsocks.com,将下载的文件shadowsocks.yaml重命名为config.yaml

因为我们需要用到透明代理,所以在配置文件添加透明代理的端口

# /etc/clash/config.yaml
port: 7890
redir-port: 7892

放到/etc/clash/目录下。

service

通过service启动clash

cat >> /etc/init.d/clash <<'EOF'
#!/bin/sh /etc/rc.common
START=90
USE_PROCD=1

start_service() {
        procd_open_instance
        procd_set_param command /bin/clash -d /etc/clash
        procd_set_param respawn 300 0 5 # threshold, timeout, retry
        procd_set_param file /etc/clash/config.yml
        procd_set_param stdout 1
        procd_set_param stderr 1
        procd_set_param pidfile /var/run/clash.pid
        procd_close_instance
}
EOF

chmod +x /etc/init.d/clash

启动 Clash

启动clash并设置为开机自动启动

service clash start
service clash enable

至此Clash已经配置好了,通过设置HTTP_PROXY=http://192.168.1.1:7890已经可以科学上网

设置DNS

在路由器上设置DNS相关组件

安装 dnsmasq-full、dnscrypt-proxy2

opkg update

# 卸载 dnsmasq 并安装 dnsmasq-full
opkg remove dnsmasq && opkg install dnsmasq-full

# 安装 dnscrypt-proxy2 等
opkg install curl ipset iptables-nft dnscrypt-proxy2 ca-certificates coreutils-base64

禁用运营商的DNS

/etc/config/network里增加以下参数,禁用ISP的DNS服务器

config interface 'wan'    # or 'wan6'
    option peerdns '0'

配置 dnscrypt-proxy2

这里只为dnscrypt-proxy2增加了一个http代理的配置,详细的参数可以参考官方文档

# /etc/dnscrypt-proxy2/dnscrypt-proxy.toml
http_proxy = 'http://127.0.0.1:7890'

重启 dnscrypt-proxy

service dnscrypt-proxy restart
service dnscrypt-proxy enable

配置 dnsmasq

定位 dnsmasq 的配置文件

mkdir /etc/dnsmasq.d
uci add_list dhcp.@dnsmasq[0].confdir=/etc/dnsmasq.d
uci add_list dhcp.@dnsmasq[0].cachesize=0
uci commit dhcp

下载大陆白名单,让大陆的走指定的解析,直接连接,达到加速国内站点的目的。

mkdir -p /etc/scripts && cd /etc/scripts

# 下载国内域名列表的生成脚本
curl -L -o generate_dnsmasq_chinalist.sh https://github.com/cokebar/openwrt-scripts/raw/master/generate_dnsmasq_chinalist.sh

chmod +x generate_dnsmasq_chinalist.sh

# 生成 dnsmasq 配置
sh generate_dnsmasq_chinalist.sh -d 114.114.114.114 -p 53 -s chinalist -o /etc/dnsmasq.d/accelerated-domains.china.conf

# 重启 dnsmasq 
service dnsmasq restart

登录路由器的管理界面,设置 dnsmasq 的转发服务器为dnscrypt-proxy的地址

dnsmasq转发服务器

同时,忽略解析文件。

dnsmasq ignore resolv

保存并应用后,dnsmasq 就配置好了。

配置 iptables

System > Startup > Local Startup 中,添加开机启动脚本。

iptables -t nat -N clash

# 局域网IP地址直连
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

# 清空 ipset 集合
ipset destroy
ipset create chinalist hash:net

# 国内IP地址直连
iptables -t nat -A clash -m set --match-set chinalist dst -j RETURN

# 其他流量走 clash 代理
nft add rule ip nat clash ip protocol tcp counter redirect to :7892

iptables -t nat -A PREROUTING -p tcp -j clash

Or you can use nft to replace iptables command:

cat > /etc/nftables.d/clash.nft <<'EOF'
table ip nat {
  chain clash {
    # 排除私有地址和代理自身端口
    ip daddr { 
      0.0.0.0/8, 10.0.0.0/8, 127.0.0.0/8, 169.254.0.0/16,
      172.16.0.0/12, 192.168.0.0/16, 224.0.0.0/4, 240.0.0.0/4 
    } return

    # 排除目标端口为 7892 的流量(防止循环)
    tcp dport 7892 return
    meta mark 0x1 return

    # 重定向其他 TCP 流量到代理端口
    ip protocol tcp redirect to :7892
  }

  chain PREROUTING {
    type nat hook prerouting priority dstnat; policy accept;
    meta l4proto tcp jump clash
  }

}

table ip mangle {
    chain OUTPUT {
        type filter hook output priority mangle;
        ip daddr != 127.0.0.1 meta skuid clash mark set 0x1
    }
}
EOF

nft -f /etc/nftables.d/clash.nft

重启路由器

reboot

至此,我们已经实现了科学上网。

Linux上额外的配置

systemd-resolved(8)

使用了systemd-resolvedLinux发行版,还需要额外的配置。设置resolv.conf直接使用路由器提供的DNS

ln -rsf /run/systemd/resolve/resolv.conf /etc/resolv.conf

domain-based DNS routing

当我们有私有域名需要根据域名来选择DNS服务器,例如需要访问VPN、kubernetes集群内部的域名等,我们可以通过设置systemd-networkd来实现

当我们想通过kubernetes集群的DNS缓存nodelocaldns访问集群内部的域名时,可以如下设置:

cat >> /etc/systemd/network/nodelocaldns.network <<'EOF'
[Match]
Name=nodelocaldns

[Network]
DNS=169.254.25.10
Domains=~cluster.local
EOF

systemctl enable --now systemd-networkd

验证解析状态


$ resolvectl dns
Global: 192.168.1.1
Link 2 (enp4s0):
Link 3 (enp0s31f6): 192.168.1.1
Link 4 (nodelocaldns): 169.254.25.10

$ resolvectl domain
Global:
Link 2 (enp4s0):
Link 3 (enp0s31f6):
Link 4 (nodelocaldns): ~cluster.local

至此,kubernetes里的域名、科学上网都可以访问了

Share: X (Twitter) Facebook LinkedIn