Пользователь сам включает TOTP (QR, приложение-аутентификатор); 10 одноразовых кодов восстановления; superadmin сбрасывает 2FA другому пользователю. Вход в два шага: /auth/login → mfa_token, /auth/login/2fa — под теми же лимитами и advisory-lock, что пароль. Секрет шифруется Fernet-ключом TOTP_ENC_KEY, защита от повторного использования кода (totp_last_step), лимиты перебора кода при отключении. Миграция 0015, зависимости pyotp, segno, cryptography. UI: второй шаг входа, диалоги 2FA в меню логина, бейдж и сброс в «Пользователях»; submitDialog не закрывает окно, открытое следующим шагом цепочки. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
37 lines
1.3 KiB
YAML
37 lines
1.3 KiB
YAML
services:
|
||
db:
|
||
image: postgres:16
|
||
environment:
|
||
POSTGRES_DB: ${POSTGRES_DB}
|
||
POSTGRES_USER: ${POSTGRES_USER}
|
||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||
restart: unless-stopped
|
||
volumes: [pgdata:/var/lib/postgresql/data]
|
||
ports: ["127.0.0.1:${DB_HOST_PORT}:5432"]
|
||
healthcheck:
|
||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
||
interval: 3s
|
||
retries: 20
|
||
app:
|
||
build: .
|
||
restart: unless-stopped
|
||
depends_on:
|
||
db: {condition: service_healthy}
|
||
environment:
|
||
DATABASE_URL: postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
|
||
JWT_SECRET: ${JWT_SECRET}
|
||
ADMIN_USERNAME: ${ADMIN_USERNAME}
|
||
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
|
||
TRUSTED_PROXIES: ${TRUSTED_PROXIES:-}
|
||
TOTP_ENC_KEY: ${TOTP_ENC_KEY:-}
|
||
# APP_BIND: 0.0.0.0 — доступ из LAN (по умолчанию, как раньше); 127.0.0.1 — только через reverse-proxy с TLS (см. README «Публикация»)
|
||
ports: ["${APP_BIND:-0.0.0.0}:${APP_PORT}:8000"]
|
||
healthcheck:
|
||
test: ["CMD", "python", "-c", "import sys, urllib.request; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8000/healthz', timeout=3).status == 200 else 1)"]
|
||
interval: 15s
|
||
timeout: 5s
|
||
start_period: 30s
|
||
retries: 5
|
||
volumes:
|
||
pgdata:
|