Release / verify (push) Canceled after 0s
CI / checks (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / backend (push) Canceled after 0s
Release / test (push) Canceled after 0s
Release / publish (backend) (push) Canceled after 0s
Release / publish (frontend) (push) Canceled after 0s
Release / publish (proxy) (push) Canceled after 0s
Release / publish (updater) (push) Canceled after 0s
Backups, Governance, Regelnutzung. Auf einem Test-Stack mit drei Test-Firewalls geprüft; die Vollsicherung Ende-zu-Ende (sichern, Schaden anrichten, zurückspielen), siehe Handbuch „Konfigurationssicherungen“, „Sichern und Wiederherstellen“. - **Rolle „Operator“**: rollt aus und betreibt (Regeln, NAT, VPN, Wartung, Sicherungen), verwaltet aber keine Benutzer, Einstellungen oder Hosts und lädt keine Roh-Backups herunter. Abgelehnte Versuche stehen im Audit. Tokens eines Operators gehen bis zum Bereich „Ausrollen“. - **Audit-Export** (Einstellungen): Syslog (RFC 5424, UDP/TCP/TLS) und/oder signierter Webhook, mindestens einmal zugestellt, ohne Objektinhalte. Neue Tabelle `audit_export_settings` (Migration `0010`). - **Geplante Backups** mit Aufbewahrung, **verschlüsseltem Offsite-Ziel** (S3-kompatibel, AES-256-GCM, Passphrase bleibt auf dem Server), Benachrichtigungen bei Problemen und einer **Änderungsansicht** zwischen zwei Sicherungen (Geheimnisse nur als Hash). Neue Tabellen `backup_settings`, `backup_uploads`, Spalte `notification_settings.backup_failed_enabled` (Migration `0011`). Die S3-Signatur ist gegen die offiziellen AWS-Beispiele geprüft; ein Lauf gegen einen echten Anbieter steht noch aus. - **Vollsicherung** `opnexus backup --full` und `opnexus restore`: Datenbank und alle Geheimnis-Volumes in einer verschlüsselten Datei; Ende-zu-Ende auf dem Test-Stack geprüft (Sichern, Schaden anrichten, Zurückspielen). - **Regelnutzung**: wann hat jede Regel zuletzt etwas getroffen (aus den pf-Zählern, überlebt Neuladen und Neustart); Befund „Regeln in Benutzung“ in der Sicherheitslage nach 30 Tagen ohne Treffer. Neue Tabelle `rule_hits` (Migration `0012`). - Änderungen an Einstellungen (`/api/settings/…`) und das Auslösen von Sicherungen stehen jetzt im Audit.
60 lines
2.4 KiB
Python
60 lines
2.4 KiB
Python
"""Audit export settings (admin only): where the audit trail is forwarded to."""
|
|
from datetime import datetime, timezone
|
|
|
|
from fastapi import APIRouter, Depends
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.database import get_db
|
|
from app.errors import ErrorCode, api_error
|
|
from app.models.user import AdminUser
|
|
from app.routers.auth import current_user
|
|
from app.schemas.audit_export import AuditExportRead, AuditExportUpdate
|
|
from app.services import audit_export_service as svc
|
|
from app.services import notification_secret_store
|
|
|
|
router = APIRouter(prefix="/settings/audit-export", tags=["audit-export"])
|
|
|
|
|
|
def _require_admin(user: AdminUser) -> None:
|
|
if user.role != "admin":
|
|
raise api_error(403, ErrorCode.ADMIN_REQUIRED)
|
|
|
|
|
|
@router.get("", response_model=AuditExportRead)
|
|
async def read(db: AsyncSession = Depends(get_db), user: AdminUser = Depends(current_user)):
|
|
_require_admin(user)
|
|
return await svc.get_settings(db)
|
|
|
|
|
|
@router.put("", response_model=AuditExportRead)
|
|
async def update(payload: AuditExportUpdate, db: AsyncSession = Depends(get_db), user: AdminUser = Depends(current_user)):
|
|
_require_admin(user)
|
|
row = await svc.get_settings(db)
|
|
was_active = svc.active(row)
|
|
for field in ("syslog_enabled", "syslog_host", "syslog_port", "syslog_protocol", "webhook_enabled", "webhook_url"):
|
|
value = getattr(payload, field)
|
|
if value is not None:
|
|
setattr(row, field, value.strip() if isinstance(value, str) else value)
|
|
if payload.clear_webhook_signing_secret:
|
|
notification_secret_store.delete_secret(svc.SECRET_NAME)
|
|
row.webhook_signing_secret_set = False
|
|
elif payload.webhook_signing_secret is not None:
|
|
notification_secret_store.save_secret(svc.SECRET_NAME, payload.webhook_signing_secret)
|
|
row.webhook_signing_secret_set = True
|
|
if not svc.active(row):
|
|
row.cursor_created_at = row.cursor_id = None # next enabling starts from "now" again
|
|
elif not was_active:
|
|
row.cursor_created_at, row.cursor_id = datetime.now(timezone.utc), None
|
|
await db.commit()
|
|
return row
|
|
|
|
|
|
@router.post("/test")
|
|
async def test(db: AsyncSession = Depends(get_db), user: AdminUser = Depends(current_user)):
|
|
_require_admin(user)
|
|
try:
|
|
await svc.send_test(await svc.get_settings(db))
|
|
except svc.ExportError as exc:
|
|
raise api_error(502, ErrorCode.UPSTREAM_REQUEST_FAILED) from exc
|
|
return {"success": True}
|