Files
security-alert-center/backend/app/config.py
T
PapaTramp 80320ae698 fix: harden SAC security per audit (0.4.15)
Close audit findings: anti-spoof client IP for login rate limit, JWT/CORS
enforce on startup, SSH host-key verification and sudo via stdin, optional
WinRM HTTPS, SSE httpOnly cookie auth, ingest body limit, ILIKE escaping,
and HMAC API key hashing with legacy SHA-256 fallback.
2026-07-07 19:32:12 +10:00

170 lines
6.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
from functools import lru_cache
from pathlib import Path
from pydantic_settings import BaseSettings, SettingsConfigDict
ROOT = Path(__file__).resolve().parents[2]
SCHEMA_PATH = ROOT / "schemas" / "event-schema-v1.json"
DEFAULT_CONFIG_FILE = Path("/opt/security-alert-center/config/sac-api.env")
def _resolve_config_files() -> tuple[str, ...]:
"""Файлы конфигурации для pydantic (корректный разбор кавычек и URL)."""
candidates: list[Path] = []
explicit = os.environ.get("SAC_CONFIG_FILE", "").strip()
if explicit:
candidates.append(Path(explicit))
else:
candidates.append(DEFAULT_CONFIG_FILE)
candidates.append(Path(".env"))
return tuple(str(p) for p in candidates if p.is_file())
def _settings_config() -> SettingsConfigDict:
files = _resolve_config_files()
return SettingsConfigDict(
env_file=files if files else None,
env_file_encoding="utf-8",
extra="ignore",
)
class Settings(BaseSettings):
model_config = _settings_config()
database_url: str = "postgresql+psycopg2://sac:sac@localhost:5432/sac"
sac_db_pool_size: int = 15
sac_db_max_overflow: int = 25
sac_public_url: str = "http://localhost:8000"
# URL для скачивания RDP bundle с ПК (WinRM). По умолчанию = SAC_PUBLIC_URL.
sac_agent_bundle_base_url: str = ""
jwt_secret: str = "change-me-in-production"
jwt_algorithm: str = "HS256"
jwt_expire_minutes: int = 60 * 24
# Fail-fast on weak JWT/CORS defaults (disable only for local dev/tests).
sac_security_enforce: bool = True
sac_ingest_max_body_bytes: int = 2_097_152
sac_bootstrap_api_key: str = ""
sac_admin_username: str = "admin"
sac_admin_password: str = ""
event_schema_path: str = str(SCHEMA_PATH)
cors_origins: str = "*"
telegram_enabled: bool = False
telegram_bot_token: str = ""
telegram_chat_id: str = ""
telegram_min_severity: str = "high"
webhook_enabled: bool = False
webhook_url: str = ""
webhook_secret_header: str = ""
webhook_secret: str = ""
webhook_min_severity: str = "high"
smtp_enabled: bool = False
smtp_host: str = ""
smtp_port: int = 587
smtp_user: str = ""
smtp_password: str = ""
smtp_from: str = ""
smtp_to: str = ""
smtp_starttls: bool = True
smtp_ssl: bool = False
smtp_min_severity: str = "high"
notify_min_severity: str = "warning"
notify_channels: str = "telegram,webhook,email"
# F-NOT-03: cooldown исходящих оповещений (dedup_key / fingerprint)
sac_notify_cooldown_enabled: bool = True
sac_notify_event_cooldown_sec: int = 90
sac_notify_problem_cooldown_sec: int = 300
# F-NOT-05: суточные отчёты из агрегации событий SAC (для exclusive / без отчёта агента)
sac_daily_report_enabled: bool = True
sac_daily_report_hour: int = 9
sac_daily_report_timezone: str = "Europe/Moscow"
sac_daily_report_skip_if_agent_sent: bool = True
sac_daily_report_require_activity: bool = True
# Порог «живости» агента
sac_heartbeat_stale_minutes: int = 300
# Периодическое сканирование stale heartbeat (proactive host_silence)
sac_host_silence_scan_enabled: bool = True
sac_host_silence_scan_interval_minutes: int = 5
# После ручного закрытия host_silence не открывать снова раньше N часов (auto-close по heartbeat — без паузы).
sac_host_silence_manual_resolve_cooldown_hours: int = 12
# Окно корреляции Problems: host + type + rule в одном open Problem
sac_problem_correlation_window_minutes: int = 60
# rule:brute_force_burst — ssh.login.failed / rdp.login.failed
sac_brute_force_window_minutes: int = 15
sac_brute_force_threshold: int = 30
# rule:privilege_spike — privilege.sudo.command
sac_privilege_spike_window_minutes: int = 10
sac_privilege_spike_threshold: int = 10
# rule:rdg_session_flap — RD Gateway 302→303 within window
sac_rdg_flap_window_min_sec: int = 1
sac_rdg_flap_window_max_sec: int = 10
sac_rdg_flap_dedup_sec: int = 30
# Внешние IP HAProxy/RDG-прокси: если external_ip в списке — путь Haproxy-RDG-Comp, иначе RDG-Comp
sac_rdg_haproxy_external_ips: str = ""
# Windows admin for agent qwinsta/logoff (domain-wide)
sac_win_admin_user: str = ""
sac_win_admin_password: str = ""
sac_winrm_use_https: bool = False
sac_winrm_server_cert_validation: str = "validate"
# Linux SSH admin for remote agent update (fallback SSH, phase 5)
sac_linux_admin_user: str = ""
sac_linux_admin_password: str = ""
sac_ssh_known_hosts_file: str = "/opt/security-alert-center/config/ssh_known_hosts"
sac_ssh_auto_add_host_key: bool = False
# Agent updates (mode gpo|sac, recommended versions, WinRM fallback script)
sac_agent_update_mode: str = "gpo"
sac_agent_update_fallback_enabled: bool = True
sac_agent_update_fallback_minutes: int = 15
sac_agent_recommended_rdp_version: str = ""
sac_agent_recommended_ssh_version: str = ""
sac_agent_min_rdp_version: str = ""
sac_agent_min_ssh_version: str = ""
sac_win_agent_update_script: str = ""
sac_agent_rdp_git_repo_url: str = "https://git.kalinamall.ru/PapaTramp/RDP-login-monitor.git"
sac_agent_ssh_git_repo_url: str = "https://git.kalinamall.ru/PapaTramp/ssh-monitor.git"
sac_agent_git_branch: str = "main"
sac_agent_git_cache_dir: str = "/opt/security-alert-center/cache/agent-repos"
sac_agent_git_cache_ttl_minutes: int = 30
sac_agent_git_token: str = ""
# Retention (app.jobs.retention / systemd timer)
sac_events_retention_days: int = 90
sac_problems_retention_days: int = 180
# UI login brute-force protection
sac_login_max_failures: int = 3
sac_login_failure_window_minutes: int = 15
sac_login_alert_telegram: bool = True
sac_login_ip_whitelist: str = ""
sac_fail2ban_ssh_jail: str = "sshd"
sac_fail2ban_sync_enabled: bool = False
sac_fail2ban_ignoreip_file: str = "/etc/fail2ban/jail.d/sac-login-whitelist.local"
# Seaca mobile (FCM push)
sac_fcm_enabled: bool = False
sac_fcm_project_id: str = ""
sac_fcm_service_account_json: str = ""
sac_mobile_refresh_expire_days: int = 90
@lru_cache
def get_settings() -> Settings:
return Settings()