Files
pad/cmd
xarmian 5fffee2b9e fix(docker): bind to 127.0.0.1 + require POSTGRES_PASSWORD (TASK-661) (#174)
* fix(docker): bind to 127.0.0.1 + require POSTGRES_PASSWORD (TASK-661)

A fresh Docker install previously published 7777 on 0.0.0.0 with a
hardcoded pad:pad Postgres credential. The bootstrap endpoint is
reachable until the first admin is created, so this combination lets
anyone who can route to the host claim the instance — and with M5's
X-Forwarded-For spoof (fixed in TASK-660) chained with the loopback
bootstrap check, it became a full takeover.

Changes:
- docker-compose.yml: publish "127.0.0.1:7777:7777" by default, with a
  PAD_BIND_ADDR override for operators who intentionally want LAN
  access. Require POSTGRES_PASSWORD via ${VAR:?err} so docker compose
  refuses to start when it's unset — can't silently inherit a weak
  default credential.
- docker-compose.prod.yml: drop the "change-me-in-production"
  placeholder; require the same env var as the base file.
- .env.example: new file documenting POSTGRES_PASSWORD (required),
  PAD_BIND_ADDR, REDIS_PASSWORD, PAD_CLOUD_SECRET, PAD_ENCRYPTION_KEY,
  PAD_TRUSTED_PROXIES with generation instructions.
- README.md: add a Docker Compose section covering the .env workflow
  and the loopback-default → LAN override.

Parent: PLAN-643 (OSS Security Hardening).

* fix(docker): use libpq keyword=value DSN to avoid URI-encoding the Postgres password per Codex P1

Passwords produced by 'openssl rand -base64' often include '/', '+', or ':'
which are reserved in URI userinfo. Injecting them into postgres://user:PASS@...
breaks sql.Open. Switch PAD_DATABASE_URL to the libpq keyword=value form
(host=... password=... dbname=...) where the password is parsed as a single
token regardless of special characters.

Also teach pgDbnameFromURL to parse both DSN shapes so 'pad db backup/restore'
still shows the correct database name in its confirmation prompt.
2026-04-21 19:14:15 -04:00
..