7 minute read


Background

In my previous post, I demonstrated how to set up a proxy server and configure clients on various devices. However, configuring the proxy client on each device can be cumbersome. Additionally, some users may prefer to route all network traffic through a proxy, regardless of whether it’s HTTP, HTTPS, SOCKS5, or other protocols. In such cases, setting up a transparent proxy is more convenient. This proxy acts as a router, enabling all connected devices (including the host itself) to use the proxy. Transparent proxies are also referred to as bypass gateways or soft routers and typically work on TCP/UDP protocols.

This post will guide you step-by-step on setting up a transparent proxy on Linux. To follow along, you should have basic knowledge of Linux and networking.

There are various tools to achieve transparent proxy functionality, such as Proxifier (Windows), Surge for Mac, tun2socks, and dns2socks for Linux. The key to implementing a transparent proxy lies in correctly handling DNS resolution. In this post, I use the built-in DNS configurations of V2Ray and Clash. DNS requests are forwarded using iptables and ip route. Notably, Clash offers robust DNS settings.


Basics

How DNS Works

When you make an HTTP request, the system first sends a DNS query (UDP port 53) with the domain name to the configured DNS server. The server responds with the corresponding IP address. The application then establishes a TCP connection to the target server using this IP and begins data exchange.

A typical connection flow looks like this:

            DNS Query
   ___________________________>
APP<---------------------------DNS Server
  |         Response (IP)
  |         TCP Data Exchange
  |----------------------------------->Website  

When using a proxy, the flow changes. Below are scenarios for different proxy types:


SOCKS5 Proxy

In the SOCKS5 case, the application packs the domain name and TCP data into a SOCKS5 protocol packet. The proxy client forwards this packet to the proxy server, where DNS resolution occurs.

      SOCKS5 (Domain + TCP Data)                    SOCKS5 Data / Proxy Protocol                          DNS Query
APP--------------------------------->Proxy Client------------------------------------->Proxy Server<------------------->DNS Server
                                                                                          |     Response (IP)
                                                                                          |
                                                                                          |     TCP Data
                                                                                          |--------------------------------Website  

In global/transparent proxy setups, not all applications can handle SOCKS5. Thus, a program must intercept DNS requests and process them appropriately.


tun2socks/redir

tun2socks (part of BadVPN) intercepts all incoming TCP connections (regardless of destination IP) and forwards them to a SOCKS server. This method allows applications to use SOCKS without built-in support, even on a Linux router.

In this case, the application sends a DNS request, receives an IP (possibly inaccurate due to a “dirty” DNS server), and then establishes a TCP connection. The local proxy client intercepts and repackages the domain and TCP data into a SOCKS5 packet for forwarding.

         Requested IP
   <_________________________________              Domain + Data (Proxy Protocol)                      DNS Query
APP--------------------------------->Proxy Client<------------------------------------->Proxy Server<------------------->DNS Server
  |              DNS Query             /|\                                                  |     Response (IP)
  |                                     |                                                   |
  |       TCP Data                      |                                                   |     TCP Data     
  |-------------------------------------|                                                   |---------------------------->Website

Fake IP Mode

In this mode, the local proxy client intercepts DNS requests and returns a “fake” IP to the application. The application establishes a connection with the fake IP, while the proxy client maps the fake IP to the actual domain and forwards the data to the proxy server.

          Fake IP
   <_________________________________                  Domain + Data (Proxy Protocol)                    DNS Query
APP--------------------------------->Proxy Client<------------------------------------->Proxy Server<------------------->DNS Server
  |              DNS Query             /|\                                                  |     Response (IP)
  |                                     |                                                   |
  |   TCP Data                          |                                                   |     TCP Data     
  |-------------------------------------|                                                   |---------------------------->Website

Advantages & Disadvantages:

  • Fake IP Mode: Faster, as the real IP doesn’t need to be sent to the application. However, the application cannot determine the website’s real IP.
  • Real IP Mode: Slower, but more accurate for applications requiring the actual IP.

