mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 21:41:09 +02:00
BREAKING: JWT keys are now auto-managed by Better Auth (EdDSA/Ed25519) - Remove all JWT_PRIVATE_KEY, JWT_PUBLIC_KEY, JWT_SECRET references - Keys stored in auth.jwks database table (auto-generated on first run) - Delete obsolete generate-keys.sh and generate-staging-secrets.sh scripts - Clean up legacy AUTH_*.md analysis files from root Security Improvements: - Add security_events table for audit logging - Add SecurityEventsService for tracking auth events - Enhanced security headers (HSTS, CSP, X-Frame-Options) - Rate limiting configuration Monitoring Setup: - Add auth-health-check.sh for automated testing - Add generate-dashboard.sh for HTML status dashboard - Tests: health endpoint, JWKS (EdDSA), security headers, response time - Ready for Hetzner cron deployment Documentation: - Update deployment docs with Better Auth notes - Update environment variable references - Add security improvements documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
190 lines
5.7 KiB
YAML
190 lines
5.7 KiB
YAML
version: '3.8'
|
|
|
|
services:
|
|
# Traefik reverse proxy
|
|
traefik:
|
|
image: traefik:v3.0
|
|
container_name: manacore-traefik
|
|
restart: unless-stopped
|
|
command:
|
|
- "--api.dashboard=true"
|
|
- "--providers.docker=true"
|
|
- "--providers.docker.exposedbydefault=false"
|
|
- "--entrypoints.web.address=:80"
|
|
- "--entrypoints.websecure.address=:443"
|
|
- "--certificatesresolvers.letsencrypt.acme.email=${ACME_EMAIL}"
|
|
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
|
|
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
|
|
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
|
|
# Rate limiting
|
|
- "--api.insecure=false"
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
- traefik-letsencrypt:/letsencrypt
|
|
networks:
|
|
- manacore-network
|
|
|
|
# PostgreSQL database
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
container_name: manacore-postgres
|
|
restart: unless-stopped
|
|
environment:
|
|
POSTGRES_DB: ${POSTGRES_DB:-manacore}
|
|
POSTGRES_USER: ${POSTGRES_USER:-manacore}
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
POSTGRES_INITDB_ARGS: "--encoding=UTF8 --auth=scram-sha-256"
|
|
volumes:
|
|
- postgres-data:/var/lib/postgresql/data
|
|
- ./services/mana-core-auth/postgres/init:/docker-entrypoint-initdb.d:ro
|
|
ports:
|
|
- "5432:5432"
|
|
networks:
|
|
- manacore-network
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-manacore}"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
command:
|
|
- "postgres"
|
|
- "-c"
|
|
- "max_connections=200"
|
|
- "-c"
|
|
- "shared_buffers=256MB"
|
|
- "-c"
|
|
- "effective_cache_size=1GB"
|
|
- "-c"
|
|
- "password_encryption=scram-sha-256"
|
|
|
|
# PgBouncer connection pooler
|
|
pgbouncer:
|
|
image: pgbouncer/pgbouncer:latest
|
|
container_name: manacore-pgbouncer
|
|
restart: unless-stopped
|
|
environment:
|
|
DATABASES_HOST: postgres
|
|
DATABASES_PORT: 5432
|
|
DATABASES_USER: ${POSTGRES_USER:-manacore}
|
|
DATABASES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
DATABASES_DBNAME: ${POSTGRES_DB:-manacore}
|
|
PGBOUNCER_POOL_MODE: transaction
|
|
PGBOUNCER_MAX_CLIENT_CONN: 1000
|
|
PGBOUNCER_DEFAULT_POOL_SIZE: 25
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
networks:
|
|
- manacore-network
|
|
|
|
# Redis cache
|
|
redis:
|
|
image: redis:7-alpine
|
|
container_name: manacore-redis
|
|
restart: unless-stopped
|
|
command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 256mb --maxmemory-policy allkeys-lru
|
|
volumes:
|
|
- redis-data:/data
|
|
ports:
|
|
- "6379:6379"
|
|
networks:
|
|
- manacore-network
|
|
healthcheck:
|
|
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
# Mana Core Auth Service
|
|
mana-core-auth:
|
|
build:
|
|
context: .
|
|
dockerfile: ./services/mana-core-auth/Dockerfile
|
|
container_name: manacore-auth
|
|
restart: unless-stopped
|
|
environment:
|
|
NODE_ENV: production
|
|
PORT: 3001
|
|
DATABASE_URL: postgresql://${POSTGRES_USER:-manacore}:${POSTGRES_PASSWORD}@pgbouncer:6432/${POSTGRES_DB:-manacore}
|
|
REDIS_HOST: redis
|
|
REDIS_PORT: 6379
|
|
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
|
JWT_PUBLIC_KEY: ${JWT_PUBLIC_KEY}
|
|
JWT_PRIVATE_KEY: ${JWT_PRIVATE_KEY}
|
|
JWT_ACCESS_TOKEN_EXPIRY: ${JWT_ACCESS_TOKEN_EXPIRY:-15m}
|
|
JWT_REFRESH_TOKEN_EXPIRY: ${JWT_REFRESH_TOKEN_EXPIRY:-7d}
|
|
JWT_ISSUER: ${JWT_ISSUER:-manacore}
|
|
JWT_AUDIENCE: ${JWT_AUDIENCE:-manacore}
|
|
STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY}
|
|
STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET}
|
|
STRIPE_PUBLISHABLE_KEY: ${STRIPE_PUBLISHABLE_KEY}
|
|
CORS_ORIGINS: ${CORS_ORIGINS}
|
|
CREDITS_SIGNUP_BONUS: ${CREDITS_SIGNUP_BONUS:-150}
|
|
CREDITS_DAILY_FREE: ${CREDITS_DAILY_FREE:-5}
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
networks:
|
|
- manacore-network
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.manacore-auth.rule=Host(`${AUTH_DOMAIN}`)"
|
|
- "traefik.http.routers.manacore-auth.entrypoints=websecure"
|
|
- "traefik.http.routers.manacore-auth.tls.certresolver=letsencrypt"
|
|
- "traefik.http.services.manacore-auth.loadbalancer.server.port=3001"
|
|
# Rate limiting
|
|
- "traefik.http.middlewares.auth-ratelimit.ratelimit.average=100"
|
|
- "traefik.http.middlewares.auth-ratelimit.ratelimit.burst=50"
|
|
- "traefik.http.routers.manacore-auth.middlewares=auth-ratelimit"
|
|
|
|
# Prometheus (metrics)
|
|
prometheus:
|
|
image: prom/prometheus:latest
|
|
container_name: manacore-prometheus
|
|
restart: unless-stopped
|
|
volumes:
|
|
- ./docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
|
- prometheus-data:/prometheus
|
|
command:
|
|
- '--config.file=/etc/prometheus/prometheus.yml'
|
|
- '--storage.tsdb.path=/prometheus'
|
|
- '--web.console.libraries=/usr/share/prometheus/console_libraries'
|
|
- '--web.console.templates=/usr/share/prometheus/consoles'
|
|
ports:
|
|
- "9090:9090"
|
|
networks:
|
|
- manacore-network
|
|
|
|
# Grafana (dashboards)
|
|
grafana:
|
|
image: grafana/grafana:latest
|
|
container_name: manacore-grafana
|
|
restart: unless-stopped
|
|
environment:
|
|
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD}
|
|
GF_USERS_ALLOW_SIGN_UP: false
|
|
volumes:
|
|
- grafana-data:/var/lib/grafana
|
|
- ./docker/grafana/provisioning:/etc/grafana/provisioning:ro
|
|
ports:
|
|
- "3000:3000"
|
|
depends_on:
|
|
- prometheus
|
|
networks:
|
|
- manacore-network
|
|
|
|
networks:
|
|
manacore-network:
|
|
driver: bridge
|
|
|
|
volumes:
|
|
postgres-data:
|
|
redis-data:
|
|
traefik-letsencrypt:
|
|
prometheus-data:
|
|
grafana-data:
|