# Jumphost Gateway — Implementierung Umsetzung des Konzepts `Jumphost_Konzept.md` (v0.2): browserbasiertes SSH/RDP-Gateway mit TOTP-Pflicht, Gruppen-/Hostgruppen-RBAC, manipulationssicherem Audit-Log, SQLite/Python-Backend, browserbasierter Admin-Oberflaeche und Ansible-Deployment (mit/ohne nginx, mehrere TLS-Modi). ## Verzeichnisstruktur ``` app/ Backend (FastAPI, Python 3.11+) security/ Crypto, Passwoerter, TOTP, Sessions, Audit-Hash-Chain, AV-Scan, API-Tokens auth/ Login-Flow, RBAC-/Token-Dependencies admin/ Admin-API (User/Gruppen/Hosts/Hostgruppen/Rollen/SSH-Keys/API-Tokens) catalog/ Sicht fuer normale Nutzer (direkt und ueber Gruppen zugewiesene Hosts) ssh_proxy/ SSH-Terminal-WebSocket + SFTP-Filetransfer rdp_proxy/ Guacamole-Protokoll-Tunnel zu guacd recordings/ Hash-verkettete Session-Aufzeichnung db/ SQLite-Migrationen (0001-0007, laufen automatisch beim Start) static/, templates/ Frontend (Vanilla JS, xterm.js, guacamole-common-js, Admin-Oberflaeche, API-Doku) scripts/ Betriebs-/Hilfsskripte (Admin anlegen, Assets bauen) ansible/ Deployment (Rollen, systemd-Unit-Templates) tests/ pytest-Suite ``` ## Lokale Entwicklung / Ausprobieren ```bash python3 -m venv .venv && source .venv/bin/activate pip install -r requirements-dev.txt bash scripts/fetch_frontend_assets.sh # vendored xterm.js / guacamole-common-js export JUMPHOST_ENV=development export JUMPHOST_DATA_DIR=/tmp/jumphost-dev export JUMPHOST_DEV_KEK=$(python3 -c "import secrets;print(secrets.token_hex(32))") export JUMPHOST_DEV_SESSION_SECRET=$(python3 -c "import secrets;print(secrets.token_hex(32))") uvicorn app.main:app --reload --port 8000 python scripts/create_admin.py --username admin # in zweitem Terminal ``` Danach `http://127.0.0.1:8000/` oeffnen, anmelden, TOTP einrichten (QR-Code scannen). Auf dem Dashboard erscheint fuer Admin-Konten oben rechts der Link **"Admin-Bereich"** (`/admin`) — darueber lassen sich Benutzer, Benutzergruppen, Hostgruppen/Hosts/SSH-Keys/RDP-Zugangsdaten, Rollenzuweisungen (direkt und gruppenweise) sowie API-Tokens vollstaendig ueber die Oberflaeche anlegen und verwalten; ein manueller Umweg ueber die rohe JSON-API ist dafuer nicht mehr noetig. Fuer RDP muss zusaetzlich ein laufender `guacd` erreichbar sein (siehe `JUMPHOST_GUACD_HOST`/`_PORT`). Die SQLite-Migrationen 0004 (`user_groups`), 0005 (`api_tokens`), 0006 (`tenants`) und 0007 (`crud_extras`) werden beim naechsten Start automatisch angewendet (`app/db.py`, `_apply_migrations`) — kein manueller Migrationsschritt noetig, auch nicht bei einem bestehenden Datenbestand. ## Admin-Oberflaeche, Gruppen-RBAC, API-Tokens und API-Dokumentation In dieser Session wurde die bis dahin nur per JSON-API bedienbare Verwaltung um eine vollstaendige, CSP-konforme Web-Oberflaeche unter `/admin` erweitert (`templates/admin.html`, `static/js/admin.js`), erreichbar nur fuer Admin- Konten. Tabs: Benutzer, Benutzergruppen, Hosts & Verbindungen (inkl. Connection-Anlage: Hostgruppe, Host, SSH-Key bzw. RDP-Zugangsdaten in einem Formular), Rollen, API-Tokens, Audit-Log. **Gruppen-RBAC (Migration `0004_user_groups.sql`)**: Neu sind *Benutzergruppen* (`user_groups`/`user_group_members`) als Teams von Personen, unabhaengig von den bestehenden *Hostgruppen* (Server-Gruppen). Eine Rolle kann einer Benutzergruppe auf eine Hostgruppe gewaehrt werden (`group_hostgroup_roles`) — das ist das "Sharen einer Verbindung mit einer Gruppe". Es gilt **volle Rollen-Vererbung**: jedes Mitglied einer Gruppe hat automatisch alle Rollen, die dieser Gruppe gewaehrt wurden, zusaetzlich zu seinen individuell zugewiesenen (`user_hostgroup_roles`, unveraendert bestehen geblieben). `app/rbac.py::user_has_role()` prueft dafuer beide Zuweisungswege (UNION-Query); `app/catalog/routes.py` (Host-Liste fuer normale Nutzer) beruecksichtigt beide ebenso. **API-Tokens (Migration `0005_api_tokens.sql`, `app/security/api_tokens.py`)**: Jeder Admin kann unter dem Tab "API-Tokens" persoenliche Tokens erzeugen (`Authorization: Bearer `), mit granularen **Read/Write-Scopes pro Ressource** (z. B. `hosts:ro`, `hosts:rw`, `users:ro`, `groups:rw`, ... vollstaendige Liste unter `GET /admin/scopes`). Tokens werden nur gehasht gespeichert (Klartext ist nur einmal direkt nach dem Anlegen sichtbar) und koennen jederzeit widerrufen werden. `app/auth/deps.py::require_admin_or_scope()` akzeptiert entweder eine gueltige Admin-Session (Cookie, wie bisher) oder einen Token mit passendem Scope — **niemals beides vermischt**: sobald ein `Authorization`-Header vorhanden ist, wird ausschliesslich er geprueft, ein evtl. noch gueltiges Session-Cookie wird dabei ignoriert. Aus Gruenden der Rechte-Eskalation sind die Token-Verwaltungs-Endpunkte selbst (`/admin/tokens/*`) bewusst **nicht** per Token nutzbar, sondern ausschliesslich per Admin-Session (`require_global_admin`) — ein geleaktes Token kann also nie weitere Tokens anlegen, auflisten oder widerrufen. Tokens sind ausschliesslich fuer die Management-API vorgesehen; SSH-/RDP-/SFTP-Sitzungen bleiben Session-Cookie-authentifiziert. **API-Dokumentation (`/docs`, `/openapi.json`)**: Nur fuer eingeloggte Admins sichtbar (`Depends(require_global_admin)` auf beiden Routen in `app/main.py`) — das bestehende Haertungskonzept, `/docs`/`/redoc`/`/openapi` nicht oeffentlich per FastAPI-Default auszuliefern (`docs_url=None, redoc_url=None, openapi_url=None`), bleibt unangetastet. Die Ansicht selbst (`templates/api_docs.html`, `static/js/api-docs.js`) ist bewusst **kein** vendored/CDN-bezogenes Swagger-UI-Bundle, sondern ein selbstgebauter, CSP-konformer Viewer ohne jede Laufzeit-Abhaengigkeit zu Drittanbietern: er liest `/openapi.json` per `fetch()` (`credentials: "same-origin"`) und rendert Endpunkte gruppiert nach Tag, ausschliesslich per `document.createElement`/`textContent` (kein `innerHTML`, keine Inline- Styles/-Scripts, siehe `tests/test_csp_compliance.py`). ## Phase 8: Mandantenfaehigkeit, CRUD-Vervollstaendigung, Dateitransfer, Mehrfachrollen Diese Session hat die Admin-Oberflaeche um sieben zusammenhaengende Erweiterungen ergaenzt: **1) Mandantenfaehigkeit ("volle Isolation + Mandanten-Admins", Migration `0006_tenants.sql`)**: jede Hostgruppe, Benutzergruppe, jeder SSH-Key und jedes API-Token gehoert zu genau einem Mandanten (`tenants`-Tabelle, Standard-Mandant `id=1` "Standard" fuer bestehende Daten). Es gibt zwei Admin-Stufen: **Super-Admin** (`users.is_admin=1`, unveraendert wie zuvor) sieht/verwaltet **alle** Mandanten weiterhin vollstaendig — keine Regression gegenueber Phase 7. **Mandanten-Admin** (neue `tenant_admins`-Zuordnung, ein User kann Admin mehrerer Mandanten sein) sieht/verwaltet **ausschliesslich** die Ressourcen seines/seiner Mandanten; jeder Zugriffsversuch auf eine fremde Mandanten-ID liefert bewusst **404** (nicht 403, siehe `app/tenancy.py`, `TenantScope`) — ein Mandanten-Admin soll aus der Fehlerantwort nicht einmal ableiten koennen, dass eine ID ausserhalb seines Mandanten ueberhaupt existiert. Tenant-CRUD und die Ernennung von Mandanten-Admins sind ausschliesslich Super-Admin-Aktionen (neuer Tab "Mandanten" in der Admin-Oberflaeche, fuer Mandanten-Admins nicht sichtbar). Ein API-Token ist immer an **genau einen** Mandanten gebunden (`api_tokens.tenant_id`) und kann diesen nie verlassen. **2) CRUD-Vervollstaendigung**: jede Ressource, die die Admin-Oberflaeche anlegen kann, ist jetzt auch bearbeitbar und loeschbar — Benutzer (inkl. Passwort setzen, deaktivieren, reaktivieren), Benutzergruppen, Hostgruppen, Hosts, SSH-Keys (inkl. Rotation). Zwei bewusste Sicherheits-/Integritaets- Entscheidungen dabei: ein Benutzerkonto mit vorhandener Audit-Historie wird **nicht** hart geloescht (die Hash-Chain verweist absichtlich ohne `ON DELETE CASCADE` auf `users.id`, siehe Migration `0007_crud_extras.sql`), sondern deaktiviert und anonymisiert (`deleted_user_`, Passwort/TOTP geloescht); nur ein Konto ganz ohne Audit-Spuren wird tatsaechlich entfernt — die Response (`hard_deleted: true/false`) zeigt an, welcher Fall eintrat. Ein Host wird per Default **soft-deleted** (`is_active=0`, wie schon zuvor vom Katalog beruecksichtigt); `DELETE /admin/hosts/{id}?hard=true` versucht zusaetzlich ein echtes Entfernen, faellt aber automatisch auf Soft-Delete zurueck, falls der Host bereits Sitzungshistorie hat. **3) Login-Verlauf**: neuer Tab "Login-Verlauf" filtert clientseitig aus dem bestehenden Audit-Log-Feed (`GET /admin/audit-log`) gezielt Login-/Logout-/ Fehlversuch-Ereignisse heraus — kein neuer Backend-Endpunkt noetig, da die Rohdaten bereits vorhanden waren, nur bisher nicht dediziert sichtbar. **4) "Details"-Fix fuer Hosts**: der defekte "Details"-Button in der Hosts- Tabelle wurde durch einen neuen Endpunkt `GET /admin/hosts/{id}` (liefert den vollstaendigen, aktuellen Datensatz inkl. zugeordneter SSH-Keys und ob RDP-Zugangsdaten hinterlegt sind) sowie eine neu geschriebene, robuste `showHostDetail()`-Funktion in `admin.js` ersetzt, die bei jedem Aufruf frisch nachlaedt statt sich auf ggf. veraltete Listendaten zu verlassen, und Fehler inline anzeigt statt sie zu verschlucken. Da diese Sandbox die App nicht tatsaechlich im Browser ausfuehren kann, war die urspruengliche Ursache nicht direkt reproduzierbar — bitte nach dem Update pruefen, ob der Button jetzt zuverlaessig funktioniert, oder bei einem verbleibenden Fehler die genaue Browser-Konsolenmeldung mitteilen. **5) Eigener "Zugangsdaten"-Tab**: buendelt SSH-Keys UND RDP/Windows- Passwoerter (bisher Teil des Hosts-Formulars) an einer Stelle, inkl. Uebersicht, welche RDP-Hosts bereits ein Passwort hinterlegt haben (`GET /admin/rdp-credentials`). **6) Dateitransfer-Fenster (SSH-Terminal)**: der bisherige, `prompt()`- basierte Einzel-Upload-Knopf in der Terminal-Sitzung (`templates/terminal.html`, `static/js/terminal.js`) wurde durch ein eigenstaendiges Dateitransfer-Panel ersetzt, das **beide Richtungen** abdeckt — Upload (wie zuvor, jetzt mit eigenem Formular statt Browser-`prompt()`) und **Download** (neu in der UI; der Backend-Endpunkt `GET /ssh/{host_id}/files/download` existierte bereits und wird per `fetch()` + Blob + synthetischem ``-Link angesteuert, damit Fehler inline im Panel erscheinen statt die Seite zu verlassen). Das Panel fuehrt zusaetzlich ein kurzes Transfer-Log der laufenden Sitzung. Gilt bewusst nur fuer SSH (RDP hat in dieser Implementierung keinen eigenen Dateitransfer-Endpunkt). **7) Mehrfachauswahl bei Rollenvergabe**: `POST /admin/roles/grant` und `POST /admin/group-roles/grant` akzeptieren jetzt `role_names` (Liste, 1-6 Rollen) statt einer einzelnen `role_name` — ein Benutzer bzw. eine Benutzergruppe kann damit in einem Schritt mehrere Rollen auf derselben Hostgruppe erhalten (Checkbox-Raster statt Dropdown in der Oberflaeche). Das Entziehen bleibt bewusst pro Zeile/Rolle (`role_name`, Einzahl) — das entspricht dem bestehenden "Entziehen"-Knopf pro Tabellenzeile und braucht keine Mehrfachauswahl. ## Tests ```bash pytest -q ``` 61 Tests (vorher 46) decken ab: Argon2id/TOTP-Grundfunktionen, Audit-Hash-Chain (inkl. Manipulationserkennung und Trigger-Durchsetzung), RBAC-Logik inkl. Ablaufdaten, den vollstaendigen Login-Flow (Passwort -> TOTP-Enrollment -> Session-Cookie -> geschuetzte Endpunkte) gegen die echte FastAPI-App, 17 dedizierte Security-/Pentest-Tests (`tests/test_pentest_security.py`), 5 CSP-Regressionstests (`tests/test_csp_compliance.py`, inkl. `/admin` und `/docs`) sowie 14 neue Tests zu Gruppen-RBAC-Vererbung, Admin-Oberflaeche und API-Tokens (`tests/test_admin_groups_tokens.py`): Rollen-Vererbung ueber Gruppenmitgliedschaft, dass Gruppenrollen NICHT auf Nicht-Mitglieder wirken, Admin-only-Durchsetzung fuer alle neuen Endpunkte, Token-Scope-Durchsetzung (read/write/implizites read-bei-write/widerrufen/abgelaufen/unbekannter Scope), dass ein Token niemals andere Tokens verwalten kann (Rechte- Eskalationsschutz), sowie Admin-Gating von `/docs` und `/openapi.json`. Details, Vorgehen und Ergebnisse der urspruenglichen 27 Tests: siehe `Pentest_Report.md`. 15 weitere neue Tests zu Phase 8 (`tests/test_tenants.py`) decken zusaetzlich ab: Tenant-CRUD (Super-Admin only, Mandanten-Admin explizit ausgeschlossen), volle Mandanten-Isolation (Hostgruppen/Hosts/Benutzer/SSH-Keys/Audit-Log — fremde Mandanten-IDs liefern 404), Mehrfachauswahl bei Einzel- UND Gruppen-Rollenvergabe, Update/Deactivate/Delete fuer Benutzer (inkl. Anonymisieren-statt-Hart- Loeschen bei vorhandener Audit-Historie vs. echtem Hard-Delete ohne), Update/Delete fuer Hostgruppen (blockiert solange Hosts enthalten sind), Hosts (Soft- vs. Hard-Delete), SSH-Keys (inkl. Rotation) und Benutzergruppen, sowie den neuen `GET /admin/hosts/{id}`-Detailendpunkt inkl. SSH-Key- Zuordnungen und RDP-Zugangsdaten-Status. > **Hinweis:** Alle 34 in dieser und der vorherigen Session neu > hinzugekommenen Tests (5 CSP + 14 Admin/Gruppen/Token + 15 Mandanten/CRUD) > konnten in der verwendeten Cloud-Sandbox nicht mit `pytest -q` ausgefuehrt > werden, da diese Sandbox keinen Netzwerkzugriff auf PyPI hat und > `fastapi`/`aiosqlite`/`argon2`/`asyncssh` dort nicht vorinstalliert sind. > Fuer Phase 8 wurde stattdessen ein tieferes Verifikationsverfahren > angewendet als in der vorherigen Session: minimale Stub-Module fuer die > vier fehlenden Pakete (`fastapi`s `APIRouter`-Dekoratoren als No-Ops, ein > synchrones `sqlite3`-basiertes Shim mit aiosqlite-kompatiblem > `async`-Interface) erlauben es, die **echten** Endpunkt-Funktionen aus > `app/admin/routes.py` direkt (ohne HTTP-Layer) gegen eine echte > In-Memory-`sqlite3`-Datenbank mit allen 7 Migrationen auszufuehren — damit > wurden ueber 20 Integrations-Assertions (Mandanten-Isolation, > Rollen-Mehrfachvergabe, Delete-Semantik) tatsaechlich lauffaehig > verifiziert, nicht nur simulierte SQL-Queries. Der neue `tests/test_tenants.py` > selbst nutzt weiterhin `httpx.AsyncClient` gegen die echte ASGI-App (wie > alle anderen Testdateien) und wurde daher zeilenweise gegen die tatsaechliche > Endpunkt-Implementierung gegengeprueft (Pfade, Payload-Felder, Statuscodes), > aber nicht selbst mit `pytest` ausgefuehrt. Alle geaenderten Python-/ > JS-Dateien wurden mit `py_compile`/`node --check` auf Syntaxfehler geprueft, > und jeder `document.getElementById`-Aufruf in `admin.js`/`terminal.js` wurde > automatisiert gegen die tatsaechlichen HTML-IDs in `admin.html`/`terminal.html` > abgeglichen (0 Abweichungen). Bitte `pytest -q` lokal ausfuehren und > Ergebnis melden. Manuell zusaetzlich verifiziert (siehe Entwicklungs-Log dieser Session): Server-Start, Static-/Template-Auslieferung, Security-Header, vollstaendiger Login+TOTP-Flow per curl, Admin-CRUD (Hostgruppe/Host anlegen), Audit-Log- Chain-Verifikation per `/admin/audit-log/verify`. ## Security-Tests & Haertungs-Nachweis - **SAST**: `bandit -r app -c .bandit.yml` sowie `pip-audit` (Dependency-CVE- Scan) liefen zu Beginn dieser Implementierung sauber durch (0 Findings, 0 bekannte Schwachstellen) — Details inkl. vorher/nachher-Tabellen und der dabei gefundenen und gefixten Starlette-Regression in `Pentest_Report.md`. Fuer die in dieser Session neu hinzugekommenen Dateien (Admin-Oberflaeche, Gruppen-RBAC, API-Tokens) steht ein erneuter `bandit`/`pip-audit`-Lauf noch aus (keine neuen Fremdabhaengigkeiten hinzugekommen). - **DAST**: `tests/test_pentest_security.py` (17 Tests) sowie `tests/test_admin_groups_tokens.py` (14 Tests, s.o.) simulieren konkrete Angriffsmuster gegen die laufende ASGI-App. - **OS-Haertung (CIS/STIG)**: die `ansible/roles/os_hardening`-Rolle wurde in einer frueheren Session um ~10 zusaetzliche Task-Dateien vertieft (Kernel-Module, sysctl, PAM/Passwort-Policy, erweiterte auditd-Regeln, AIDE, rkhunter, Banner, cron/at-Restriktion, Dateirechte/sudo-Logging, SSHD-Haertung). Vollstaendiges Mapping auf CIS-Controls inkl. bewusst nicht automatisierter Punkte (mit Begruendung, z.B. Partitionslayout, Bootloader-Passwort, Volltextverschluesselung, physische Sicherheit) und bekannter Einschraenkungen: `ansible/roles/os_hardening/CIS_STIG_MAPPING.md`. - Abhaengigkeiten sind in `requirements.txt`/`requirements-dev.txt` exakt auf gegen `pip-audit` gepruefte Versionen gepinnt (u.a. fastapi 0.141.1, starlette 1.6.0, cryptography 50.0.0) — bewusste Reproduzierbarkeits-/ Haertungsmassnahme, siehe `Pentest_Report.md` Abschnitt 2. ## Deployment ```bash cd ansible cp inventory/production.ini.example inventory/production.ini # anpassen cp inventory/group_vars/jumphost_with_nginx.yml.example inventory/group_vars/jumphost_with_nginx.yml ansible-vault encrypt inventory/group_vars/vault.yml # vorher aus vault.yml.example befuellen ansible-playbook -i inventory/production.ini site.yml --ask-vault-pass ``` `enable_nginx_proxy` und `tls_mode` (`internal_pki` / `external_reverse_proxy` / `acme_public`) steuern Proxy- und TLS-Verhalten, siehe Konzept Kap. 7.2/7.2a und `ansible/inventory/group_vars/all.yml`. `ansible-playbook site.yml --syntax-check` laeuft sauber durch (in einer frueheren Session verifiziert); ein voller `--check`-Lauf gegen eine echte Testumgebung (inkl. `apt`, `systemd`, `guacd`-Paketverfuegbarkeit auf der Zieldistribution) steht noch aus. Ein Neustart des Dienstes (`systemctl restart jumphost`) reicht aus, damit die Migrationen 0004/0005 angewendet werden. ## Was bewusst noch offen ist Diese Implementierung ist ein funktionsfaehiges, getestetes und in einer frueheren Session per SAST+DAST geprueftes Grundgeruest, aber weiterhin **kein fertig auditiertes Produktivsystem**. Der vollstaendige Befund inkl. Methodik, Vorgehen und Restrisikobewertung steht in `Pentest_Report.md` — die dortige Abschnitt-4-Tabelle ("Restrisiko / vor Produktivbetrieb noch zu tun") ist die massgebliche, aktuelle Fassung dieser Liste. Kurzfassung: 1. **Echter Netzwerk-Penetrationstest** gegen eine laufende Instanz (Portscan, TLS-Konfiguration live, Session-Isolation unter Last, Guacamole-Protokoll-Fuzzing) — in dieser Sandbox ohne Netzwerkzugriff auf ein reales Zielsystem nicht durchfuehrbar. Was stattdessen gemacht wurde: SAST (bandit, pip-audit) und ein DAST-Testlauf gegen die App im Prozess (17+14 Security-/Admin-Tests, siehe `Pentest_Report.md` bzw. oben). 2. **RDP/guacd-Integrationstest gegen echte Zielsysteme** — die Guacamole-Protokoll-Implementierung (`app/rdp_proxy/guacd_client.py`) wurde gegen die Protokollspezifikation implementiert und die Handshake-Logik lokal auf Korrektheit der Kodierung geprueft, aber NICHT gegen einen laufenden `guacd` + FreeRDP + Windows-Ziel end-to-end getestet (keine RDP-Zielumgebung in dieser Sitzung verfuegbar). Parameter-Namen/-Reihenfolge sollten gegen die tatsaechlich eingesetzte guacd-Version verifiziert werden. 3. **Ansible-Rollout gegen ein reales Zielsystem** (`--check`-Dry-Run und echter Rollout in einer Staging-Umgebung) — bisher nur `ansible-playbook site.yml --syntax-check` sowie YAML-/Jinja2-Parsing verifiziert, siehe `ansible/roles/os_hardening/CIS_STIG_MAPPING.md`. 4. **OpenSCAP-Compliance-Scan** (`oscap xccdf eval`) gegen das zutreffende CIS/STIG-Profil — die `os_hardening`-Rolle wurde in einer frueheren Session um ca. 10 Task-Dateien vertieft (siehe CIS_STIG_MAPPING.md fuer das vollstaendige Mapping inkl. bewusst nicht automatisierter Punkte), ersetzt aber keinen zertifizierten Benchmark-Scan. 5. **Verteiltes Rate-Limiting** — der aktuelle Login-Rate-Limiter ist In-Memory/Single-Process (siehe `app/security/rate_limit.py`); bei horizontaler Skalierung durch einen geteilten Store ersetzen. 6. **fail2ban-Filter** setzt strukturierte Access-Logs mit `client_ip=`-Feld voraus, die die App aktuell nicht schreibt — vor Produktivbetrieb ein Access-Log-Middleware ergaenzen oder auf das Audit-Log umstellen. 7. **CSRF**: SameSite=Strict-Cookies mindern das Risiko bereits deutlich; ein expliziter CSRF-Token fuer zustandsaendernde JSON-Requests ist als zusaetzliche Haertungsstufe vorgesehen, aber noch nicht implementiert. 8. Alle in Konzept Kap. 12 genannten Erweiterungen (LDAP/AD, WebAuthn, PostgreSQL-Migrationspfad, Just-in-Time-Zugriff, HA) sind noch nicht umgesetzt. 9. **Social Engineering / physische Sicherheit / Lastest (DoS)** wurden nicht getestet — ausserhalb des Scopes eines Code-/Konfigurations-Reviews in dieser Sandbox-Umgebung. 10. **API-Token-Rotation/-Ablaufrichtlinien**: Tokens koennen zwar mit optionalem Ablaufdatum erzeugt und jederzeit widerrufen werden, aber es gibt noch keine erzwungene maximale Laufzeit, keine automatische Benachrichtigung vor Ablauf und keine "Token zuletzt benutzt vor X Tagen -> automatisch deaktivieren"-Routine. 11. Ein erneuter `bandit`/`pip-audit`-Lauf ueber die in dieser und der Phase-8-Session neu hinzugekommenen Dateien (Admin-Oberflaeche, Gruppen-RBAC, API-Tokens, Mandantenfaehigkeit, Dateitransfer-Panel) steht noch aus (keine neuen Fremdabhaengigkeiten hinzugekommen, siehe oben). 12. **Phase 8 / "Details"-Fix**: der Root-Cause des urspruenglich gemeldeten defekten "Details"-Buttons konnte in dieser Sandbox nicht reproduziert werden (die App laesst sich hier nicht im Browser ausfuehren) — der neue `GET /admin/hosts/{id}`-Endpunkt plus robustere `showHostDetail()`-Logik sollten das Problem loesen, sollten aber nach dem Deployment einmal manuell im Browser bestaetigt werden.