iptables

To manipulate traffic for transparent proxies, familiarity with iptables is essential. Here’s an overview:

iptables Overview

Basic iptables commands:

iptables -L -t {nat,mangle}   # List chains
iptables -N XXXX              # Create chain
iptables -A ...               # Add rule
iptables -D ...               # Delete rule

Requirements

  • A Linux machine.
  • An operational V2Ray or Clash proxy.

Setting Up a V2Ray Transparent Proxy

V2Ray Configuration (config.json)

The dokodemo-door inbound receives all traffic redirected by iptables. Traffic processed by V2Ray is marked with socket mark 255 (0xFF) to avoid loopback.

{
  "routing": {...},
  "inbounds": [
    {
      ...
    },
    {
      "port": 12345,
      "protocol": "dokodemo-door",
      "settings": {
        "network": "tcp,udp",
        "followRedirect": true
      },
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "streamSettings": {
        "sockopt": {
          "tproxy": "redirect"
        }
      }
    }
  ],
  "outbounds": [
    {
      ...
      "streamSettings": {
        ...
        "sockopt": {
          "mark": 255
        }
      }
    }
  ]
}

iptables Configuration

Run these commands with root privileges (sudo su):

lan_ipaddr="192.168.1.1"  # Local router IP
proxy_server="123.123.123.123"  # Proxy server IP
proxy_port="7892"  # Transparent proxy port

# Enable IP forwarding
echo net.ipv4.ip_forward=1 >> /etc/sysctl.conf && sysctl -p

# Route for loopback
ip rule add fwmark 1 table 100
ip route add local 0.0.0.0/0 dev lo table 100

# Proxy local network
iptables -t mangle -N V2RAY
iptables -t mangle -A V2RAY -d ${proxy_server} -j RETURN
iptables -t mangle -A V2RAY -d 127.0.0.1/32 -j RETURN
...

This method uses the REDIRECT approach. For the TPROXY method, refer to this guide.

Setting Up a Clash Transparent Proxy

Clash is a powerful, rule-based proxy tool with features like high-level routing and DNS management. It is widely used due to its flexibility and robust capabilities.

Configuring Clash as a Bypass Gateway

If you’re using a Raspberry Pi as a bypass gateway, you can set a static IP address for it and configure it to act as both a DHCP and DNS server. Alternatively, if your main router supports multiple gateways, you can configure two gateways:

  1. One for traffic routed through the Raspberry Pi (for proxying).
  2. Another for direct routing (normal internet access).

The network overview would look like this:

Phone/PC/Pad
        |
      1 |
        |
+-------v-------+      2      +---------------+
|               |------------->               |
|  WiFi Router  |             |  Raspberry Pi |
|               <-------------|               |
+------+--+-----+      3      +---------------+
       |  |
    3.1|  | 3.2
       |  +---------->  Direct LAN
       v
   +---+---+
   | Proxy |
   +---+---+
       |
       |
       v
 Internet WAN

Avoiding Loop Problems

To prevent traffic loops, create a dedicated user clash and ensure that traffic originating from this user is excluded from the proxying rules.

Steps to Create a User for Clash

  1. Create the clash user and its home directory:
sudo useradd -U clash
sudo mkhomedir_helper clash
sudo chown clash:clash /usr/local/bin/clash
  1. Create or modify the service file at /etc/systemd/system/clash.service to define the user as clash:
[Unit]
Description=clash
After=network.target

[Service]
User=clash
Group=clash
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_ADMIN
ExecStart=/usr/local/bin/clash -d /etc/clash
Restart=on-failure

[Install]
WantedBy=multi-user.target
  • CAP_NET_BIND_SERVICE: Allows the Clash process to bind to privileged ports like 53 (DNS).
  • CAP_NET_ADMIN: Grants the Clash process permissions for network administration (necessary for UDP proxying).
  1. Reload the systemd configuration and enable Clash:
