mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-01-08 12:40:38 -06:00
Add WTF_CSRF_TRUSTED_ORIGINS to app/config.py (env-driven, comma-separated; default https://track.example.com) to allow CSRF validation when referrer/origin host matches a trusted origin behind a proxy. Keep existing ProxyFix(x_proto=1, x_host=1, x_for=1, x_port=1) so Flask honors X-Forwarded-* headers. Ensure forms/AJAX post to the same origin you’ve configured via WTF_CSRF_TRUSTED_ORIGINS.
112 lines
4.0 KiB
YAML
112 lines
4.0 KiB
YAML
services:
|
|
# Certificate generator - runs once to create self-signed certs with SANs
|
|
certgen:
|
|
image: alpine:latest
|
|
container_name: timetracker-certgen
|
|
volumes:
|
|
- ./nginx/ssl:/certs
|
|
- ./scripts:/scripts:ro
|
|
command: sh /scripts/generate-certs.sh
|
|
restart: "no"
|
|
|
|
# HTTPS reverse proxy (TLS terminates here)
|
|
nginx:
|
|
image: nginx:alpine
|
|
container_name: timetracker-nginx
|
|
ports:
|
|
- "80:80"
|
|
- "443:443"
|
|
volumes:
|
|
- ./nginx/conf.d:/etc/nginx/conf.d:ro
|
|
- ./nginx/ssl:/etc/nginx/ssl:ro
|
|
depends_on:
|
|
certgen:
|
|
condition: service_completed_successfully
|
|
app:
|
|
condition: service_started
|
|
restart: unless-stopped
|
|
|
|
app:
|
|
build: .
|
|
container_name: timetracker-app
|
|
environment:
|
|
- TZ=${TZ:-Europe/Brussels}
|
|
- CURRENCY=${CURRENCY:-EUR}
|
|
- ROUNDING_MINUTES=${ROUNDING_MINUTES:-1}
|
|
- SINGLE_ACTIVE_TIMER=${SINGLE_ACTIVE_TIMER:-true}
|
|
- ALLOW_SELF_REGISTER=${ALLOW_SELF_REGISTER:-true}
|
|
- IDLE_TIMEOUT_MINUTES=${IDLE_TIMEOUT_MINUTES:-30}
|
|
- ADMIN_USERNAMES=${ADMIN_USERNAMES:-admin}
|
|
# IMPORTANT: Change SECRET_KEY in production! Used for sessions and CSRF tokens.
|
|
# Generate a secure key: python -c "import secrets; print(secrets.token_hex(32))"
|
|
#
|
|
# CSRF CONFIGURATION:
|
|
# - WTF_CSRF_SSL_STRICT: Set to 'false' for HTTP access (localhost or IP address)
|
|
# Set to 'true' only when using HTTPS in production
|
|
# - If accessing via IP address (e.g., 192.168.1.100), also set:
|
|
# SESSION_COOKIE_SECURE=false and CSRF_COOKIE_SECURE=false
|
|
#
|
|
# TROUBLESHOOTING: If forms fail with "CSRF token missing or invalid":
|
|
# 1. Verify SECRET_KEY is set and doesn't change between restarts
|
|
# 2. Check CSRF is enabled: WTF_CSRF_ENABLED=true
|
|
# 3. Ensure cookies are enabled in your browser
|
|
# 4. If behind a reverse proxy, ensure it forwards cookies correctly
|
|
# 5. Check the token hasn't expired (increase WTF_CSRF_TIME_LIMIT if needed)
|
|
# 6. If accessing via IP (not localhost): WTF_CSRF_SSL_STRICT=false
|
|
# For details: docs/CSRF_CONFIGURATION.md and docs/CSRF_IP_ACCESS_GUIDE.md
|
|
# Disable strict Referer check by default to avoid privacy/port issues
|
|
- WTF_CSRF_SSL_STRICT=${WTF_CSRF_SSL_STRICT:-true}
|
|
- WTF_CSRF_ENABLED=${WTF_CSRF_ENABLED:-true}
|
|
- WTF_CSRF_TIME_LIMIT=${WTF_CSRF_TIME_LIMIT:-3600}
|
|
- SESSION_COOKIE_SECURE=${SESSION_COOKIE_SECURE:-true}
|
|
- SESSION_COOKIE_SAMESITE=${SESSION_COOKIE_SAMESITE:-Lax}
|
|
- REMEMBER_COOKIE_SECURE=${REMEMBER_COOKIE_SECURE:-true}
|
|
- CSRF_COOKIE_SECURE=${CSRF_COOKIE_SECURE:-true}
|
|
- CSRF_COOKIE_HTTPONLY=${CSRF_COOKIE_HTTPONLY:-false}
|
|
- CSRF_COOKIE_SAMESITE=${CSRF_COOKIE_SAMESITE:-Lax}
|
|
- CSRF_COOKIE_NAME=${CSRF_COOKIE_NAME:-XSRF-TOKEN}
|
|
- PREFERRED_URL_SCHEME=${PREFERRED_URL_SCHEME:-https}
|
|
- WTF_CSRF_TRUSTED_ORIGINS=$(WTF_CSRF_TRUSTED_ORIGINS:-https://localhost)
|
|
- DATABASE_URL=postgresql+psycopg2://timetracker:timetracker@db:5432/timetracker
|
|
- LOG_FILE=/app/logs/timetracker.log
|
|
|
|
# Expose only internally; nginx publishes ports
|
|
ports: []
|
|
volumes:
|
|
- app_data:/data
|
|
- ./logs:/app/logs
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
restart: unless-stopped
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:8080/_health"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 40s
|
|
|
|
db:
|
|
image: postgres:16-alpine
|
|
container_name: timetracker-db
|
|
environment:
|
|
- POSTGRES_DB=${POSTGRES_DB:-timetracker}
|
|
- POSTGRES_USER=${POSTGRES_USER:-timetracker}
|
|
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-timetracker}
|
|
- TZ=${TZ:-Europe/Brussels}
|
|
volumes:
|
|
- db_data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 30s
|
|
restart: unless-stopped
|
|
|
|
|
|
volumes:
|
|
app_data:
|
|
driver: local
|
|
db_data:
|
|
driver: local |