Setting up Clash as Second Gateway for LAN

Less painful way setting up a second gateway using Clash, IPset and iptables.

Prerequisite

  • comzyh/clash
    • Open sourced tun support
  • Debian 10
  • Proxy provider
    • Shadowsocks or whatever supported by clash, Dler Cloud for example

Steps

1 Setup Tun

TUN_NAME=clash0
sudo ip tuntap add mode tun user clash name $TUN_NAME
sudo ip link set $TUN_NAME up
# or sudo ifconfig utun up

2 Setup iptables

iptables -t mangle -N CLASH # Create Clash Chain
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
# ipset can be enabled optionally.
# iptables -t mangle -A CLASH -m set --match-set CN dst -j RETURN
iptables -t mangle -A CLASH -j MARK --set-xmark 129
iptables -t mangle -A PREROUTING -j CLASH # have traffic come to the CLASH chain
# if you need to have the local machine(the machine clash is running on) traffic to go through clash TUN
# note: you should have clash running under the user `clash', see below systemd script
# iptables -t mangle -A OUTPUT -m owner --uid-owner clash -j RETURN
# iptables -t mangle -A OUTPUT -j CLASH

3 Setup route

ip route add default dev $ table 129
ip rule add fwmark 129 $TUN_NAME lookup 129

4 Checkpoint 1

At this stage, all the traffic routed by this machine will be tagged with fwmark 129 and redirected to $TUN_NAME.

Feel free to change the number 129 to whatever you like.

5 Setup clash

To have clash handle the traffic on $TUN_NAME, we need to setup clash properly.

5.1 Install

Download the binary from https://github.com/comzyh/clash/releases and put it under /usr/bin/clash. For example:

curl -sL https://github.com/comzyh/clash/releases/download/20210310/clash-linux-amd64 -o /usr/bin/clash

5.2 systemd script

Note The user should match that one speficied in the iptable rules above.

cat /lib/systemd/system/clash@.service
[Unit]
Description=Clash
After=network.target
[Service]
Type=simple
User=%i
Restart=always
ExecStart=/usr/bin/clash -d /home/wtf/clash
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
[Install]
WantedBy=multi-user.target
systemctl enable clash@clash

5.3 clash config

Add this section to your existing config.yaml

Note

The device url should be dev://$TUN_NAME

tun:
enable: true
stack: system
device-url: dev://clash0
# dns-hijack:
# - 10.0.0.5

5.4 Start clash

systemctl enable clash@clash

6 Checkpoint 2

At this stage, you should be able to use this machine as the gateway for you LAN devices.

Note

Don't forget to enable ipv4_forwarding on your machine. In case you need help: https://linuxconfig.org/how-to-turn-on-off-ip-forwarding-in-linux

7 Misc

7.1 Setup ipset

Sometimes you don't really want to route all traffic to the gateway, and that can be done by setting up ipset. By doing this, you can either to have the iptables RETURN all the traffic that is matching a particular ipset or only mark the traffic when it matches.

In my case, I want to have all the China Mainland traffic to be going though the clash tun, so I'll have the script below to update the ipset everyday.

cat /etc/cron.daily/update-ipset.sh
IPSET_BIN=/usr/sbin/ipset
$IPSET_BIN -L CN-new > /dev/null 2>&1
if [ $? -eq 0 ]; then
$IPSET_BIN destroy CN-new
fi
$IPSET_BIN create CN-new hash:net
curl -sL http://www.ipdeny.com/ipblocks/data/countries/cn.zone | xargs -n1 $IPSET_BIN -A CN-new
$IPSET_BIN -L CN > /dev/null 2>&1
if [ $? -ne 0 ]; then
$IPSET_BIN create CN hash:net
fi
$IPSET_BIN swap CN-new CN
$IPSET_BIN destroy CN-new

And have my mangle table:

iptables -t mangle -L
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
RETURN udp -- anywhere anywhere udp dpts:4096:65535
RETURN tcp -- anywhere anywhere tcp dpts:8192:65535
CLASH all -- anywhere anywhere
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
Chain CLASH (1 references)
target prot opt source destination
MARK all -- anywhere anywhere match-set CN dst MARK set 0xc0

7.2 Persistence

  • iptables/ipset

    You'll want to use iptables-persistent/ipset-persistent to have iptables/ipset persistence

  • route and tun dev

    Use Debian network configuration, an example:

    cat /etc/network/interfaces | grep -A 20 clash0
    auto clash0
    iface clash0 inet manual
          pre-up ip tuntap add mode tun user clash name $IFACE
          up ip link set dev $IFACE up
          post-up ip route add default dev $IFACE table 192
          post-up ip rule add fwmark 192 lookup 192
          down ip link set dev $IFACE down
          post-down ip link del dev $IFACE
          post-down ip route del default dev $IFACE table 192
          post-down ip rule del fwmark 192 lookup 192

Summary

You should now be ready to setup the IP of the above machine to be the default gateway on you DHCP service.

Best practive is having a static IP for the above machine and always have a backup gateway in case something went wrong with the second gateway.

All the script sample above are for my personal note and demo purpose only - make sure you understand it and feel free to make any change accordingly.