In the process of developing software on the Ubuntu system, it is often necessary to use the system’s command line tools. Many of these tools ignore system proxy settings and connect directly to the Internet, resulting in poor network quality. This article introduces the use of iptables to make all traffic go through the proxy.
Create a clash system account
sudo adduser --shell /usr/sbin/nologin clash
Configure crash system service
Creat a service at /etc/systemd/system/clash.service, add following settings.
[Unit]
Description=Clash daemon, A rule-based proxy in Go.
After=network.target
[Service]
Type=simple
Restart=always
User=clash
Group=clash
ExecStart=/path_to_your_clash_folder/clash-linux-amd64 -d /etc/clash
[Install]
WantedBy=multi-user.target
[Unit]
Description=Clash daemon, A rule-based proxy in Go.
After=network.target
[Service]
Type=simple
Restart=always
User=clash
Group=clash
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
ExecStart=/path_to_your_clash_folder/clash-linux-amd64 -d /etc/clash
[Install]
WantedBy=multi-user.target
Configure the ExecStart above to your real clash excutable path and configuration folder.
Configure iptables
Create a bash script named iptables-tproxy.sh
to enable and disable transparent proxy.
#!/bin/bash
remove_iptables_proxy_setting(){
# clear old settings
sudo ip rule del fwmark 666 table 666 2>/dev/null
sudo ip route del local 0.0.0.0/0 dev lo table 666 2>/dev/null
sudo iptables -t nat -F
sudo iptables -t nat -X
sudo iptables -t mangle -F
sudo iptables -t mangle -X
}
add_iptables_proxy_setting(){
# ENABLE ipv4 forward
sudo sysctl -w net.ipv4.ip_forward=1 > /dev/null
# ROUTE RULES
sudo ip rule add fwmark 666 lookup 666
sudo ip route add local 0.0.0.0/0 dev lo table 666
# create clash chain for traffic forwarding
sudo iptables -t mangle -N clash
# skip LAN addresses
sudo iptables -t mangle -A clash -d 0.0.0.0/8 -j RETURN
sudo iptables -t mangle -A clash -d 127.0.0.0/8 -j RETURN
sudo iptables -t mangle -A clash -d 10.0.0.0/8 -j RETURN
sudo iptables -t mangle -A clash -d 172.16.0.0/12 -j RETURN
sudo iptables -t mangle -A clash -d 192.168.0.0/16 -j RETURN
sudo iptables -t mangle -A clash -d 169.254.0.0/16 -j RETURN
sudo iptables -t mangle -A clash -d 224.0.0.0/4 -j RETURN
sudo iptables -t mangle -A clash -d 240.0.0.0/4 -j RETURN
# all other traffic is redirected to port 7893 and marked with 666
sudo iptables -t mangle -A clash -p tcp -j TPROXY --on-port 7893 --tproxy-mark 666
sudo iptables -t mangle -A clash -p udp -j TPROXY --on-port 7893 --tproxy-mark 666
# forward all DNS queries to port 1053
sudo iptables -t nat -I PREROUTING -p udp --dport 53 -j REDIRECT --to 1053
# finally, let all traffic be processed through the clash chain
sudo iptables -t mangle -A PREROUTING -j clash
# create clash_local chain, this chian is responsible for processing traffic sent by the gateway itself
sudo iptables -t mangle -N clash_local
# skip LAN addresses
sudo iptables -t mangle -A clash_local -d 0.0.0.0/8 -j RETURN
sudo iptables -t mangle -A clash_local -d 127.0.0.0/8 -j RETURN
sudo iptables -t mangle -A clash_local -d 10.0.0.0/8 -j RETURN
sudo iptables -t mangle -A clash_local -d 172.16.0.0/12 -j RETURN
sudo iptables -t mangle -A clash_local -d 192.168.0.0/16 -j RETURN
sudo iptables -t mangle -A clash_local -d 169.254.0.0/16 -j RETURN
sudo iptables -t mangle -A clash_local -d 224.0.0.0/4 -j RETURN
sudo iptables -t mangle -A clash_local -d 240.0.0.0/4 -j RETURN
# mark all tranfic sent by localhost with 666
sudo iptables -t mangle -A clash_local -p tcp -j MARK --set-mark 666
sudo iptables -t mangle -A clash_local -p udp -j MARK --set-mark 666
# skip the traffic sent by the clash program itself to prevent an infinite loop (the clash program needs to be started by the "clash" user)
sudo iptables -t mangle -A OUTPUT -p tcp -m owner --uid-owner clash -j RETURN
sudo iptables -t mangle -A OUTPUT -p udp -m owner --uid-owner clash -j RETURN
# let the traffic from the local machine jump to clash_local
# the clash_local chain marked the traffic sent by localhost, and the marked traffic will return to PREROUTING
sudo iptables -t mangle -A OUTPUT -j clash_local
# forward all localhost DNS queries to port 1053
sudo iptables -t nat -A OUTPUT -p udp -m owner ! --uid-owner clash --dport 53 -j REDIRECT --to 1053
# fix ICMP PING(optional)
# this does not guarantee the validity of the ping result (clash doesn't support ICMP forwarding), it just makes it return a result
# you can uncomment following 2 lines and set --to-destination with a reachable address
# sysctl -w net.ipv4.conf.all.route_localnet=1
# sudo iptables -t nat -A PREROUTING -p icmp -d 198.18.0.0/16 -j DNAT --to-destination 127.0.0.1
}
# Check for the argument and call the appropriate function
if [[ $1 == "on" ]]; then
remove_iptables_proxy_setting
add_iptables_proxy_setting
elif [[ $1 == "off" ]]; then
remove_iptables_proxy_setting
else
echo "Usage: ./iptables-tproxy.sh <on|off>"
fi
Excute iptables-tproxy.sh on
to enable transparent proxy and iptables-tproxy.sh off
to disable transparent proxy.