feat: embed independently managed Xray with encrypted DNS
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
*
|
||||
!docker/
|
||||
!docker/Dockerfile
|
||||
@@ -1,5 +1,7 @@
|
||||
* text=auto
|
||||
*.yaml text eol=lf
|
||||
*.json text eol=lf
|
||||
*.conf text eol=lf
|
||||
*.sh text eol=lf
|
||||
*.md text eol=lf
|
||||
.env* text eol=lf
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
.runtime/
|
||||
backups/
|
||||
*.log
|
||||
configs/xray/10-node.json
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# CPR 部署配置
|
||||
|
||||
使用官方 `ghcr.io/zyycn/codex-proxy-rs:3.33.0` 镜像,包含单副本应用、PostgreSQL 18 和 Redis 8。需要 Docker Engine 和 Docker Compose 2.20.0 或更新版本
|
||||
基于官方 CPR `3.33.0` 和 Xray `26.7.28` 镜像构建运行环境,不修改或编译上游源码。单副本应用容器内运行 CPR、Xray 和 DNS 转发器,PostgreSQL 18、Redis 8 使用独立容器。需要 Linux Docker Engine 和 Docker Compose 2.20.0 或更新版本
|
||||
|
||||
## 文件
|
||||
|
||||
@@ -15,28 +15,49 @@
|
||||
| `compose/redis.yaml` | Redis、认证、AOF 和数据卷 |
|
||||
| `configs/config.yaml` | 独立的应用配置模板,包含密码占位符 |
|
||||
| `configs/caddy.caddy` | `cpr.pchuan.top` 的 Caddy 站点配置 |
|
||||
| `configs/xray/00-base.json` | 透明代理、DoH、日志及统计配置 |
|
||||
| `configs/xray/10-node.json` | 真实节点配置,Git 忽略 |
|
||||
| `configs/xray-node.example.json` | VLESS Reality 节点配置示例 |
|
||||
| `configs/dnsmasq.conf` | Docker 服务名与公网 DNS 分流 |
|
||||
| `configs/supervisord.conf` | CPR、Xray、DNS 的独立进程管理与日志轮转 |
|
||||
| `docker/Dockerfile` | 官方镜像的运行环境扩展 |
|
||||
| `scripts/start-app.sh` | 启动时填入密码并执行官方程序 |
|
||||
| `scripts/start-proxy.sh` | 配置容器透明代理规则并启动进程管理器 |
|
||||
| `.externals/codex-proxy-rs` | 官方源码子模块,固定为 `v3.33.0`,仅供核对,不参与构建 |
|
||||
|
||||
## 配置
|
||||
|
||||
新检出目录时,将 `.env.example` 复制为 `.env`。在 `.env` 分别填写 `ADMIN_PASSWORD`、`POSTGRES_PASSWORD`、`REDIS_PASSWORD`,每项使用独立的 48 位随机十六进制值。空密码会被 Compose 拒绝,不提供默认密码
|
||||
|
||||
`.env` 仅维护三项密码。镜像版本和端口写在对应 Compose 文件中,管理员用户名和业务时区写在 `configs/config.yaml`,项目名在根 `compose.yaml` 中固定为 `cpr`
|
||||
`.env` 仅维护三项密码。基础镜像版本写在 `docker/Dockerfile`,生成的镜像标签与端口写在 `compose/app.yaml`,管理员用户名和业务时区写在 `configs/config.yaml`,项目名在根 `compose.yaml` 中固定为 `cpr`
|
||||
|
||||
参考 `configs/xray-node.example.json` 创建 `configs/xray/10-node.json`,将 `proxy` 出站填写为真实节点。节点地址使用 IP,TLS/Reality 的 `serverName` 保留节点要求的域名,避免代理 DNS 启动时循环依赖。节点文件含认证信息,只保存在本机或服务器,不提交 Git
|
||||
|
||||
配置模板只读挂载到容器。启动脚本检查密码格式,替换三个占位符,在容器内生成仅当前用户可读写的 `/app/deploy/config.yaml`,然后启动官方程序。宿主模板始终不含真实密码,不需要手动生成配置文件
|
||||
|
||||
默认管理端用户名为 `admin@cpr.local`,本机地址为 `http://127.0.0.1:8080`。PostgreSQL 和 Redis 仅在项目默认网络内可达,不映射宿主端口
|
||||
|
||||
应用同时接入已有外部网络 `web-network`,别名为 `cpr-app`,部署前需确保该网络存在。将 `configs/caddy.caddy` 的站点配置接入同网络的 Caddy,可通过 `https://cpr.pchuan.top` 访问,客户端 Base URL 为 `https://cpr.pchuan.top/v1`。域名需解析到服务器,Caddy 自动申请和续期证书,SSE 即时刷新且支持 WebSocket Upgrade。账号出站代理在管理端另行配置
|
||||
应用同时接入已有外部网络 `web-network`,别名为 `cpr-app`,部署前需确保该网络存在。将 `configs/caddy.caddy` 的站点配置接入同网络的 Caddy,可通过 `https://cpr.pchuan.top` 访问,客户端 Base URL 为 `https://cpr.pchuan.top/v1`。域名需解析到服务器,Caddy 自动申请和续期证书,SSE 即时刷新且支持 WebSocket Upgrade
|
||||
|
||||
数据库密码和管理员密码是初始化参数:已有 PostgreSQL 数据卷时,修改 `.env` 不会自动修改库内用户密码;管理员创建后,修改初始密码也不会重设已有管理员。密码轮换需同步修改对应服务中的实际密码
|
||||
|
||||
## 持久化
|
||||
|
||||
应用数据、日志、PostgreSQL、Redis 分别保存到四个 Docker 命名卷,卷名前缀为 `cpr`。已有数据后保持项目名不变
|
||||
应用数据、应用日志、Xray/DNS 日志、PostgreSQL、Redis 分别保存到五个 Docker 命名卷,卷名前缀为 `cpr`。已有数据后保持项目名不变
|
||||
|
||||
应用以官方 UID/GID `10001:10001` 运行。空应用卷使用镜像目录初始化;恢复旧卷时需保留对应属主与权限
|
||||
入口和进程管理器以 root 配置容器网络,CPR 仍以官方 UID/GID `10001:10001` 运行。空应用卷使用镜像目录初始化;恢复旧卷时需保留对应属主与权限。仅增加容器内所需能力,不使用 privileged 或 host 网络
|
||||
|
||||
## 代理与 DNS
|
||||
|
||||
CPR 的公网 IPv4 TCP/UDP 经容器内 TPROXY 进入 Xray。Docker 内网、环回和入站连接的返回流量绕过代理;Xray 自身使用专用 GID 防止循环。IPv6 出站阻断,避免绕过。规则仅作用于应用容器,不影响宿主机、CPA、数据库或 Redis 容器
|
||||
|
||||
管理页面中的“直连”表示未配置账号级代理,公网流量仍会经过容器的 Xray。另行给账号绑定 HTTP/SOCKS 代理会形成代理链,应确认这是预期行为
|
||||
|
||||
应用使用 `127.0.0.1` 的 DNS 转发器:`postgres`、`redis` 交给 Docker 的 `127.0.0.11` 动态解析,公网域名交给 Xray。Xray 通过代理并行查询 Cloudflare `1.1.1.1` 和 Quad9 `9.9.9.9` 的 DoH,并缓存结果。DNS 故障不会回退到公网明文 DNS
|
||||
|
||||
Xray 可以独立重启或自动从崩溃恢复,期间 CPR 进程不重启、透明代理规则保留,公网连接暂时不可用,已有上游连接会中断,需要客户端重试。独立重启不保证正在生成的请求无中断
|
||||
|
||||
Xray 的访问、错误和 DNS 转发器日志保存在 `/app/.runtime/xray/`,每类单文件上限 10 MiB,最多保留 3 个轮转文件。错误日志中的 DNS 记录可查看 DoH 解析结果与耗时;访问日志可查看目标地址及所选出站。日志包含访问域名和 IP,分享前需脱敏
|
||||
|
||||
## 运维命令
|
||||
|
||||
@@ -45,23 +66,32 @@
|
||||
| 命令 | 用途 |
|
||||
| --- | --- |
|
||||
| `just check` | 检查配置,不输出展开后的密码 |
|
||||
| `just pull` | 拉取固定版本镜像 |
|
||||
| `just pull` | 拉取 PostgreSQL 和 Redis 镜像 |
|
||||
| `just build` | 构建包含 Xray 的应用镜像 |
|
||||
| `just up` | 创建或更新全部服务,等待健康 |
|
||||
| `just status` | 查看服务状态 |
|
||||
| `just logs` | 持续查看应用日志 |
|
||||
| `just logs postgres` | 持续查看 PostgreSQL 日志,Redis 同理 |
|
||||
| `just health` | 在应用容器内检查健康接口 |
|
||||
| `just health` | 检查 Xray、DNS 进程及应用健康接口 |
|
||||
| `just restart` | 重启应用,不应用配置变更 |
|
||||
| `just recreate` | 重新创建应用,应用配置变更 |
|
||||
| `just update` | 拉取并重新创建应用,不更新数据库和 Redis |
|
||||
| `just update` | 更新基础镜像、构建并重新创建应用 |
|
||||
| `just xray-check` | 校验 Xray 和节点配置 |
|
||||
| `just xray-restart` | 校验成功后仅重启 Xray |
|
||||
| `just xray-status` | 查看 CPR、Xray 和 DNS 进程状态 |
|
||||
| `just xray-logs` | 持续查看 Xray 访问日志 |
|
||||
| `just xray-logs error` | 持续查看连接错误和 DoH 日志 |
|
||||
| `just xray-logs dns` | 持续查看应用的 DNS 查询与缓存记录 |
|
||||
| `just xray-stats` | 查看出站收发字节,Xray 重启后清零 |
|
||||
| `just dns api.openai.com` | 检查解析结果及耗时 |
|
||||
| `just stop` | 停止全部服务,保留容器 |
|
||||
| `just down` | 移除容器和网络,保留数据卷 |
|
||||
|
||||
`restart`、`recreate` 接受服务名,默认 `app`,也可指定 `postgres` 或 `redis`。首次启动使用 `just pull`、`just up`;应用配置修改后使用 `just recreate`
|
||||
`restart`、`recreate` 接受服务名,默认 `app`,也可指定 `postgres` 或 `redis`。首次启动使用 `just pull`、`just build`、`just up`。应用配置修改后使用 `just recreate`;仅修改 Xray 配置或节点时使用 `just xray-restart`
|
||||
|
||||
健康接口 `/healthz` 返回 204 表示网关、数据库、Redis 和后台任务通过检查,不代表上游账号可用。创建或导入账号后,在管理端创建客户端 Key,按“使用密钥”页面配置客户端
|
||||
健康接口 `/healthz` 返回 204 表示网关、数据库、Redis 和后台任务通过检查;容器健康检查还确认 Xray 和 DNS 进程运行,但不证明代理节点可达。通过 DNS 查询、出口 IP 和真实账号请求分别验证网络与模型能力
|
||||
|
||||
升级修改 `compose/app.yaml` 中的镜像版本,并核对对应 Release 的配置及迁移说明。应用内置 PostgreSQL 备份客户端为 18,不随意切换数据库大版本。升级前备份数据库、应用数据与 `.env`;停止服务保留命名卷,不使用 `down -v`
|
||||
升级修改 `docker/Dockerfile` 中的基础镜像版本和 `compose/app.yaml` 中的生成镜像标签,核对对应 Release 的配置及迁移说明,再执行 `just update`。应用内置 PostgreSQL 备份客户端为 18,不随意切换数据库大版本。升级前备份数据库、应用数据、节点文件与 `.env`;停止服务保留命名卷,不使用 `down -v`
|
||||
|
||||
## 官方参考
|
||||
|
||||
|
||||
+22
-5
@@ -1,10 +1,14 @@
|
||||
services:
|
||||
app:
|
||||
image: ghcr.io/zyycn/codex-proxy-rs:3.33.0
|
||||
image: cpr:3.33.0-xray26.7.28
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile
|
||||
restart: unless-stopped
|
||||
user: '10001:10001'
|
||||
# 入口配置网络,Supervisor 将 CPR 降权到官方 cpr 用户。
|
||||
user: '0:0'
|
||||
init: true
|
||||
entrypoint: [/bin/sh, /app/scripts/start-app.sh]
|
||||
entrypoint: [/bin/bash, /app/scripts/start-proxy.sh]
|
||||
stop_grace_period: 75s
|
||||
depends_on:
|
||||
postgres:
|
||||
@@ -23,8 +27,12 @@ services:
|
||||
volumes:
|
||||
- app-data:/app/.runtime/data
|
||||
- app-logs:/app/.runtime/logs
|
||||
- xray-logs:/app/.runtime/xray
|
||||
- ../configs/config.yaml:/app/deploy/config.template.yaml:ro
|
||||
- ../scripts/start-app.sh:/app/scripts/start-app.sh:ro
|
||||
- ../configs/xray:/etc/xray:ro
|
||||
- ../configs/dnsmasq.conf:/etc/cpr/dnsmasq.conf:ro
|
||||
- ../configs/supervisord.conf:/etc/supervisor/supervisord.conf:ro
|
||||
- ../scripts:/app/scripts:ro
|
||||
environment:
|
||||
HOME: /app
|
||||
CPR_DEPLOYMENT_MODE: docker
|
||||
@@ -37,10 +45,18 @@ services:
|
||||
- no-new-privileges:true
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_BIND_SERVICE
|
||||
- SETUID
|
||||
- SETGID
|
||||
- KILL
|
||||
- DAC_OVERRIDE
|
||||
tmpfs:
|
||||
- /tmp:size=64m,mode=1777
|
||||
- /run/cpr:size=8m,mode=0700
|
||||
healthcheck:
|
||||
test: [CMD, curl, -fsS, http://127.0.0.1:8080/healthz]
|
||||
test: [CMD, /bin/sh, /app/scripts/health.sh]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
@@ -54,6 +70,7 @@ services:
|
||||
volumes:
|
||||
app-data:
|
||||
app-logs:
|
||||
xray-logs:
|
||||
|
||||
networks:
|
||||
web-network:
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# 容器服务名仍由 Docker 动态解析;公网域名交给 Xray 的 DoH。
|
||||
listen-address=127.0.0.1
|
||||
bind-interfaces
|
||||
port=53
|
||||
no-resolv
|
||||
no-hosts
|
||||
server=/postgres/127.0.0.11
|
||||
server=/redis/127.0.0.11
|
||||
server=127.0.0.1#5353
|
||||
cache-size=1000
|
||||
max-cache-ttl=60
|
||||
neg-ttl=5
|
||||
user=root
|
||||
group=root
|
||||
pid-file=/run/cpr/dnsmasq.pid
|
||||
log-queries=extra
|
||||
log-facility=-
|
||||
@@ -0,0 +1,63 @@
|
||||
[unix_http_server]
|
||||
file=/run/cpr/supervisor.sock
|
||||
chmod=0600
|
||||
|
||||
[supervisord]
|
||||
nodaemon=true
|
||||
user=root
|
||||
pidfile=/run/cpr/supervisord.pid
|
||||
logfile=/dev/stdout
|
||||
logfile_maxbytes=0
|
||||
|
||||
[rpcinterface:supervisor]
|
||||
supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface
|
||||
|
||||
[supervisorctl]
|
||||
serverurl=unix:///run/cpr/supervisor.sock
|
||||
|
||||
[program:xray]
|
||||
command=/usr/bin/setpriv --regid 23333 --clear-groups /usr/local/bin/xray run -confdir /etc/xray
|
||||
priority=10
|
||||
autostart=true
|
||||
autorestart=true
|
||||
startsecs=1
|
||||
startretries=5
|
||||
stopasgroup=true
|
||||
killasgroup=true
|
||||
stopwaitsecs=15
|
||||
stdout_logfile=/app/.runtime/xray/access.log
|
||||
stdout_logfile_maxbytes=10MB
|
||||
stdout_logfile_backups=3
|
||||
stderr_logfile=/app/.runtime/xray/error.log
|
||||
stderr_logfile_maxbytes=10MB
|
||||
stderr_logfile_backups=3
|
||||
|
||||
[program:dns]
|
||||
command=/usr/sbin/dnsmasq --keep-in-foreground --conf-file=/etc/cpr/dnsmasq.conf
|
||||
priority=20
|
||||
autostart=true
|
||||
autorestart=true
|
||||
startsecs=1
|
||||
startretries=5
|
||||
stopasgroup=true
|
||||
killasgroup=true
|
||||
redirect_stderr=true
|
||||
stdout_logfile=/app/.runtime/xray/dns.log
|
||||
stdout_logfile_maxbytes=10MB
|
||||
stdout_logfile_backups=3
|
||||
|
||||
[program:app]
|
||||
command=/bin/sh /app/scripts/start-app.sh
|
||||
user=cpr
|
||||
priority=30
|
||||
autostart=true
|
||||
autorestart=true
|
||||
startsecs=3
|
||||
startretries=5
|
||||
stopasgroup=true
|
||||
killasgroup=true
|
||||
stopwaitsecs=45
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"outbounds": [
|
||||
{
|
||||
"tag": "proxy",
|
||||
"protocol": "vless",
|
||||
"settings": {
|
||||
"vnext": [
|
||||
{
|
||||
"address": "192.0.2.1",
|
||||
"port": 443,
|
||||
"users": [
|
||||
{"id": "00000000-0000-0000-0000-000000000000", "encryption": "none", "flow": "xtls-rprx-vision"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"streamSettings": {
|
||||
"network": "raw",
|
||||
"security": "reality",
|
||||
"realitySettings": {
|
||||
"serverName": "replace-with-node-sni.example",
|
||||
"fingerprint": "chrome",
|
||||
"password": "replace-with-node-public-key",
|
||||
"shortId": ""
|
||||
},
|
||||
"sockopt": {"mark": 2}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"log": {
|
||||
"access": "/dev/stdout",
|
||||
"error": "/dev/stderr",
|
||||
"loglevel": "info",
|
||||
"dnsLog": true
|
||||
},
|
||||
"api": {
|
||||
"tag": "api",
|
||||
"listen": "127.0.0.1:10085",
|
||||
"services": ["StatsService"]
|
||||
},
|
||||
"stats": {},
|
||||
"policy": {
|
||||
"system": {
|
||||
"statsOutboundUplink": true,
|
||||
"statsOutboundDownlink": true
|
||||
}
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"tag": "transparent-in",
|
||||
"listen": "0.0.0.0",
|
||||
"port": 12345,
|
||||
"protocol": "tunnel",
|
||||
"settings": {"allowedNetwork": "tcp,udp", "followRedirect": true},
|
||||
"streamSettings": {"sockopt": {"tproxy": "tproxy"}},
|
||||
"sniffing": {
|
||||
"enabled": true,
|
||||
"destOverride": ["http", "tls", "quic"],
|
||||
"routeOnly": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "dns-in",
|
||||
"listen": "127.0.0.1",
|
||||
"port": 5353,
|
||||
"protocol": "tunnel",
|
||||
"settings": {"address": "1.1.1.1", "port": 53, "allowedNetwork": "tcp,udp"}
|
||||
}
|
||||
],
|
||||
"outbounds": [
|
||||
{"tag": "blocked", "protocol": "blackhole"},
|
||||
{"tag": "dns-out", "protocol": "dns"}
|
||||
],
|
||||
"routing": {
|
||||
"domainStrategy": "AsIs",
|
||||
"rules": [
|
||||
{"type": "field", "inboundTag": ["dns-in"], "outboundTag": "dns-out"},
|
||||
{
|
||||
"type": "field",
|
||||
"inboundTag": ["dns-upstream", "transparent-in"],
|
||||
"outboundTag": "proxy"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dns": {
|
||||
"servers": [
|
||||
{"address": "https://1.1.1.1/dns-query", "timeoutMs": 3000},
|
||||
{"address": "https://9.9.9.9/dns-query", "timeoutMs": 3000}
|
||||
],
|
||||
"queryStrategy": "UseIPv4",
|
||||
"disableCache": false,
|
||||
"enableParallelQuery": true,
|
||||
"tag": "dns-upstream"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
FROM ghcr.io/xtls/xray-core:26.7.28 AS xray
|
||||
|
||||
FROM ghcr.io/zyycn/codex-proxy-rs:3.33.0
|
||||
|
||||
USER root
|
||||
|
||||
# 保留官方 CPR 二进制与页面,仅补充透明代理、DNS 和进程管理工具。
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
bash dnsmasq-base dnsutils iproute2 iptables supervisor util-linux \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& install -d /app/scripts /app/.runtime/xray /run/cpr
|
||||
|
||||
COPY --from=xray /usr/local/bin/xray /usr/local/bin/xray
|
||||
|
||||
WORKDIR /app
|
||||
@@ -9,9 +9,13 @@ default:
|
||||
check:
|
||||
docker compose config --quiet
|
||||
|
||||
# 拉取配置中固定版本的镜像。
|
||||
# 拉取数据库和 Redis 的固定版本镜像。
|
||||
pull: check
|
||||
docker compose pull
|
||||
docker compose pull postgres redis
|
||||
|
||||
# 基于官方 CPR 与 Xray 镜像构建运行环境,不编译上游源码。
|
||||
build: check
|
||||
docker compose build app
|
||||
|
||||
# 创建或更新全部服务,等待健康检查通过。
|
||||
up: check
|
||||
@@ -33,9 +37,9 @@ restart service="app":
|
||||
recreate service="app": check
|
||||
docker compose up -d --no-build --no-deps --force-recreate --wait {{service}}
|
||||
|
||||
# 拉取并更新应用镜像;先在 compose/app.yaml 中修改版本。
|
||||
# 更新基础镜像并重建应用;版本固定在 Dockerfile 和 Compose 中。
|
||||
update: check
|
||||
docker compose pull app
|
||||
docker compose build --pull app
|
||||
docker compose up -d --no-build --no-deps --force-recreate --wait app
|
||||
|
||||
# 查看所有服务的状态,包括已停止的容器。
|
||||
@@ -48,4 +52,28 @@ logs service="app":
|
||||
|
||||
# 在应用容器内检查健康接口,成功返回 HTTP 204。
|
||||
health:
|
||||
docker compose exec -T app curl --fail --silent --show-error --include http://127.0.0.1:8080/healthz
|
||||
docker compose exec -T app /bin/sh /app/scripts/health.sh
|
||||
|
||||
# 校验 Xray 基础配置和节点文件,不重启进程。
|
||||
xray-check:
|
||||
docker compose exec -T app xray run -test -confdir /etc/xray
|
||||
|
||||
# 配置有效才单独重启 Xray,保留 CPR 进程与透明代理规则。
|
||||
xray-restart: xray-check
|
||||
docker compose exec -T app supervisorctl -c /etc/supervisor/supervisord.conf restart xray
|
||||
|
||||
# 查看 CPR、Xray 和 DNS 的独立进程状态。
|
||||
xray-status:
|
||||
docker compose exec -T app supervisorctl -c /etc/supervisor/supervisord.conf status
|
||||
|
||||
# 查看访问日志;error 可查看连接错误和 DoH 查询耗时。
|
||||
xray-logs kind="access":
|
||||
docker compose exec -T app tail -n 100 -F /app/.runtime/xray/{{kind}}.log
|
||||
|
||||
# 查询各出站的收发字节,Xray 重启后计数清零。
|
||||
xray-stats:
|
||||
docker compose exec -T app xray api statsquery --server=127.0.0.1:10085
|
||||
|
||||
# 查看当前解析结果和耗时,默认检查 OpenAI 域名。
|
||||
dns domain="api.openai.com":
|
||||
docker compose exec -T app dig "@127.0.0.1" {{domain}} A +tries=1 +time=5
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
[ "$(supervisorctl -c /etc/supervisor/supervisord.conf status xray dns | grep -c RUNNING)" -eq 2 ]
|
||||
exec curl --fail --silent --show-error http://127.0.0.1:8080/healthz
|
||||
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# 只管理当前容器的网络命名空间,不修改宿主或 CPA 的规则。
|
||||
[[ "$(id -u)" == 0 ]] || { echo 'Proxy setup requires root.' >&2; exit 1; }
|
||||
[[ -s /etc/xray/10-node.json ]] || { echo 'Missing configs/xray/10-node.json.' >&2; exit 1; }
|
||||
xray run -test -confdir /etc/xray >/dev/null
|
||||
dnsmasq --test --conf-file=/etc/cpr/dnsmasq.conf
|
||||
mkdir -p /run/cpr /app/.runtime/xray
|
||||
chmod 0700 /run/cpr
|
||||
|
||||
# 容器重启时可重复执行;重启 Xray 本身不会经过这里或删除规则。
|
||||
for chain in CPR_XRAY_OUT CPR_XRAY_IN CPR_LOCAL_IN; do
|
||||
iptables -t mangle -N "$chain" 2>/dev/null || iptables -t mangle -F "$chain"
|
||||
done
|
||||
while iptables -t mangle -D OUTPUT -j CPR_XRAY_OUT 2>/dev/null; do :; done
|
||||
while iptables -t mangle -D PREROUTING -j CPR_LOCAL_IN 2>/dev/null; do :; done
|
||||
while iptables -t mangle -D PREROUTING -m mark --mark 1/1 -j CPR_XRAY_IN 2>/dev/null; do :; done
|
||||
while ip rule del priority 10010 2>/dev/null; do :; done
|
||||
ip route replace local 0.0.0.0/0 dev lo table 100
|
||||
ip rule add fwmark 1/1 table 100 priority 10010
|
||||
|
||||
# 保留入站连接的返回路径,避免对 Caddy 的响应也进入代理。
|
||||
iptables -t mangle -A CPR_LOCAL_IN -i lo -j RETURN
|
||||
iptables -t mangle -A CPR_LOCAL_IN -m conntrack --ctstate NEW -m addrtype --dst-type LOCAL -j CONNMARK --set-xmark 4/4
|
||||
iptables -t mangle -A PREROUTING -j CPR_LOCAL_IN
|
||||
iptables -t mangle -A CPR_XRAY_IN -p tcp -j TPROXY --on-port 12345 --tproxy-mark 1/1
|
||||
iptables -t mangle -A CPR_XRAY_IN -p udp -j TPROXY --on-port 12345 --tproxy-mark 1/1
|
||||
iptables -t mangle -A PREROUTING -m mark --mark 1/1 -j CPR_XRAY_IN
|
||||
|
||||
# Xray 使用专用 GID,自己的出口和 Docker 内网不会被再次截获。
|
||||
iptables -t mangle -A CPR_XRAY_OUT -m owner --gid-owner 23333 -j RETURN
|
||||
iptables -t mangle -A CPR_XRAY_OUT -m mark --mark 2/2 -j RETURN
|
||||
iptables -t mangle -A CPR_XRAY_OUT -m connmark --mark 4/4 -j RETURN
|
||||
iptables -t mangle -A CPR_XRAY_OUT -m addrtype --dst-type LOCAL -j RETURN
|
||||
for subnet in 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.168.0.0/16 224.0.0.0/3; do
|
||||
iptables -t mangle -A CPR_XRAY_OUT -d "$subnet" -j RETURN
|
||||
done
|
||||
iptables -t mangle -A CPR_XRAY_OUT -p tcp -j MARK --set-xmark 1/1
|
||||
iptables -t mangle -A CPR_XRAY_OUT -p udp -j MARK --set-xmark 1/1
|
||||
iptables -t mangle -A OUTPUT -j CPR_XRAY_OUT
|
||||
|
||||
# IPv6 暂不代理,禁止绕过;本机回环保留。
|
||||
ip6tables -N CPR_IPV6_OUT 2>/dev/null || ip6tables -F CPR_IPV6_OUT
|
||||
while ip6tables -D OUTPUT -j CPR_IPV6_OUT 2>/dev/null; do :; done
|
||||
ip6tables -A CPR_IPV6_OUT -o lo -j RETURN
|
||||
ip6tables -A CPR_IPV6_OUT -j REJECT
|
||||
ip6tables -A OUTPUT -j CPR_IPV6_OUT
|
||||
|
||||
printf 'nameserver 127.0.0.1\noptions ndots:0 timeout:2 attempts:2\n' > /etc/resolv.conf
|
||||
exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
|
||||
Reference in New Issue
Block a user