2026-09-20 12:27:47 +03:00
|
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
|
|
|
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
|
|
|
|
|
|
|
|
|
database_url: str = "postgresql+psycopg://ipam:ipam@localhost:55432/ipam"
|
2026-09-26 21:33:50 +03:00
|
|
|
jwt_secret: str = "" # обязателен: проверяется при старте приложения (validate_secrets), миграциям не нужен
|
2026-09-20 12:27:47 +03:00
|
|
|
jwt_ttl_minutes: int = 480
|
|
|
|
|
admin_username: str = "admin"
|
|
|
|
|
admin_password: str = ""
|
|
|
|
|
trusted_proxies: str = "" # CIDR через запятую: от них принимается X-Forwarded-For
|
2026-09-27 16:57:14 +03:00
|
|
|
totp_enc_key: str = "" # изменение 037: ключ Fernet для шифрования секретов TOTP; пусто — 2FA недоступна (503), потеря ключа = сброс 2FA всем
|
2026-09-20 12:27:47 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
settings = Settings()
|
2026-09-26 21:33:50 +03:00
|
|
|
|
|
|
|
|
_WEAK_SECRETS = {"change-me", "changeme", "dev-only-secret", "secret", "password"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def validate_secrets(s: Settings = settings) -> None:
|
|
|
|
|
"""Fail-fast при старте: подпись токенов известным/коротким ключом позволяет подделать токен любого пользователя."""
|
|
|
|
|
if len(s.jwt_secret) < 32 or s.jwt_secret.lower() in _WEAK_SECRETS:
|
|
|
|
|
raise RuntimeError("JWT_SECRET не задан, короче 32 символов или является заглушкой: сгенерируйте значение (python scripts/gen_env.py)")
|
|
|
|
|
if s.admin_password and len(s.admin_password) < 8:
|
|
|
|
|
raise RuntimeError("ADMIN_PASSWORD короче 8 символов: пароль слабее, чем допускает UI")
|
2026-09-27 16:57:14 +03:00
|
|
|
if s.totp_enc_key: # изменение 037: не задан — приложение стартует, 2FA недоступна; задан некорректно — отказ старта
|
|
|
|
|
from cryptography.fernet import Fernet
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
Fernet(s.totp_enc_key.encode())
|
|
|
|
|
except (ValueError, TypeError) as e:
|
|
|
|
|
raise RuntimeError("TOTP_ENC_KEY некорректен: нужен ключ Fernet (python -c \"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())\")") from e
|