Layer 7 Policy Routing With Clash
In this article I will show you how you can set up a layer 7 router that has extensive rule policy and outbound destination support.
I have had introduced Clash in the past and gave an introductory guide about how you can deploy it on your Linux gateway as a transparent proxy. This article will do pretty much the same, but more in-depth - and since Clash has evolved extensively with TUN, TPROXY and auto-redir support, this time we’re going to do it much more simpler, and discover the massive potential you could get with Clash as your gateway.
First of all, we’re going to deploy Clash Premium on the gateway. Different from Clash, it’s proprietary at the moment and only compiled on Dreamacro’s (author of Clash) personal computers. It has TUN support, script-based rules support, rule providers, profiling (like a built-in Prometheus exporter), eBPF and auto-redir support. We’ll dive in to those later.
The latest release for linux-amd64 can be found at https://release.dreamacro.workers.dev/latest/clash-linux-amd64-latest.gz.
Download it to the gateway and unarchive:
$ wget -O clash.gz https://release.dreamacro.workers.dev/latest/clash-linux-amd64-latest.gz --2022-08-26 15:21:38-- https://release.dreamacro.workers.dev/latest/clash-linux-amd64-latest.gz Resolving release.dreamacro.workers.dev (release.dreamacro.workers.dev)... 22.214.171.124, 126.96.36.199, 2606:4700:3032::6815:253d, ... Connecting to release.dreamacro.workers.dev (release.dreamacro.workers.dev)|188.8.131.52|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 6204563 (5.9M) [binary/octet-stream] Saving to: 'clash.gz' clash.gz 100%[==========================================================>] 5.92M 4.92MB/s in 1.2s 2022-08-26 15:21:48 (4.92 MB/s) - 'clash.gz' saved [6204563/6204563] $ gzip -d clash.gz $ file clash clash: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped $ chmod +x clash $ ./clash -v Clash 2022.08.26-1-g8720ef5 linux amd64 with go1.19 Fri 26 Aug 2022 02:52:04 PM UTC
Okay. Now we’re going to give it a place to live and put the configuration files and IP database there.
$ mv ./clash /usr/local/bin $ mkdir -p /etc/clash $ clash -d /etc/clash # this creates default configuration and downloads IP database
Lastly let’s set up its systemd service file:
[Unit] Description=Clash daemon, A rule-based proxy in Go. After=network.target [Service] Type=simple Restart=always ExecStart=/usr/local/bin/clash -d /etc/clash [Install] WantedBy=multi-user.target
… and make it start at boot:
systemctl daemon-reload systemctl enable clash systemctl start clash
You can refer to the official guide here: https://github.com/Dreamacro/clash/wiki/Running-Clash-as-a-service#systemd
As a transparent proxy, here’s an example config.yml to make it work.
# HTTP(S) and SOCKS4(A)/SOCKS5 server on the same port mixed-port: 7890 mode: rule routing-mark: 6666 auto-redir: enable: true auto-route: true # This creates TUN device named utun on Linux tun: enable: true stack: gvisor auto-route: true auto-detect-interface: true # This stores proxy group selection and fake-ip mappings # in $HOME/.config/clash/.cache profile: store-selected: true store-fake-ip: true
Look at the
tun section. Enabling their
auto-route options, Clash automatically sets up the Linux routing table so any incoming/outgoing L2 traffic will be captured and forwarded by it. We no longer need to setup iptables manually now!
DNS, fake-ip and rules #
# Required for domain-based policy routing. dns: enable: true listen: 0.0.0.0:53 ipv6: false # when false, response to AAAA questions will be empty # These nameservers are used to resolve the DNS nameserver hostnames below. default-nameserver: - 184.108.40.206 - 220.127.116.11 - 18.104.22.168 enhanced-mode: fake-ip fake-ip-range: 198.18.0.1/16 nameserver: - 'https://22.214.171.124/dns-query' - 126.96.36.199
This DNS server setup is particularly interesting if you haven’t approached to the concept of fake-ip before. We will discuss later.
proxies section you setup a variety of proxy servers and proxy groups. This is a brief example, check out the official documentation here for more.
# Some example outbound proxies you can have. proxies: - name: "ss3" type: ss server: server port: 443 cipher: chacha20-ietf-poly1305 password: "password" plugin: v2ray-plugin plugin-opts: mode: websocket # no QUIC now # tls: true # wss # skip-cert-verify: true # host: bing.com # path: "/" # mux: true # headers: # custom: value - name: "vmess-h2" type: vmess server: server port: 443 uuid: uuid alterId: 32 cipher: auto network: h2 tls: true h2-opts: host: - http.example.com - http-alt.example.com path: / # socks5 - name: "socks" type: socks5 server: server port: 443 # username: username # password: password # tls: true # skip-cert-verify: true # udp: true # http - name: "http" type: http server: server port: 443 # username: username # password: password # tls: true # https # skip-cert-verify: true # sni: custom.com - name: trojan-grpc server: server port: 443 type: trojan password: "example" network: grpc sni: example.com # skip-cert-verify: true udp: true grpc-opts: grpc-service-name: "example" # You associate a rule with a single proxy or a proxy group. # In a proxy group, Clash offers proxy relaying, failovering, load-balancing, benchmarking or simply you can select which proxy you want the group to use as the outbound. # Read the official documentation at https://github.com/Dreamacro/clash/wiki/Configuration for more. proxy-groups: # If you have a VPN that exposes theirselves on a TUN device, # Create a proxy group and use only DIRECT as outbound. # Specify your outbound network device in `interface-name`. - name: Cisco AnyConnect type: select interface-name: tun0 proxies: - DIRECT - name: Wireguard type: select interface-name: wg0 proxies: - DIRECT - name: Proxy type: select proxies: - http - trojan-grpc - DIRECT
After setting up proxies and proxy groups, you set up your policy rules.
# Rule providers are a collection of rules. # You can load them in a local file or load remotely from a URL. rule-providers: CloudflareIP: type: file behavior: ipcidr path: ./RuleProviders/CloudflareIP.yaml # Check this out for available rule types: # https://github.com/Dreamacro/clash/wiki/Configuration#rules rules: - DOMAIN-SUFFIX,google.co.uk,Proxy - DOMAIN,accounts.google.co.uk,DIRECT - DOMAIN-SUFFIX,internal.company.com,Cisco AnyConnect - DOMAIN-SUFFIX,doubleclick.net,REJECT - RULE-SET,CloudflareIP,Wireguard - GEOIP,US,Wireguard - MATCH,DIRECT
Finally let me introduce you to fake-ip.
Simply put, Clash DNS will resolve every name as a fake-ip, which is an address in a particular IPv6 reserved address pool, by default
I’ll give you an example. Let’s say we want to
curl -v http://icanhazip.com. You would get
* Trying 198.18.3.18:80... * Connected to icanhazip.com (198.18.3.18) port 80 (#0) > GET / HTTP/1.1 > Host: icanhazip.com > User-Agent: curl/7.79.1 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 200 OK < Date: Fri, 26 Aug 2022 16:45:50 GMT < (some http response headers redacted here) < 188.8.131.52
The process is like this (simplified):
curl asks the Clash DNS for the IP address of
Clash finds an empty spot in
198.18.0.1/16and answers the A question with
curl opens TCP socket to
198.18.3.18and sends the HTTP request.
The packet to
198.18.3.18gets routed to Clash.
Clash looks up in the fake-ip cache and see which hostname it is associated with:
icanhazip.comin this case.
Clash looks up the policy rules and see:
If there’s an applying rule for
If there’s any
Clash asks every nameservers set up in
184.108.40.206(DoT) here in this example, what is the IP address of
Clash then checks if the resolved IP address applies to any of the above rules.
If any of the rules apply, the outbound target is selected based on the rule.
Otherwise the outbound target specified in the
MATCHrule is selected.
Clash forwards the packet to the target selected above, and will forward back if there is a reply.
You may ask: why fake-ip?
Some proxy protocols connect to the remote host by their hostname. You give them the hostname that you want to connect to.
This means if you’re connecting to
google.com with a SOCKS5 proxy, the IP address of
google.com is actually resolved on the proxy server.
Imagine this scenario without fake-ip:
You’re in EU, you would locally resolve
google.com to their PoP in EU.
Your proxy server is in AS. So connecting to the EU PoP of Google with an AS proxy is obviously a bad idea.
Hence, fake-ip this offers significant speed boost on connections over proxies.
Notice that in the above example, without fake-ip, Clash would still need to resolve
google.com locally even we don’t actually need it. So fake-ip saves one DNS lookup.
Additionally, it offers protection against DNS pollution attack. Some ISP or national firewalls give you false DNS answers. This solves it by resolving remotely.
fake-ip is an RTC standard.
If you do read Chinese, here’s an awesome in-depth article of fake-ip and recursive DNS: https://blog.skk.moe/post/i-have-my-unique-dns-setup/ (shoutout to Sukka)
Using Clash as the gateway #
In your DHCP server options, set the advertised gateway and DNS server to the IP address of Clash.
Your devices will now have default route to the Clash host. They will resolve everything as a fake-ip. Every packet will be intercepted by Clash and forwarded conditionally.
Controlling Clash on the fly #
There are two popular options available for a Clash web interface:
- https://clash.razord.top (https://github.com/Dreamacro/clash-dashboard)
- https://yacd.haishan.me (https://github.com/haishanh/yacd)
You can either use the public URL above or host them with Clash (see config option
The web interfaces connect to the external-controller port of Clash locally on your browser.