Files
08413599f4 · 2026-05-18 00:53:22 +08:00
History
..
2026-05-18 00:53:22 +08:00
2026-05-18 00:53:22 +08:00
2026-05-18 00:53:22 +08:00
2026-05-18 00:53:22 +08:00
2026-05-18 00:53:22 +08:00
2026-05-18 00:53:22 +08:00
2026-05-18 00:53:22 +08:00

Ubuntu Xray 透明代理容器

这个目录构造的是一个基于 ubuntu:24.04 的测试/编译容器。容器启动后由 s6-overlay 管理 Xray,并用 iptables 把容器内部 IPv4 TCP 出站流量透明转发到 Xray。

推荐使用 .env + Docker Compose 测试:

docker compose --env-file .env -f compose.yml run --rm proxy-on

总览

flowchart LR
    Env[.env: XRAY_URL] --> Compose[Docker Compose]
    Compose --> Container[Ubuntu 容器]
    Container --> S6[s6-overlay /init]
    S6 --> Init[cont-init.d 初始化]
    Init --> Config[生成或读取 Xray 配置]
    Init --> IPv6[默认关闭 IPv6]
    Init --> Iptables[设置 IPv4 iptables 透明代理]
    Init --> Service[按需创建 xray 服务]
    S6 --> Xray[监督 xray longrun]
    App[容器内命令 / 编译任务 / curl] --> Iptables
    Iptables --> Xray
    Xray --> Remote[VLESS 节点]

核心目标是:你只在 .env 里提供 XRAY_URL=vless://...,容器自己完成配置生成、透明代理设置、Xray 启动和进程维护。

镜像构造

flowchart TD
    Base[ubuntu:24.04] --> Mirror[替换 apt 源为中科大镜像]
    Mirror --> Packages[安装 curl / iptables / jq / unzip / xz-utils]
    Packages --> S6Install[安装 s6-overlay]
    S6Install --> XrayInstall[下载并安装 Xray-core]
    XrayInstall --> User[创建 xray 系统用户]
    User --> CopyScripts[复制运行时 scripts/]
    CopyScripts --> CopyRootfs[复制 rootfs/]
    CopyRootfs --> Entry[ENTRYPOINT /init]

文件职责

flowchart TD
    Ubuntu[Ubuntu/] --> EnvFile[.env]
    Ubuntu --> ComposeFile[compose.yml]
    Ubuntu --> Dockerfile[Dockerfile]
    Ubuntu --> EnvExample[.env.example]
    Ubuntu --> Test[test.bat]
    Ubuntu --> Scripts[scripts/]
    Ubuntu --> Rootfs[rootfs/]

    Scripts --> Resolve[resolve-xray-config.sh]
    Scripts --> Convert[xray-url-to-config.sh]
    Scripts --> Transparent[transparent-proxy.sh]
    Scripts --> IPv6[ipv6-mode.sh]
    Scripts --> Enable[enable-xray-service.sh]
    Scripts --> Run[xray-service-run.sh]

    Rootfs --> Init[etc/cont-init.d/]
    Rootfs --> Finish[etc/cont-finish.d/]

    Init --> InitIPv6[05-ipv6-mode]
    Init --> InitConfig[10-xray-config]
    Init --> InitProxy[20-transparent-proxy]
    Init --> InitService[30-enable-xray-service]
    Finish --> Cleanup[90-transparent-proxy-cleanup]

.env

运行时配置文件。默认包含 IMAGEXRAY_URLXRAY_IPV6_ENABLEDXRAY_DEBUG_CONFIG。真实 .env 不入库,使用 .env.example 复制后填写真实节点。

.env.example

可提交的配置模板,不包含真实节点密钥。

compose.yml

定义测试服务和运行时环境。测试命令由 test.bat 传入,不再维护额外的测试 runner 文件。

Dockerfile

