From d1bf5ea2dfb3200020c46679c9873e5290523ca4 Mon Sep 17 00:00:00 2001 From: xiaji Date: Sat, 13 Jun 2026 19:47:11 +0800 Subject: [PATCH] =?UTF-8?q?fix(auth):=20Caddy=20=E6=98=BE=E5=BC=8F?= =?UTF-8?q?=E5=86=99=E5=85=A5=20X-Forwarded-For=20+=20uvicorn=20=E4=BF=A1?= =?UTF-8?q?=E4=BB=BB=20docker=20=E7=BD=91=E7=BB=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因(在 smoke test 中定位): - Caddy reverse_proxy 默认会覆盖客户端的 X-Forwarded-For 头, 写入它自己识别的 client IP(防伪造) - uvicorn --forwarded-allow-ips 默认只信任 127.0.0.1, docker 网络 172.18.0.x 不在白名单 - 结果: api 端读 X-Forwarded-For 时,看到的是 Caddy 替换后的值 (Caddy 识别的真实 client IP),不是客户端伪造的值 — 这其实是正确的! 但 uvicorn 不会用这个值更新 client scope 修法: - Caddyfile: header_up X-Forwarded-For {remote_host} 显式让 Caddy 把自己识别的 client IP 写入 X-Forwarded-For - docker-compose api command: 加 --forwarded-allow-ips 172.18.0.0/16 信任 docker 网络(让 uvicorn 采用 X-Forwarded-For 的值) - api 端 get_client_ip 不变,读 X-Forwarded-For 拿真实 client IP 效果: X-Forwarded-For 在代理链中始终代表真实 client IP, 不再被任何中间件覆盖或丢弃 --- Caddyfile | 8 ++++++-- docker-compose.yml | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Caddyfile b/Caddyfile index deeedbf..0b56a6c 100644 --- a/Caddyfile +++ b/Caddyfile @@ -13,9 +13,13 @@ http://{$DOMAIN:NEWS_DOMAIN_FALLBACK} { # /api/* 直接转发,保留路径(后端 FastAPI 路由就是 /api/v1/*) - reverse_proxy /api/* api:8000 + reverse_proxy /api/* api:8000 { + # 把 Caddy 识别的 client IP 写入 X-Forwarded-For(覆盖任何客户端伪造) + # 后端 uvicorn --forwarded-allow-ips 信任 docker 网络后会用这个值 + header_up X-Forwarded-For {remote_host} + } - # 其余走前端 SPA + # 其余走前�?SPA reverse_proxy /* frontend:80 encode gzip zstd diff --git a/docker-compose.yml b/docker-compose.yml index fe49869..f680e24 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -61,7 +61,9 @@ services: - ./backend/app:/app/app - ./backend/alembic:/app/alembic - ./backend/alembic.ini:/app/alembic.ini - command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] + # --forwarded-allow-ips 信任 docker 网络(让 Caddy 写入的 X-Forwarded-For 被采用) + # 生产部署可改成 Caddy 容器的具体 IP(动态 IP 用子网) + command: ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--forwarded-allow-ips", "172.18.0.0/16"] logging: driver: json-file options: