363 lines
9.5 KiB
Markdown
363 lines
9.5 KiB
Markdown
# Ubuntu Xray 透明代理容器
|
||
|
||
这个目录构造的是一个基于 `ubuntu:24.04` 的测试/编译容器。容器启动后由 `s6-overlay` 管理 Xray,并用 `iptables` 把容器内部 IPv4 TCP 出站流量透明转发到 Xray。
|
||
|
||
推荐使用 `.env` + Docker Compose 测试:
|
||
|
||
```bat
|
||
docker compose --env-file .env -f compose.yml run --rm proxy-on
|
||
```
|
||
|
||
## 总览
|
||
|
||
```mermaid
|
||
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 启动和进程维护。
|
||
|
||
## 镜像构造
|
||
|
||
```mermaid
|
||
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]
|
||
```
|
||
|
||
## 文件职责
|
||
|
||
```mermaid
|
||
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`
|
||
|
||
运行时配置文件。默认包含 `IMAGE`、`XRAY_URL`、`XRAY_IPV6_ENABLED`、`XRAY_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` 生成,并先写入临时文件,成功后再原子替换目标文件。当前不是通用转换器,只支持下面两类链接:
|
||
|
||
```text
|
||
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` 透明代理规则的 `setup` 和 `cleanup`。
|
||
|
||
`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 服务脚本。
|
||
|
||
## 启动生命周期
|
||
|
||
```mermaid
|
||
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
|
||
```
|
||
|
||
## 配置选择逻辑
|
||
|
||
```mermaid
|
||
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` 里配置:
|
||
|
||
```env
|
||
XRAY_URL=vless://...
|
||
```
|
||
|
||
高级方式是挂载完整 JSON:
|
||
|
||
```bat
|
||
-v D:\path\config.json:/etc/xray/config.json:ro
|
||
```
|
||
|
||
如果挂载了 `/etc/xray/config.json`,它会优先于 `XRAY_URL`。
|
||
|
||
## 透明代理数据路径
|
||
|
||
```mermaid
|
||
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 + 策略路由。
|
||
|
||
## 环境变量
|
||
|
||
```mermaid
|
||
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`
|
||
|
||
```text
|
||
1 启动 Xray 和透明代理,默认值
|
||
0 不启动 Xray,用于 proxy-off 对比测试
|
||
```
|
||
|
||
`XRAY_URL`
|
||
|
||
传入受支持的 `vless://...`,容器自动生成 Xray JSON 配置。
|
||
|
||
`XRAY_IPV6_ENABLED`
|
||
|
||
```text
|
||
0 默认值,启动时尽量关闭容器 IPv6
|
||
1 保留 IPv6
|
||
```
|
||
|
||
`XRAY_DEBUG_CONFIG`
|
||
|
||
```text
|
||
0 默认值,不打印生成的 Xray JSON
|
||
1 打印生成的 Xray JSON,仅用于调试
|
||
```
|
||
|
||
`XRAY_CONFIG`
|
||
|
||
默认:
|
||
|
||
```text
|
||
/etc/xray/config.json
|
||
```
|
||
|
||
`XRAY_TRANSPARENT`
|
||
|
||
```text
|
||
1 设置 iptables 透明代理,默认值
|
||
0 只启动 Xray,不设置透明转发
|
||
```
|
||
|
||
`XRAY_REDIRECT_PORT`
|
||
|
||
默认:
|
||
|
||
```text
|
||
12345
|
||
```
|
||
|
||
## 测试
|
||
|
||
运行:
|
||
|
||
```bat
|
||
Ubuntu\test.bat
|
||
```
|
||
|
||
测试脚本流程:
|
||
|
||
```mermaid
|
||
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[对比结果]
|
||
```
|
||
|
||
也可以手动运行:
|
||
|
||
```bat
|
||
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` 日志里应该出现:
|
||
|
||
```text
|
||
[transparent >> proxy]
|
||
```
|
||
|
||
这说明流量经过 Xray。
|
||
|
||
`proxy-off` 应该看到:
|
||
|
||
```text
|
||
Xray disabled by XRAY_ENABLED=0.
|
||
```
|
||
|
||
如果当前网络允许直连,`proxy-off` 会输出:
|
||
|
||
```text
|
||
[INFO] HTTP request without xray succeeded directly
|
||
```
|
||
|
||
如果当前网络不允许直连,`proxy-off` 会输出:
|
||
|
||
```text
|
||
[PASS] HTTP request without xray is blocked
|
||
```
|
||
|
||
测试脚本内部使用:
|
||
|
||
```sh
|
||
curl --noproxy '*' -I -L http://google.com
|
||
```
|
||
|
||
这是为了避免 `HTTP_PROXY`、`HTTPS_PROXY`、Docker Desktop 代理设置污染测试。
|
||
|
||
## 注意事项
|
||
|
||
运行透明代理必须加:
|
||
|
||
```text
|
||
--cap-add NET_ADMIN
|
||
```
|
||
|
||
否则容器内没有权限设置 `iptables`。
|
||
|
||
Windows 开启系统代理时,Docker Desktop/WSL 可能继承代理设置。可以用这些命令只读检查:
|
||
|
||
```powershell
|
||
docker info
|
||
netsh winhttp show proxy
|
||
Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' |
|
||
Select-Object ProxyEnable,ProxyServer,AutoConfigURL
|
||
```
|
||
|
||
如果 `docker info` 里出现类似:
|
||
|
||
```text
|
||
HTTP Proxy: http.docker.internal:3128
|
||
HTTPS Proxy: http.docker.internal:3128
|
||
```
|
||
|
||
说明 Docker Desktop 自己也配置了代理,测试直连/代理效果时要特别注意。
|