构建镜像,安装依赖、s6-overlay、Xray,并设置 ENTRYPOINT ["/init"]

test.bat

本地测试脚本。调用 Docker Compose 构建镜像,并依次运行 IPv6 与代理开关测试。

scripts/xray-url-to-config.sh

把受支持的 vless://... 转换成 Xray JSON 配置,写到 /tmp/xray.generated.json。配置由 jq 生成,并先写入临时文件,成功后再原子替换目标文件。当前不是通用转换器,只支持下面两类链接:

vless + tcp + reality + flow=xtls-rprx-vision + headerType=none
vless + ws + security=none + host + path

其他组合会直接报错,不会尝试生成可能错误的配置。

scripts/resolve-xray-config.sh

决定使用哪个配置。如果 /etc/xray/config.json 存在,就使用它;否则尝试从 XRAY_URL 自动生成。

scripts/transparent-proxy.sh

负责 iptables 透明代理规则的 setupcleanup

scripts/ipv6-mode.sh

默认关闭容器内 IPv6。设置 XRAY_IPV6_ENABLED=1 时保留 IPv6。

scripts/enable-xray-service.sh

根据 XRAY_ENABLED 决定是否创建 /etc/services.d/xray/run。关闭代理时不会创建空跑的 xray 服务。

scripts/xray-service-run.sh

真正的 Xray s6 longrun 服务脚本。

启动生命周期

sequenceDiagram
    participant Docker
    participant S6 as s6-overlay /init
    participant Init as cont-init.d
    participant Scripts as scripts/
    participant Xray as xray service
    participant Cmd as CMD
    participant Finish as cont-finish.d

    Docker->>S6: 启动 /init
    S6->>Init: 05-ipv6-mode
    Init->>Scripts: ipv6-mode.sh
    S6->>Init: 10-xray-config
    Init->>Scripts: resolve-xray-config.sh
    S6->>Init: 20-transparent-proxy
    Init->>Scripts: transparent-proxy.sh setup
    S6->>Init: 30-enable-xray-service
    Init->>Scripts: enable-xray-service.sh
    S6->>Xray: 启动动态创建的 xray/run
    S6->>Cmd: 执行 Docker CMD
    Xray-->>S6: 如果崩溃,s6 自动重启
    Cmd-->>S6: CMD 结束
    S6->>Finish: 执行 cleanup
    Finish->>Scripts: transparent-proxy.sh cleanup

配置选择逻辑

flowchart TD
    Start[启动 Xray 配置解析] --> HasFile{XRAY_CONFIG 文件存在?}
    HasFile -->|是| UseFile[使用 /etc/xray/config.json]
    HasFile -->|否| HasUrl{XRAY_URL 是否存在?}
    HasUrl -->|是| Generate[从 vless URL 生成临时 JSON]
    HasUrl -->|否| Fail[启动失败: 缺少配置]
    Generate --> Supported{链接类型受支持?}
    Supported -->|是| Service[创建并启动 xray 服务]
    Supported -->|否| FailType[启动失败: 不支持的链接类型]
    UseFile --> Service

推荐方式是在 .env 里配置:

XRAY_URL=vless://...

高级方式是挂载完整 JSON

-v D:\path\config.json:/etc/xray/config.json:ro

如果挂载了 /etc/xray/config.json,它会优先于 XRAY_URL

透明代理数据路径

flowchart LR
    App[容器内普通进程] -->|访问外部 IPv4 TCP| Output[iptables nat OUTPUT]
    Output -->|REDIRECT| Door[dokodemo-door 127.0.0.1:12345]
    Door --> Xray[Xray]
    Xray -->|VLESS| Server[远端代理节点]
    Server --> Internet[目标网站]

    Xray -. xray 用户流量排除 .-> Output