sudo systemctl daemon-reload
sudo systemctl enable clash

Clash DNS Configuration

To ensure reliable DNS resolution, configure Clash to handle DNS queries. Below is a sample DNS configuration for config.yaml:

dns:
  enable: true
  ipv6: false
  listen: 0.0.0.0:1053
  enhanced-mode: redir-host       # Modes: redir-host or fake-ip
  use-hosts: true                 # Use hosts for resolution
  nameserver:
    - 119.29.29.29      # DNSPod
    - 223.5.5.5         # Alibaba DNS
  fallback:
    - tls://8.8.8.8:853         # Google DNS over TLS
    - tls://8.8.4.4:853         # Google DNS over TLS
    - https://1.1.1.1/dns-query # Cloudflare DNS over HTTPS
    - https://dns.google/dns-query # Google DNS over HTTPS
  fallback-filter:
    geoip: true

For fake-ip mode, update the configuration:

  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16  # Fake IP pool

Clash Full Configuration Example

Below is a complete example of config.yaml:

port: 7890
socks-port: 7891
redir-port: 7892
allow-lan: true
mode: Rule
log-level: info
external-controller: 0.0.0.0:9090
secret: ""
external-ui: dashboard

# Define proxies
proxies:
  - name: "Proxy"
    type: http
    server: your.proxy.server
    port: 1234
    username: user
    password: pass

# Proxy groups and rules
proxy-groups:
  - name: "Default"
    type: select
    proxies:
      - Proxy

rules:
  - DOMAIN-SUFFIX,example.com,Default
  - GEOIP,CN,DIRECT
  - MATCH,Default

dns:
  enable: true
  ipv6: false
  listen: 0.0.0.0:1053
  enhanced-mode: redir-host
  nameserver:
    - 119.29.29.29
    - 223.5.5.5
  fallback:
    - tls://8.8.8.8:853
    - tls://8.8.4.4:853
    - https://1.1.1.1/dns-query
    - https://dns.google/dns-query
  fallback-filter:
    geoip: true

Configuring iptables for Clash

  1. Enable IP forwarding:
echo net.ipv4.ip_forward=1 >> /etc/sysctl.conf && sysctl -p
  1. Create a script to set up iptables rules:
#!/bin/bash

IPT=/sbin/iptables
lan_ipaddr=$(/sbin/ip route | awk '/default/ { print $3 }')
dns_port="1053"  
proxy_port="7892" 

# Flush existing rules
$IPT -F

# Create chains
$IPT -t nat -N CLASH_TCP_RULE
$IPT -t nat -F CLASH_TCP_RULE

# Exclude local addresses
$IPT -t nat -A CLASH_TCP_RULE -d 10.0.0.0/8 -j RETURN
$IPT -t nat -A CLASH_TCP_RULE -d 127.0.0.0/8 -j RETURN
$IPT -t nat -A CLASH_TCP_RULE -d 192.168.0.0/16 -j RETURN
$IPT -t nat -A CLASH_TCP_RULE -p tcp --dport 22 -j RETURN
$IPT -t nat -A CLASH_TCP_RULE -p tcp --dport ${proxy_port} -j RETURN

# Redirect remaining TCP traffic
$IPT -t nat -A CLASH_TCP_RULE -p tcp -j REDIRECT --to-ports ${proxy_port}

# DNS redirection
$IPT -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-port ${dns_port}

# Apply rules
$IPT -t nat -A PREROUTING -p tcp -j CLASH_TCP_RULE
  1. Save the script, make it executable, and run it at startup:
chmod +x clash-iptables.sh
sudo ./clash-iptables.sh
  1. To persist the rules after reboot, use iptables-persistent:
sudo apt install iptables-persistent
iptables-save > /etc/iptables/rules.v4

Alternatively, add the script to /etc/rc.local for execution at startup.


Special statement: This tutorial is only for learning and research, thanks.

Comments