1682 字
8 分钟
Linux Desktop 上的 Xray 裸核部署指南
本文档记录在 Debian 13 (Trixie) 桌面环境下部署 Xray-core TProxy IPv4/IPv6 双栈透明代理的流程。
本文的网络拓扑为单机桌面模式,普通用户产生的 TCP/UDP 流量 Xray 接管并按规则分流,Xray 自身发出的外部流量及系统 root 流量则直接放行。
资源文件
- 安装工具包:
Terminal window sudo apt updatesudo apt install nftables iproute2 curl - 前往 Xray-core Releases 下载
Xray-linux-64.zip(推荐 Pre-release) - 前往 MetaCubeX/meta-rules-dat 下载
geoip.dat、geosite.dat规则文件
目录结构
建议规划如下部署目录:
/opt/proxy/xray/├── xray # Xray-core 可执行文件├── config/│ ├── xray.yaml # Xray 运行时配置文件 (自行创建)│ └── xray-tproxy.nft # nftables 声明式规则集 (自行创建)├── geo/ # 规则库资产目录│ ├── geoip.dat│ └── geosite.dat├── logs/ # 日志│ ├── access.log│ └── error.log└── scripts/ # 脚本 ├── tproxy-up.sh # 规则加载脚本 ├── tproxy-down.sh # 规则清理脚本 └── rotate-logs.py # 日志轮转脚本 (可选)创建目录:
sudo install -d -o root -g root -m 755 /opt/proxy/xraysudo install -d -o root -g root -m 755 /opt/proxy/xray/configsudo install -d -o root -g root -m 755 /opt/proxy/xray/geosudo install -d -o root -g root -m 755 /opt/proxy/xray/scriptssudo install -d -o root -g root -m 755 /opt/proxy/xray/logs将下载的 xray 二进制和 geoip.dat、geosite.dat 放入对应目录,并赋予权限:
sudo chmod 755 /opt/proxy/xray/xraysudo chmod 644 /opt/proxy/xray/geo/*.dat配置文件
接下来书写核心配置文件。
1. Xray 配置文件
与 Windows 几乎一致,不同的地方是入站和部分路由
入站需要改为 dokodemo-door / tunnel
涉及到进程的路由建议删除
inbounds:# 0. TProxy 端口入站 (双栈混合监听,IPv4/IPv6 共用 12345)- tag: tproxy-in port: 12345 protocol: dokodemo-door settings: network: tcp,udp followRedirect: true sniffing: enabled: true destOverride: [http, tls, quic] metadataOnly: false routeOnly: false streamSettings: sockopt: tproxy: tproxy # 可选显式设置 IPv6 transparent,弥补 Xray 对 IPv6 TPROXY inbound 的默认 sockopt 覆盖 # customSockopt: # - system: linux # network: tcp # type: int # level: "41" # opt: "75" # value: "1" # - system: linux # network: udp # type: int # level: "41" # opt: "75" # value: "1"
# 1. Socks 入站- tag: socks-in port: 10808 listen: 127.0.0.1 protocol: socks settings: auth: noauth udp: true sniffing: enabled: true destOverride: [http, tls, quic] metadataOnly: false routeOnly: false
# 2. HTTP 入站- tag: http-in port: 10809 listen: 127.0.0.1 protocol: http sniffing: enabled: true destOverride: [http, tls, quic] metadataOnly: false routeOnly: false2. nftables 规则文件
#!/usr/sbin/nft -f
table inet xray { # 绕行保留地址与局域网网段 (IPv4) set reserved_v4 { type ipv4_addr 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, 224.0.0.0/4, 240.0.0.0/4 } }
# 绕行保留地址与特殊网段 (IPv6) set reserved_v6 { type ipv6_addr flags interval elements = { ::1/128, fe80::/10, fc00::/7, ff00::/8 } }
chain prerouting { type filter hook prerouting priority mangle; policy accept;
# 匹配已建立透明代理连接的 TCP 数据包,打标后直通 meta l4proto tcp socket transparent 1 socket wildcard 0 meta mark set 1 accept
# 绕行无需代理的保留/私网地址 ip daddr @reserved_v4 return ip6 daddr @reserved_v6 return
# 绕行局域网非 DNS 流量(放行 53 端口) ip daddr 192.168.0.0/16 tcp dport != 53 return ip daddr 192.168.0.0/16 udp dport != 53 return
# 放行 Xray 自身出站打标流量 meta mark 0xff return
# 透明代理转发 TCP 与 UDP 流量至本地 12345 端口 meta l4proto { tcp, udp } tproxy to :12345 meta mark set 1 accept }
chain output { type route hook output priority mangle; policy accept;
# 绕行无需代理的保留/私网地址 ip daddr @reserved_v4 return ip6 daddr @reserved_v6 return
# 绕行局域网非 DNS 流量 ip daddr 192.168.0.0/16 tcp dport != 53 return ip daddr 192.168.0.0/16 udp dport != 53 return
# 放行 Xray 自身出站流量及 root 权限进程流量 meta mark 0xff return meta skuid root return
# 拦截用户 your_username 发出的 TCP 和 UDP 流量,打上 mark 1 以触发策略路由重定向至 lo meta skuid "你地鸣至" meta l4proto { tcp, udp } meta mark set 1 }}3. 启停脚本
启动脚本 tproxy-up.sh
负责配置策略路由(IPv4 table 100 / IPv6 table 106)并加载 nftables 配置。
#!/bin/bash
set -eecho "[INFO] 加载 Xray TProxy (nftables) 规则..."
# 配置策略路由(IPv4 table 100 / IPv6 table 106)ip route add local default dev lo table 100 2>/dev/null || trueip rule add fwmark 1 table 100 2>/dev/null || trueip -6 route add local default dev lo table 106 2>/dev/null || trueip -6 rule add fwmark 1 table 106 2>/dev/null || true
# 加载 nftables 规则/usr/sbin/nft -f /opt/proxy/xray/config/xray-tproxy.nft
echo "[INFO] Xray TProxy (nftables) 规则加载完成。"停止脚本 tproxy-down.sh
#!/bin/bash
echo "[INFO] 清理 Xray TProxy 规则..."
# 卸载并销毁 nftables 表/usr/sbin/nft delete table inet xray 2>/dev/null || true
# 清理策略路由(IPv4 table 100 / IPv6 table 106)ip rule del fwmark 1 table 100 2>/dev/null || trueip route flush table 100 2>/dev/null || trueip -6 rule del fwmark 1 table 106 2>/dev/null || trueip -6 route flush table 106 2>/dev/null || true
echo "[INFO] Xray TProxy (nftables) 规则清理完成。"赋予执行权限:
sudo chmod 755 /opt/proxy/xray/scripts/*.shSystemd 服务
将 Xray 注册为系统级守护进程,并在启动与退出时自动调用启停脚本:
[Unit]Description=Xray Core Service (Standalone nftables TProxy Mode)After=network.target nss-lookup.target
[Service]Type=simpleUser=rootWorkingDirectory=/opt/proxy/xrayEnvironment="XRAY_LOCATION_ASSET=/opt/proxy/xray/geo"RuntimeDirectory=xrayRuntimeDirectoryMode=0755ExecStart=/opt/proxy/xray/xray run -config /opt/proxy/xray/config/xray.yamlExecStartPost=/opt/proxy/xray/scripts/tproxy-up.shExecStopPost=/opt/proxy/xray/scripts/tproxy-down.shRestart=on-failureRestartSec=3sLimitNOFILE=1000000AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAWCapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
[Install]WantedBy=multi-user.target启动
1. 启动服务
修改日志文件属主为 root,随后启动服务:
sudo touch /opt/proxy/xray/logs/access.log /opt/proxy/xray/logs/error.logsudo chown root:root /opt/proxy/xray/logs/*.logsudo chmod 644 /opt/proxy/xray/logs/*.log
sudo systemctl daemon-reloadsudo systemctl enable --now xray查看服务状态:
sudo systemctl status xray --no-pager查看 nftables 规则是否已加载:
sudo /usr/sbin/nft list table inet xray查看策略路由:
ip rule show | grep 100ip -6 rule show | grep 106测试双栈连通性:
# 验证 IPv4 国内直连curl -4 -I --max-time 5 https://www.baidu.com
# 验证 IPv6 国内直连curl -6 -I --max-time 5 https://www.baidu.com
# 验证海外节点代理出站curl -I --max-time 5 https://www.google.com实时日志:
tail -n 20 /opt/proxy/xray/logs/access.log期望输出应类似下面的,表明双栈流量均成功经由 TProxy 分流:
accepted tcp:172.217.119.4:443 [tproxy-in -> proxy]accepted tcp:[2001:4860:4843:400::]:443 [tproxy-in -> proxy]accepted tcp:[2409:...]:443 [tproxy-in -> direct]完结撒花了,下文是可选配置。
可选增强
1. 日志自动轮转
Xray-core 自身不提供日志文件按大小自动切割能力。可以通过 Python 脚本结合 Xray 提供的 xray api restartlogger 实现平滑轮转。
点击展开/收起 rotate-logs.py
#!/usr/bin/env python3# -*- coding: utf-8 -*-"""Xray 日志自动轮转脚本 (单文件超过 5 MB 触发,保留 3 份历史备份)"""
import datetimeimport globimport osimport shutilimport subprocess
XRAY_BIN = "/opt/proxy/xray/xray"LOG_DIR = "/opt/proxy/xray/logs"API_SERVER = "127.0.0.1:10085"MAX_LOG_BYTES = 5 * 1024 * 1024MAX_BACKUP_COUNT = 3
def prune_backups(log_path): pattern = f"{log_path}.*.bak" backups = sorted(glob.glob(pattern), key=os.path.getmtime) while len(backups) > MAX_BACKUP_COUNT: oldest = backups.pop(0) try: os.remove(oldest) except Exception: pass
def restart_logger(): cmd = [XRAY_BIN, "api", "restartlogger", f"-server={API_SERVER}"] res = subprocess.run(cmd, capture_output=True, text=True, timeout=5) if res.returncode != 0: raise RuntimeError(f"restartlogger failed: {res.stderr or res.stdout}")
def main(): targets = [] for name in ("access.log", "error.log"): p = os.path.join(LOG_DIR, name) if os.path.exists(p) and os.path.getsize(p) >= MAX_LOG_BYTES: ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") targets.append((p, f"{p}.{ts}.bak"))
if not targets: return
# 1. 优先尝试:原子重命名 + gRPC API restartlogger try: for orig, bak in targets: os.rename(orig, bak) restart_logger() for orig, _ in targets: prune_backups(orig) except Exception: # 2. 失败自动降级到 copytruncate for orig, bak in targets: if not os.path.exists(orig) and os.path.exists(bak): os.rename(bak, orig) shutil.copy2(orig, bak) with open(orig, "r+", encoding="utf-8", errors="ignore") as f: f.truncate(0) prune_backups(orig)
if __name__ == "__main__": main()配合 Systemd Timer 每天执行一次日志检查:
[Unit]Description=Run Xray log rotation daily
[Timer]OnBootSec=10minOnCalendar=dailyPersistent=trueUnit=xray-rotate.service
[Install]WantedBy=timers.target[Unit]Description=Rotate Xray logs
[Service]Type=oneshotExecStart=/usr/bin/python3 /opt/proxy/xray/scripts/rotate-logs.py Linux Desktop 上的 Xray 裸核部署指南
https://blog.rokutosei.com/posts/proxy/debian13-xray-tproxy/