✧ 君と集まって星座になれたら ✦ ROKUTOSEI
1682 字
8 分钟
Linux Desktop 上的 Xray 裸核部署指南

本文档记录在 Debian 13 (Trixie) 桌面环境下部署 Xray-core TProxy IPv4/IPv6 双栈透明代理的流程。

本文的网络拓扑为单机桌面模式,普通用户产生的 TCP/UDP 流量 Xray 接管并按规则分流,Xray 自身发出的外部流量及系统 root 流量则直接放行。


资源文件#

  • 安装工具包:
    Terminal window
    sudo apt update
    sudo 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 # 日志轮转脚本 (可选)

创建目录:

Terminal window
sudo install -d -o root -g root -m 755 /opt/proxy/xray
sudo install -d -o root -g root -m 755 /opt/proxy/xray/config
sudo install -d -o root -g root -m 755 /opt/proxy/xray/geo
sudo install -d -o root -g root -m 755 /opt/proxy/xray/scripts
sudo install -d -o root -g root -m 755 /opt/proxy/xray/logs

将下载的 xray 二进制和 geoip.dat、geosite.dat 放入对应目录,并赋予权限:

Terminal window
sudo chmod 755 /opt/proxy/xray/xray
sudo chmod 644 /opt/proxy/xray/geo/*.dat

配置文件#

接下来书写核心配置文件。

1. Xray 配置文件#

与 Windows 几乎一致,不同的地方是入站和部分路由

入站需要改为 dokodemo-door / tunnel

涉及到进程的路由建议删除

/opt/proxy/xray/config/xray.yaml
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: false

2. nftables 规则文件#

/opt/proxy/xray/config/xray-tproxy.nft
#!/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 配置。

/opt/proxy/xray/scripts/tproxy-up.sh
#!/bin/bash
set -e
echo "[INFO] 加载 Xray TProxy (nftables) 规则..."
# 配置策略路由(IPv4 table 100 / IPv6 table 106)
ip route add local default dev lo table 100 2>/dev/null || true
ip rule add fwmark 1 table 100 2>/dev/null || true
ip -6 route add local default dev lo table 106 2>/dev/null || true
ip -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#

/opt/proxy/xray/scripts/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 || true
ip route flush table 100 2>/dev/null || true
ip -6 rule del fwmark 1 table 106 2>/dev/null || true
ip -6 route flush table 106 2>/dev/null || true
echo "[INFO] Xray TProxy (nftables) 规则清理完成。"

赋予执行权限:

Terminal window
sudo chmod 755 /opt/proxy/xray/scripts/*.sh

Systemd 服务#

将 Xray 注册为系统级守护进程,并在启动与退出时自动调用启停脚本:

/etc/systemd/system/xray.service
[Unit]
Description=Xray Core Service (Standalone nftables TProxy Mode)
After=network.target nss-lookup.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/proxy/xray
Environment="XRAY_LOCATION_ASSET=/opt/proxy/xray/geo"
RuntimeDirectory=xray
RuntimeDirectoryMode=0755
ExecStart=/opt/proxy/xray/xray run -config /opt/proxy/xray/config/xray.yaml
ExecStartPost=/opt/proxy/xray/scripts/tproxy-up.sh
ExecStopPost=/opt/proxy/xray/scripts/tproxy-down.sh
Restart=on-failure
RestartSec=3s
LimitNOFILE=1000000
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW
[Install]
WantedBy=multi-user.target

启动#

1. 启动服务#

修改日志文件属主为 root,随后启动服务:

Terminal window
sudo touch /opt/proxy/xray/logs/access.log /opt/proxy/xray/logs/error.log
sudo chown root:root /opt/proxy/xray/logs/*.log
sudo chmod 644 /opt/proxy/xray/logs/*.log
sudo systemctl daemon-reload
sudo systemctl enable --now xray

查看服务状态:

Terminal window
sudo systemctl status xray --no-pager

查看 nftables 规则是否已加载:

Terminal window
sudo /usr/sbin/nft list table inet xray

查看策略路由:

Terminal window
ip rule show | grep 100
ip -6 rule show | grep 106

测试双栈连通性:

Terminal window
# 验证 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

实时日志:

Terminal window
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
/opt/proxy/xray/scripts/rotate-logs.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Xray 日志自动轮转脚本 (单文件超过 5 MB 触发,保留 3 份历史备份)"""
import datetime
import glob
import os
import shutil
import 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 * 1024
MAX_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 每天执行一次日志检查:

/etc/systemd/system/xray-rotate.timer
[Unit]
Description=Run Xray log rotation daily
[Timer]
OnBootSec=10min
OnCalendar=daily
Persistent=true
Unit=xray-rotate.service
[Install]
WantedBy=timers.target
/etc/systemd/system/xray-rotate.service
[Unit]
Description=Rotate Xray logs
[Service]
Type=oneshot
ExecStart=/usr/bin/python3 /opt/proxy/xray/scripts/rotate-logs.py
Rokutosei
Rokutosei ✦ 六等星
“君と集まって星座になれたら”
Linux Desktop 上的 Xray 裸核部署指南
https://blog.rokutosei.com/posts/proxy/debian13-xray-tproxy/
作者
Rokutosei
发布于
2026-09-11
许可协议
CC BY-NC-SA 4.0
✧ 文章目录 ✦
1
资源文件
2
目录结构
3
配置文件
1. Xray 配置文件
2. nftables 规则文件
3. 启停脚本
4
Systemd 服务
5
启动
1. 启动服务
6
可选增强
1. 日志自动轮转