Transparent proxy with V2ray and clash
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:
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:
- One for traffic routed through the Raspberry Pi (for proxying).
- 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
- Create the
clash
user and its home directory:
sudo useradd -U clash
sudo mkhomedir_helper clash
sudo chown clash:clash /usr/local/bin/clash
- Create or modify the service file at
/etc/systemd/system/clash.service
to define the user asclash
:
[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).
- 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
- Enable IP forwarding:
echo net.ipv4.ip_forward=1 >> /etc/sysctl.conf && sysctl -p
- 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
- Save the script, make it executable, and run it at startup:
chmod +x clash-iptables.sh
sudo ./clash-iptables.sh
- 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