Yesterday's prod incident exposed postgres on 0.0.0.0:5432 with the
default 'postgres:postgres' credentials. A scanner ransomware bot
brute-forced it and dropped the database (left a readme_to_recover
note). We restored from a pre-incident dump and the user data is back,
but the underlying weakness was in this docker-compose.yml.
Changes:
- Remove `ports: "5432:5432"` from the postgres service entirely.
Postgres is reachable only via the internal docker network. For
ad-hoc admin access, use an SSH tunnel:
`ssh -L 5432:localhost:5432 deploy@<host>`
- POSTGRES_PASSWORD now reads from `${POSTGRES_PASSWORD:-postgres}`
via env interpolation. Prod `.env` (not in repo) provides the real
value; local dev gets the `postgres` fallback so `docker compose up`
still works without setup.
- Remove the no-longer-needed DATABASE_URL override in the app service
`environment:` — `env_file: .env` already supplies it.
After this lands, the deploy pipeline will rsync the new compose.yml,
recreate containers with no exposed pg port, and substitute the strong
password from .env at container start. Volumes persist, data intact.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
43 lines
1022 B
YAML
43 lines
1022 B
YAML
services:
|
|
app:
|
|
build: .
|
|
ports:
|
|
- "4321:4321"
|
|
env_file:
|
|
- .env
|
|
environment:
|
|
- NODE_ENV=production
|
|
- HOST=0.0.0.0
|
|
- PORT=4321
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD", "wget", "-qO-", "http://localhost:4321/api/health"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 10s
|
|
restart: unless-stopped
|
|
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
# No public port mapping: Postgres is reachable only via the internal
|
|
# docker network. Use an SSH tunnel for ad-hoc admin access.
|
|
environment:
|
|
- POSTGRES_USER=postgres
|
|
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-postgres}
|
|
- POSTGRES_DB=randify
|
|
volumes:
|
|
- postgres_data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U postgres -d randify"]
|
|
interval: 5s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 5s
|
|
restart: unless-stopped
|
|
|
|
volumes:
|
|
postgres_data:
|