8.3 KiB
Xray Subnet Mimicry (10.0.0.0/8)
Caution
EDUCATIONAL PURPOSES ONLY This project is designed as a technical demonstration of Xray-core's routing capabilities and private subnet mimicry. It is NOT intended for restriction evasion or illegal activities. Use this knowledge responsibly and in compliance with your local laws.
This repository provides a minimal example of how to mimic a contiguous 10.0.0.0/8 private subnet across three distinct servers using Xray-core and reverse tunneling.
Technical Deep-Dive & Architecture
The Virtual Subnet Concept
We are constructing a "Flat Network" (Layer 3) over the public internet. Each node is an authoritative gateway for its own /16 segment of the 10.0.0.0/8 range.
graph TD
subgraph Hub ["Server A (Hub / Portal) - 10.1.0.0/16"]
A_Local["🖥️ Host / Local Clients"]
A_Xray{"⚛️ Xray Core"}
A_DNS["🛡️ AdGuard (10.1.1.3)"]
A_Nginx["🌐 Nginx (10.1.1.4)"]
A_Local -- "TPROXY" --> A_Xray
A_Xray -.-> A_DNS
A_Xray -.-> A_Nginx
end
subgraph SpokeB ["Server B (Spoke / Bridge) - 10.2.0.0/16"]
B_Local["🖥️ Host / Local Clients"]
B_Xray{"⚛️ Xray Core"}
B_Local -- "TPROXY" --> B_Xray
end
subgraph SpokeC ["Server C (Spoke / Bridge) - 10.3.0.0/16"]
C_Local["🖥️ Host / Local Clients"]
C_Xray{"⚛️ Xray Core"}
C_Local -- "TPROXY" --> C_Xray
end
%% Main Tunnels
B_Xray -- "VLESS (Direct)" --> A_Xray
A_Xray -- "VLESS (Reverse)" --> B_Xray
C_Xray -- "VLESS (Direct)" --> A_Xray
A_Xray -- "VLESS (Reverse)" --> C_Xray
%% Example Routing Flow
B_Local -.-> |"1. Dest: 10.3.x.x"| B_Xray
A_Xray -.-> |"2. Route to C"| C_Xray
C_Xray -.-> |"3. Deliver"| C_Local
The Science of Interception (TPROXY)
Unlike REDIRECT which only works for TCP and changes the destination IP to the proxy's IP, TPROXY (Transparent Proxy) allows the proxy to intercept both TCP and UDP while preserving the original packet headers.
- Mechanism: When a packet for
10.x.x.xenters thePREROUTINGorOUTPUTchain, the Linux Mangle table marks it with0x1. - Preservation: Xray's
tunnelinbound (which intercepts the host's traffic) uses\"sockopt\": { \"tproxy\": \"tproxy\" }to read the original destination IP from the socket metadata, allowing it to route the packet as if it were the intended gateway. - Resource: Linux Kernel TPROXY Documentation
Policy Routing (Table 100)
While the iptables rules in our firewall mark intercepted packets (with 0x1), marking alone does not change where a packet goes. The standard Linux routing table only cares about the destination IP. If a packet is destined for 10.3.x.x but there is no physical route, the kernel will send it to the default gateway (the public internet), which will simply drop it.
To make the kernel route traffic based on our firewall mark, we use Policy Routing:
- The Rule (
ip rule):ip rule add fwmark 1 lookup 100tells the Linux networking stack: "Before doing normal routing, if a packet has firewall mark 1, ignore the main routing table and use Table 100 instead." - The Table (
ip route):ip route add local default dev lo table 100populates Table 100 with a single rule: "Treat ALL destination IPs as if they belong to this local machine (local default), and route them to the internal loopback interface (dev lo)." - The Handoff: Because the packet is forced onto the local loopback interface, the
TPROXYmechanism can finally grab it. Xray, which is listening on127.0.0.1:12345with thetproxysocket option, intercepts the packet and reads its original destination. - Loop Prevention: We use
sockopt: { \"mark\": 255 }in Xray's outbounds. This "exempts" Xray's own outbound traffic from being caught by ouriptablesrules (since 255 is not 1), preventing an infinite loop where Xray proxies its own traffic.
The Reverse Proxy Mechanism (Bridge & Portal)
To allow a server behind NAT (Spoke) to be reachable by the Hub, Xray implements a sophisticated Reverse Proxy architecture.
- Bridge (The Spoke): The Bridge actively initiates a connection to the Portal. It acts as the "Client" for the tunnel but functionally behaves as a server once the tunnel is open.
- Portal (The Hub): The Portal listens for these incoming Bridge connections. It uses a "Dummy Domain" (configured in the
reverseobject) to identify the Bridge. When a packet for Spoke B arrives at the Hub, the Hub relays it through the already-open connection from Spoke B. Xray Reverse Proxy Documentation - Multiplexing (Mux): Xray uses Mux.cool by default over these tunnels. This allows hundreds of virtual connections (TCP/UDP streams) to be packed into a single physical TLS connection between the Bridge and the Portal, significantly reducing overhead and bypassing NAT timeouts.
- VLESS Transport: We use VLESS with XTLS for high-performance data encapsulation. Xray VLESS
Setup Guide
Phase 0: Prerequisites (Server A only)
Before starting, Server A (the Hub) requires a valid SSL certificate for Xray's XTLS-Vision.
- Point your domain (
A-DOMAIN) to Server A's public IP. - Generate an SSL certificate (e.g., via Let's Encrypt / Certbot) so that it is available at
/etc/letsencrypt/live/A-DOMAIN/on the host.
Phase 1: Security & Identity
Each connection needs its own cryptographic "Key" (UUID).
- Generate 4 UUIDs:
docker run --rm ghcr.io/xtls/xray-core uuid - Mapping:
UUID_B_REVERSE: Tunnel control for Server B.UUID_B_DIRECT: Data traffic for Server B.UUID_C_REVERSE: Tunnel control for Server C.UUID_C_DIRECT: Data traffic for Server C.
Phase 2: Host Networking (The Glue)
On EVERY server node:
- Initialize Routing: Run routing.sh. This sets up the kernel parameters and the custom routing Table 100.
- Firewall Injection: Open
/etc/ufw/before.rulesand find the*filterheader. CRITICAL PLACEMENT: Insert the contents of your node'sbefore.rulesfile immediately after these lines. The provided files containCOMMITcommands and new table declarations specifically crafted to be pasted here without breaking UFW.Files to Copy:*filter :ufw-before-input - [0:0] :ufw-before-output - [0:0] :ufw-before-forward - [0:0] :ufw-not-local - [0:0] # End required lines- Server A: Copy a/before.rules
- Server B: Copy b/before.rules
- Server C: Copy c/before.rules
- Reload:
sudo ufw reload
Phase 3: Deployment
- Update Configs: In each
xray.jsonc, replace:REPLACE_WITH_UUID_...with your generated IDs.A-DOMAINwith your public domain.A-EXTERNAL-IPwith Server A's public IP.
- Launch:
docker compose up -din thea/,b/, andc/directories. (Note: On Server A, Docker Compose automatically creates the10.1.0.0/16bridge network and assigns static IPs to the AdGuard (10.1.1.3) and Nginx (10.1.1.4) containers.)
Educational Resources & References
- Xray-core Official Documentation: XTLS.github.io
- Project X (Xray) GitHub: XTLS/Xray-core
- Understanding TPROXY: Cloudflare Blog: TPROXY - excellent deep dive into how transparent proxying works at scale.
Copyright & License
Information described here is licensed under the CC-BY-4.0.
Software and tools mentioned or used within this repository are licensed individually under their respective terms and conditions by their original authors.
AI Assistance: Information in this repository was enhanced and formatted with the use of Large Generative Models (sadly, they are incapable of correctly setting up the processes described here by themselves - otherwise, this repository wouldn't exist).
Copyright © 2026 Vladimir Eremin