bifroest/services/db-mcp/dbmcp/core/config.py
2026-10-27 08:34:17 +01:00

60 lines
2.4 KiB
Python

"""Einstellungen aus Umgebungsvariablen (Präfix DBMCP_)."""
import os
from collections.abc import Mapping
from dataclasses import dataclass
def _list(value: str) -> tuple[str, ...]:
return tuple(v.strip().lower() for v in value.split(",") if v.strip())
def _bool(value: str) -> bool:
return value.strip().lower() in ("1", "true", "yes", "on")
@dataclass(frozen=True)
class Settings:
# Schlüssel der Clients: Geheimnis -> Name (für das Audit-Log); DBMCP_API_KEYS="bifroest:geheim,…"
api_keys: Mapping[str, str]
# Datenbank-Hosts, zu denen verbunden werden darf: exakt oder als Endung (".db.intern")
allowed_hosts: tuple[str, ...] = ()
allow_local: bool = False
connect_timeout: float = 5
query_timeout: float = 15
max_rows: int = 500
max_tables: int = 300
sample_rows: int = 5
max_cell_chars: int = 200
max_sql_chars: int = 10000
# Host-Header, die der MCP-Transport annimmt (Schutz vor DNS-Rebinding); leer = keine Prüfung
http_hosts: tuple[str, ...] = ()
# Nur für die lokale Entwicklung: ohne Schlüssel starten
insecure_no_auth: bool = False
bind: str = "0.0.0.0"
port: int = 8080
@classmethod
def from_env(cls, env: Mapping[str, str] = os.environ) -> "Settings":
keys = {}
for entry in env.get("DBMCP_API_KEYS", "").split(","):
name, sep, secret = entry.strip().partition(":")
if entry.strip():
if not sep or not name or len(secret) < 16:
raise ValueError("DBMCP_API_KEYS: Einträge als name:geheimnis, Geheimnis mindestens 16 Zeichen")
keys[secret] = name
settings = cls(
api_keys=keys,
allowed_hosts=_list(env.get("DBMCP_ALLOWED_HOSTS", "")),
allow_local=_bool(env.get("DBMCP_ALLOW_LOCAL", "")),
connect_timeout=float(env.get("DBMCP_CONNECT_TIMEOUT_SECONDS", 5)),
query_timeout=float(env.get("DBMCP_QUERY_TIMEOUT_SECONDS", 15)),
max_rows=int(env.get("DBMCP_MAX_ROWS", 500)),
http_hosts=_list(env.get("DBMCP_HTTP_HOSTS", "")),
insecure_no_auth=_bool(env.get("DBMCP_INSECURE_NO_AUTH", "")),
bind=env.get("DBMCP_BIND", "0.0.0.0"),
port=int(env.get("DBMCP_PORT", 8080)),
)
if not settings.api_keys and not settings.insecure_no_auth:
raise ValueError("DBMCP_API_KEYS fehlt (nur für die Entwicklung: DBMCP_INSECURE_NO_AUTH=true)")
return settings