Wang Ziting
Posted on May 24, 2022
I bought a very small fanless PC in April 2019, intended to install a Linux then replace my main router in my home, and set up a transparent proxy on it, Finally, I finished it in November 2019.
Hardware and OS
The router using J1900 CPU, installed 2G memory and 128G SSD, and I install an Ubuntu on it. Because Ubuntu is my most used Linux distributions.
Many people believe router that using specially designed hardware may have higher performance. But I think that a Linux on a universal PC hardware will have almost the same performance with special hardware.
Overview
I installed or use the following software on my router:
- dnsmasq for providing DHCP service and DNS cache.
- Clash for providing redir proxy and DNS service.
- iptables for providing NAT and firewall, redirects TCP connections.
I install them by Ansible — a configuration management tool, the source code is available on my GitHub. You can’t use it directly, but you can learn more details about my setup, or modify your own version based on it.
Turn into a router
My router has four RJ45 ports, Let’s assume that the first port (enp1s0
) connected to the Internet — we call it WAN, and the rest ports connect to used by my devices — we call it LAN.
Edit /etc/sysctl.conf
, set the following instructions to enable forwarding for IPv4:
net.ipv4.ip_forward=1
Create /etc/netplan/bridges.yaml
, use NetPlan — Ubuntu’s new network configuration utility to make a bridge interface(called brlan
) between our LAN interfaces, includes enp2s0
, enp3s0
and enp4s0
, then assign 10.0.0.1/24
to it:
network:
version: 2
renderer: networkd
ethernets:
enp1s0:
dhcp4: true
enp2s0:
optional: true
enp3s0:
optional: true
enp4s0:
optional: true
bridges:
brlan:
addresses:
- 10.0.0.1/24
interfaces:
- enp2s0
- enp3s0
- enp4s0
You will need a reboot to make sysctl.conf
and NetPlan take effects.
Run the following commands in shell to setup iptables NAT rules:
iptables -t nat -A POSTROUTING -o enp1s0 -j MASQUERADE
We need to set some of basic firewall rules, we deny all income connections by default unless explicitly permit.
iptables -A INPUT -i enp1s0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -i enp1s0 -p icmp -j ACCEPT
iptables -A INPUT -i enp1s0 -j REJECT --reject-with icmp-port-unreachable
Then install netfilter-persistent
to persistent the rules after reboot:
apt-get install netfilter-persistent
netfilter-persistent save
Now your Linux has become a NAT router.
DHCP
To be a real router, we also need DHCP service, we use dnsmasq to do that:
apt install dnsmasq
Modify the following lines in /etc/dnsmasq.conf
:
interface=brlan
dhcp-range=brlan,10.0.0.1,10.0.0.255,12h
dhcp-option=6,10.0.0.1
Clash
Download release from GitHub, unarchive and move to /usr/bin/clash
, add executable permissions:
curl -o clash.gz https://github.com/Dreamacro/clash/releases/download/v1.1.0/clash-linux-amd64-v1.1.0.gz
gzip -dk clash.gz
mv clash /usr/bin/clash
chmod +x /usr/bin/clash
Create /etc/systemd/system/clash.service
:
[Unit]
Description=clash daemon
[Service]
Type=simple
LimitNOFILE=49152
ExecStart=/usr/bin/clash -d /etc/clash
[Install]
WantedBy=multi-user.target
Prepare a clash config in /etc/clash/config.yml
, the following just showing some key instructions, not the complete configuration, you need to modify by yourself:
redir-port: 7892
mode: rule
proxies:
- { name: 'My Upstream Proxy', type: 'http', server: 'example.com', port: 443}
proxy-groups:
- { name: "Proxy", type: select, proxies: ['My Upstream Proxy']}
rules:
# Domains don't want to be proxy
- DOMAIN-SUFFIX,alipay.com,DIRECT
- DOMAIN-SUFFIX,baidu.com,DIRECT
- DOMAIN-SUFFIX,bilibili.com,DIRECT
- DOMAIN-SUFFIX,douban.com,DIRECT
- DOMAIN-SUFFIX,iqiyi.com,DIRECT
- DOMAIN-SUFFIX,jd.com,DIRECT
- DOMAIN-SUFFIX,qq.com,DIRECT
- DOMAIN-SUFFIX,taobao.com,DIRECT
- DOMAIN-SUFFIX,tmall.com,DIRECT
- DOMAIN-SUFFIX,weibo.com,DIRECT
- DOMAIN-SUFFIX,zhihu.com,DIRECT
# Domain keywords to be proxy
- DOMAIN-KEYWORD,amazon,Proxy
- DOMAIN-KEYWORD,google,Proxy
- DOMAIN-KEYWORD,gmail,Proxy
- DOMAIN-KEYWORD,youtube,Proxy
- DOMAIN-KEYWORD,facebook,Proxy
- DOMAIN-KEYWORD,twitter,Proxy
- DOMAIN-KEYWORD,instagram,Proxy
- DOMAIN-KEYWORD,dropbox,Proxy
# Local address
- IP-CIDR,127.0.0.0/8,DIRECT
- IP-CIDR,172.16.0.0/12,DIRECT
- IP-CIDR,192.168.0.0/16,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
# Finally rules
- GEOIP,CN,DIRECT
- MATCH,Proxy
Redir proxy
We will use iptables to redirect TCP connections to our clash redir port.
First, create a chain for Clash:
iptables -t nat -N CLASH
Ignore connections to local private address:
iptables -t nat -A CLASH -p tcp -d 0.0.0.0/8 -j RETURN
iptables -t nat -A CLASH -p tcp -d 10.0.0.0/8 -j RETURN
iptables -t nat -A CLASH -p tcp -d 127.0.0.0/8 -j RETURN
iptables -t nat -A CLASH -p tcp -d 172.16.0.0/12 -j RETURN
iptables -t nat -A CLASH -p tcp -d 192.168.0.0/16 -j RETURN
Ignore connections to redir port to avoid cyclic redirect:
iptables -t nat -A CLASH -p tcp -m tcp --dport 7892 -j RETURN
We also need ignore connections to your upstream proxy to avoid cyclic redirect:
# if you upstream proxy has a stable address
iptables -t nat -A CLASH -p tcp -d <your upstream proxy address> -j RETURN
# if you upstream proxy has a stable proxy
iptables -t nat -A CLASH -p tcp -m tcp --dport <your upstream proxy port> -j RETURN
Then, redirect to clash:
iptables -t nat -A CLASH -p tcp -j REDIRECT --to-ports 7892
Send traffics from LAN to the CLASH chain:
iptables -t nat -A PREROUTING -s 10.0.0.0/24 -p tcp -j CLASH
Now, all of your outgoing connections will be redirect to Clash.
Troubles about DNS
Unlike a system proxy, iptables redirect traffic on the IP layer, it uses IP instead of a domain. So the domain-related rules in Clash don’t work.
The DNS query is sent by UDP, which can’t redirect by the previous rules, and may produce an incorrect result. We are going to use Clash’s DNS to replace systemd-resolved
— the default DNS resolver of Ubuntu.
Disable systemd-resolved
:
systemctl disable systemd-resolved
systemctl stop systemd-resolved
Modify Clash config:
dns:
enable: true
ipv6: false
listen: 127.0.0.53:53
enhanced-mode: redir-host
nameserver:
- 119.29.29.29
- 223.5.5.5
fallback:
- 'tls://1.1.1.1:853'
- 'tls://8.8.8.8:853'
- 'tls://9.9.9.9:853'
Clash will resolve domains by servers in nameserver section and fallback section in the same time.
Servers in nameserver section are fast but unreliable, if it returns an address which GEOIP country is CN
, we use that; otherwise, we wait for the result form servers in fallback
section.
In this process, Clash will remember the mapping from domain to IP, so Clash will know the domain of a redirected connection, and apply domain-related rules.
Now, Clash will provide correct DNS resolve on 127.0.0.53:53
. Then we need set dnsmasq as a DNS cache server, Modify the following lines in /etc/dnsmasq.conf
:
port=53
no-resolv
server=127.0.0.53
cache-size=1000
Modify /etc/resolv.conf
to use dnsmasq as local resolver:
nameserver 127.0.0.1
Other components in my case
- pppd for providing PPPoE connection.
- netdata for monitoring dashboard.
Posted on May 24, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.