Replaces the implicit Let's Encrypt flow with a general corporate-CA HTTPS path: - Caddy switches to cert-file mode (`tls /certs/fullchain.pem /certs/privkey.pem`) with HSTS + TLS 1.2/1.3 floor - New `docker-compose.tls.yml` overlay closes host `:8000` when Caddy fronts (no TLS bypass) - New `scripts/tls-fetch.sh` — generic URL fetcher for `sm://`, `gs://`, `https://`, `file://` with redirect refusal + PEM validation - New `scripts/grpn/agnes-tls-rotate.sh` — daily rotation, self-signed fallback against same key (zero key churn), on-VM RSA-2048 + CSR auto-gen, atomic swap, SIGUSR1 reload - `scripts/grpn/agnes-auto-upgrade.sh` becomes cert-aware (auto-enables tls overlay when certs present) - Compose profile `production` renamed to `tls` (aligns with DEPLOYMENT.md and infra startup) Pairs with FoundryAI/agnes-the-ai-analyst-infra#27 (merged) which wires per-VM `local.vm_tls`, writes `TLS_*` env vars into `.env`, auto-creates Secret Manager containers for `sm://` privkey URLs, and installs `agnes-tls-rotate.{service,timer}` for daily polling. Includes hardening + docs follow-ups from code review: - `TLS_CSR_SUBJECT` env-var parametrisation applied to both CSR and self-signed cert paths - curl `--max-redirs 0 --proto '=https'` + post-fetch PEM validation in `tls-fetch.sh` - `ulimit -c 0` + array-form `COMPOSE_FILES` (POSIX-safe, bash 3.2 compatible) - TLS section added to `config/.env.template` - Historical-note headers in `docs/superpowers/{plans,specs}/2026-04-09-*.md` flagging the profile rename
26 lines
1.1 KiB
Caddyfile
26 lines
1.1 KiB
Caddyfile
{$DOMAIN:localhost} {
|
|
# Cert-file mode (corporate CA path). For Let's Encrypt, drop the
|
|
# `tls` directive entirely so Caddy auto-issues. See docs/DEPLOYMENT.md.
|
|
tls /certs/fullchain.pem /certs/privkey.pem {
|
|
# Modern TLS only. Caddy default already excludes 1.0/1.1 in
|
|
# most builds, but pin explicitly so a future Caddy default
|
|
# change can't silently weaken our posture.
|
|
protocols tls1.2 tls1.3
|
|
}
|
|
|
|
# HSTS: tell compliant browsers to refuse plain-HTTP for this host
|
|
# for a year. Skipping `preload` so we keep an escape hatch (preload
|
|
# submission is hard-bound and blocks rollback). Skipping
|
|
# `includeSubDomains` because we don't control subdomains.
|
|
header Strict-Transport-Security "max-age=31536000"
|
|
|
|
reverse_proxy app:8000 {
|
|
# App's uvicorn runs with --proxy-headers, so stamping these
|
|
# ourselves makes OAuth callback URLs and Set-Cookie Secure
|
|
# flags resolve to https consistently. X-Forwarded-Host is
|
|
# also Caddy's default, but pinning it explicitly insures
|
|
# against future default changes.
|
|
header_up X-Forwarded-Proto https
|
|
header_up X-Forwarded-Host {host}
|
|
}
|
|
}
|