关键点:

  1. 默认只处理 IPv4 TCP。
  2. 使用 nat OUTPUT,影响容器内部进程发起的出站连接。
  3. xray 用户自己的流量会被 RETURN 排除,避免代理流量再次进入代理导致死循环。
  4. 默认关闭 IPv6,设置 XRAY_IPV6_ENABLED=1 才保留 IPv6。
  5. UDP 透明代理没有实现;如果需要 UDP,要改成 TPROXY + 策略路由。

环境变量

flowchart TD
    Env[.env / compose environment] --> Enabled[XRAY_ENABLED]
    Env --> Url[XRAY_URL]
    Env --> Config[XRAY_CONFIG]
    Env --> Transparent[XRAY_TRANSPARENT]
    Env --> Port[XRAY_REDIRECT_PORT]
    Env --> IPv6[XRAY_IPV6_ENABLED]
    Env --> Debug[XRAY_DEBUG_CONFIG]

    Enabled --> EnabledDesc[1 启动代理 / 0 完全关闭]
    Url --> UrlDesc[vless:// 链接]
    Config --> ConfigDesc[默认 /etc/xray/config.json]
    Transparent --> TransparentDesc[1 设置 iptables / 0 不设置]
    Port --> PortDesc[默认 12345]
    IPv6 --> IPv6Desc[0 默认关闭 IPv6 / 1 保留 IPv6]
    Debug --> DebugDesc[1 打印生成配置]

XRAY_ENABLED

1  启动 Xray 和透明代理,默认值
0  不启动 Xray,用于 proxy-off 对比测试

XRAY_URL

传入受支持的 vless://...,容器自动生成 Xray JSON 配置。

XRAY_IPV6_ENABLED

0  默认值,启动时尽量关闭容器 IPv6
1  保留 IPv6

XRAY_DEBUG_CONFIG

0  默认值,不打印生成的 Xray JSON
1  打印生成的 Xray JSON,仅用于调试

XRAY_CONFIG

默认:

/etc/xray/config.json

XRAY_TRANSPARENT

1  设置 iptables 透明代理,默认值
0  只启动 Xray,不设置透明转发

XRAY_REDIRECT_PORT

默认:

12345

测试

运行:

Ubuntu\test.bat

测试脚本流程:

flowchart TD
    Build[docker compose build] --> On[proxy-on]
    On --> OnRun[读取 .env 并启用透明代理]
    OnRun --> OnCurl[curl google.com]
    OnCurl --> Off[proxy-off]
    Off --> OffRun[XRAY_ENABLED=0]
    OffRun --> OffCurl[curl google.com]
    OffCurl --> Compare[对比结果]

也可以手动运行:

docker compose --env-file .env -f compose.yml build
docker compose --env-file .env -f compose.yml run --rm --no-deps proxy-on bash
docker compose --env-file .env -f compose.yml run --rm --no-deps proxy-off bash

proxy-on 日志里应该出现:

[transparent >> proxy]

这说明流量经过 Xray。

proxy-off 应该看到:

Xray disabled by XRAY_ENABLED=0.

如果当前网络允许直连,proxy-off 会输出:

[INFO] HTTP request without xray succeeded directly

如果当前网络不允许直连,proxy-off 会输出:

[PASS] HTTP request without xray is blocked

测试脚本内部使用:

curl --noproxy '*' -I -L http://google.com

这是为了避免 HTTP_PROXYHTTPS_PROXY、Docker Desktop 代理设置污染测试。

注意事项

运行透明代理必须加:

--cap-add NET_ADMIN

否则容器内没有权限设置 iptables

Windows 开启系统代理时,Docker Desktop/WSL 可能继承代理设置。可以用这些命令只读检查:

docker info
netsh winhttp show proxy
Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' |
  Select-Object ProxyEnable,ProxyServer,AutoConfigURL

如果 docker info 里出现类似:

HTTP Proxy: http.docker.internal:3128
HTTPS Proxy: http.docker.internal:3128

说明 Docker Desktop 自己也配置了代理,测试直连/代理效果时要特别注意。