Files
anwar-pi-website/web/docker-compose.vps.yml
AHMED 8e577165cf Security fixes from pentest 2026-10-09 (F1, F2, F4, F7)
- F2: per-IP rate limits on POST /api/users, /login, /forgot-password
  via new Next middleware (5/h signup, 10/15min login, 5/h forgot);
  429 + Retry-After, generic Arabic error, no UX change
- F7: ghost-email logins burn the same PBKDF2 cost (600k/32/sha256)
  as real verifies, closing response-time user enumeration
- F4: HSTS max-age=63072000; includeSubDomains on all routes
- F1: Traefik pagelimit (60/min, burst 120) + connlimit (in-flight 50)
  on the page router; apilimit untouched
- Pentester accounts (ids 42-48) deleted in prod DB
2026-10-09 17:00:15 +03:00

76 lines
2.6 KiB
YAML

services:
db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_USER: anwarpi
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: anwarpi
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U anwarpi -d anwarpi"]
interval: 5s
timeout: 5s
retries: 10
web:
build:
context: .
args:
NEXT_PUBLIC_SITE_URL: https://anwarpy.ai
restart: unless-stopped
expose:
- "3000"
volumes:
- media-uploads:/app/media
- files-uploads:/app/files-private
environment:
DATABASE_URI: postgres://anwarpi:${DB_PASSWORD}@db:5432/anwarpi
PAYLOAD_SECRET: ${PAYLOAD_SECRET}
AGENT_API_KEY: ${AGENT_API_KEY}
NEXT_PUBLIC_SITE_URL: https://anwarpy.ai
CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN}
CLOUDFLARE_ACCOUNT_ID: ${CLOUDFLARE_ACCOUNT_ID}
MAIL_FROM: noreply@anwarpy.ai
labels:
- traefik.enable=true
- traefik.http.routers.anwarpi.rule=Host(`anwarpy.ai`) || Host(`www.anwarpy.ai`)
- traefik.http.routers.anwarpi.entrypoints=websecure
- traefik.http.routers.anwarpi.tls.certresolver=mytlschallenge
- traefik.http.routers.anwarpi.middlewares=pagelimit@docker,connlimit@docker
- traefik.http.services.anwarpi.loadbalancer.server.port=3000
- traefik.http.routers.anwarpi-api.rule=PathPrefix(`/api`) && (Host(`anwarpy.ai`) || Host(`www.anwarpy.ai`))
- traefik.http.routers.anwarpi-api.entrypoints=websecure
- traefik.http.routers.anwarpi-api.tls=true
- traefik.http.routers.anwarpi-api.tls.certresolver=mytlschallenge
- traefik.http.routers.anwarpi-api.service=anwarpi
- traefik.http.routers.anwarpi-api.middlewares=apilimit@docker
- traefik.http.routers.anwarpi-api.priority=100
- traefik.http.middlewares.apilimit.ratelimit.average=90
- traefik.http.middlewares.apilimit.ratelimit.burst=180
- traefik.http.middlewares.apilimit.ratelimit.period=1m
# F1: the pentest flood froze pages while /api held. Cap per-IP page
# rate and in-flight requests; apilimit already guards /api itself.
- traefik.http.middlewares.pagelimit.ratelimit.average=60
- traefik.http.middlewares.pagelimit.ratelimit.period=1m
- traefik.http.middlewares.pagelimit.ratelimit.burst=120
- traefik.http.middlewares.connlimit.inflightreq.amount=50
- traefik.docker.network=proxy
depends_on:
db:
condition: service_healthy
networks:
- default
- proxy
volumes:
db-data:
media-uploads:
files-uploads:
networks:
default:
proxy:
external: true