• Create an xray/sing-box configuration with dokodemo-door/tproxy inbound listening on 127.0.0.1 port 12345. Run xray systemd service, or sing-box using the systemd service below.

  • /etc/systemd/system/[email protected]/99-transparent-proxy.conf

    [Service]
    ExecStartPost=/usr/sbin/ip rule add fwmark 1 table 1
    ExecStartPost=/usr/sbin/ip route add local default dev lo table 1
    ExecStartPost=/usr/sbin/nft -f /etc/nftables/bsbf_bonding.nft
    
    ExecStopPost=/usr/sbin/nft delete table bsbf_bonding
    ExecStopPost=/usr/sbin/ip route flush table 1
    ExecStopPost=/usr/sbin/ip rule delete fwmark 1 table 1
    
  • /usr/lib/systemd/system/[email protected]

    [Unit]
    Description=sing-box service
    Documentation=https://sing-box.sagernet.org
    After=network.target nss-lookup.target network-online.target
    
    [Service]
    CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
    AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
    
    ExecStart=/usr/bin/sing-box -D /var/lib/sing-box-%i -c /etc/sing-box/%i.json run
    ExecStartPost=/usr/sbin/ip rule add fwmark 1 table 1 priority 0
    ExecStartPost=/usr/sbin/ip route add local default dev lo table 1
    ExecStartPost=/usr/sbin/nft -f /etc/nftables/transparent_proxy.nft
    
    ExecStopPost=/usr/sbin/nft delete table transparent_proxy
    ExecStopPost=/usr/sbin/ip route flush table 1
    ExecStopPost=/usr/sbin/ip rule delete fwmark 1 table 1
    
    ExecReload=/bin/kill -HUP $MAINPID
    Restart=on-failure
    RestartSec=10s
    LimitNOFILE=infinity
    
    [Install]
    WantedBy=multi-user.target
    
  • /etc/nftables/transparent_proxy.nft

    # Make packets from client destined to an IPv4 address that is local to the
    # system bypass the proxy. This includes unicast DHCP Request packets with the
    # IPv4 daddr not being a private IPv4 address, for which we had to put the 'udp
    # dport 67' rule to have it bypass the proxy. This rule ensures that all traffic
    # from client that is destined to the router bypasses the proxy. This covers the
    # case that if the LAN IP address of the router is not a private IPv4 address,
    # it wouldn't bypass the proxy.
    #
    # This makes it so that all packets that are supposed to be routed will be
    # tproxied, except packets with a private IPv4 address as daddr.
    #
    # Author: Chester A. Unal <[email protected]>
    
    table ip transparent_proxy {
    	set byp4 {
    		typeof ip daddr
    		flags interval
    		elements = { 0.0.0.0/8, 10.0.0.0/8,
    			     100.64.0.0/10, 127.0.0.0/8,
    			     169.254.0.0/16, 172.16.0.0/12,
    			     192.0.0.0/24, 192.0.2.0/24,
    			     192.88.99.0/24, 192.168.0.0/16,
    			     198.18.0.0/15, 198.51.100.0/24,
    			     203.0.113.0/24, 224.0.0.0/4,
    			     240.0.0.0/4 }
    	}
    
    	chain prerouting_mangle {
    		type filter hook prerouting priority mangle; policy accept;
    		ip daddr @byp4 return
    		fib daddr type != local meta l4proto { tcp, udp } tproxy ip to 127.0.0.1:12345 meta mark set 0x00000001
    	}
    
    	chain output_mangle {
    		type route hook output priority mangle; policy accept;
    		meta mark 0x00000002 return
    		oifname != "lo" meta l4proto { tcp, udp } meta mark set 0x00000001
    	}
    }