chore(deploy): trust proxy headers + document HTTPS env vars (#48)
* chore(deploy): trust proxy headers + document HTTPS env vars - uvicorn: add --proxy-headers --forwarded-allow-ips='*' so the app honors X-Forwarded-Proto/Host from a TLS-terminating reverse proxy (Caddy, Cloudflare Tunnel, nginx, LB). Without this the app saw every request as plain HTTP and built redirect/OAuth URLs from the raw Host, which is fragile behind a proxy. - .env.template: document DOMAIN (enables Secure cookie flag) and new SERVER_URL (deterministic base URL for OAuth callbacks and external links). Grouped under a dedicated HTTPS / REVERSE PROXY section. * chore(deploy): add proxy header flags to Dockerfile CMD and Kamal config Matches the docker-compose changes so non-compose deployments (docker run, Kubernetes, ECS, Kamal) also trust X-Forwarded-Proto/X-Forwarded-For. * fix(auth): align Google OAuth cookie Secure flag with password/email providers Google OAuth set the access_token cookie Secure flag based on the TESTING env var, while password and email providers use DOMAIN. This meant the DOMAIN env var (now documented in config/.env.template) did not actually control Secure for Google cookies. Align all three providers on DOMAIN so the documented behavior holds consistently.
This commit is contained in:
parent
1bbbe58ea0
commit
9e19fb5219
7 changed files with 29 additions and 9 deletions
|
|
@ -24,4 +24,4 @@ RUN uv build --wheel --out-dir /app/dist
|
|||
RUN uv pip install --system --no-cache .
|
||||
|
||||
EXPOSE 8000
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers", "--forwarded-allow-ips", "*"]
|
||||
|
|
|
|||
|
|
@ -111,13 +111,15 @@ async def google_callback(request: Request):
|
|||
request.session.pop("login_next", None), default="/dashboard"
|
||||
)
|
||||
|
||||
# Redirect to target with token in cookie
|
||||
is_production = os.environ.get("TESTING", "").lower() not in ("1", "true")
|
||||
# Redirect to target with token in cookie. Match password/email providers:
|
||||
# Secure only when DOMAIN is set (production with TLS), so the cookie is
|
||||
# actually sent over plain HTTP in dev.
|
||||
use_secure = os.environ.get("DOMAIN", "") != ""
|
||||
response = RedirectResponse(url=target, status_code=302)
|
||||
response.set_cookie(
|
||||
key="access_token", value=jwt_token,
|
||||
httponly=True, max_age=86400, samesite="lax",
|
||||
secure=is_production,
|
||||
secure=use_secure,
|
||||
)
|
||||
return response
|
||||
|
||||
|
|
|
|||
|
|
@ -44,4 +44,22 @@ SESSION_SECRET= # python -c "import secrets; print(secrets.token_he
|
|||
# DATA_DIR=/data # Default: /data in Docker, ./data locally
|
||||
# LOG_LEVEL=info # debug, info, warning, error
|
||||
# CORS_ORIGINS=http://localhost:3000,http://localhost:8000
|
||||
# DOMAIN=data.yourcompany.com # For Caddy TLS (production profile)
|
||||
|
||||
# ── HTTPS / REVERSE PROXY ───────────────────────────
|
||||
# Set these when the app runs behind a TLS terminator (Caddy, Cloudflare
|
||||
# Tunnel, nginx, GCP LB, etc.). The app itself speaks plain HTTP on :8000;
|
||||
# the terminator is responsible for TLS.
|
||||
#
|
||||
# DOMAIN: public hostname. When set, session cookies get the `Secure` flag
|
||||
# (browser only sends them over HTTPS). Also used by the Caddy
|
||||
# profile to auto-provision Let's Encrypt certs.
|
||||
# DOMAIN=data.yourcompany.com
|
||||
#
|
||||
# SERVER_URL: absolute base URL used to build OAuth callback URLs and other
|
||||
# external links. Set this to avoid relying on the incoming
|
||||
# request's Host header (which a misconfigured proxy can get
|
||||
# wrong). Must match the redirect URI registered in OAuth apps.
|
||||
# SERVER_URL=https://data.yourcompany.com
|
||||
#
|
||||
# Uvicorn is started with `--proxy-headers --forwarded-allow-ips='*'` so it
|
||||
# trusts X-Forwarded-Proto / X-Forwarded-For from the reverse proxy.
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ servers:
|
|||
web:
|
||||
hosts:
|
||||
- YOUR_SERVER_IP
|
||||
cmd: uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||
cmd: uvicorn app.main:app --host 0.0.0.0 --port 8000 --proxy-headers --forwarded-allow-ips='*'
|
||||
options:
|
||||
volume:
|
||||
- /data:/data
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# This file is auto-loaded by `docker compose up` when present
|
||||
services:
|
||||
app:
|
||||
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
|
||||
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload --proxy-headers --forwarded-allow-ips='*'
|
||||
volumes:
|
||||
- .:/app
|
||||
- data:/data
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
services:
|
||||
app:
|
||||
build: .
|
||||
command: uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --proxy-headers --forwarded-allow-ips='*'
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
services:
|
||||
app:
|
||||
build: .
|
||||
command: uvicorn app.main:app --host 0.0.0.0 --port 8000
|
||||
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --proxy-headers --forwarded-allow-ips='*'
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
|
|
|
|||
Loading…
Reference in a new issue