Zum Inhalt

Admin-Zugriff & Split-Container

Der Admin-Bereich (alles unter /admin) kann zusätzlich vom User-Portal getrennt werden, damit ein gestohlener Super-Admin-Account allein nicht reicht — der Angreifer muss zusätzlich Netzwerkzugang zur Admin-Topologie haben.

Drei Modi

Modus Wer kommt rein Aufwand Wann sinnvoll
unrestricted (Default) Jeder mit Login keiner Single-Person-Setup, kein erhöhter Threat
ip_allowlist Nur IPs aus deiner CIDR-Liste 1 Setting Office-IP / VPN-Subnet — ausreichend für die meisten Self-Hoster
split_container Nur wer den Admin-Hostname / VPN / SSH-Tunnel erreicht hoch IT-Firmen mit vielen Endkunden

unrestricted und ip_allowlist werden direkt unter Admin → System → Zugriff umgeschaltet. split_container braucht Compose-Setup-Arbeit — dafür ist der Setup-Wizard.


Split-Container — die drei Setup-Profile

Wenn du split_container aktivierst, läuft Vesana mit zwei API-Containern (public + admin). Wie der Traffic zu denen kommt, hängt davon ab, wer bei dir TLS terminiert. Dafür gibt es drei Profile:

stack-only — Stack-internes nginx terminiert TLS

Standard-Self-Hoster. Der nginx-Container im Compose-Stack hört direkt auf Port 443 des Hosts und macht selbst TLS.

So erkennst du das:

# Es gibt einen nginx-Container im Compose-Stack:
docker ps | grep nginx
# → vesana-nginx-1 ... 0.0.0.0:443->443/tcp ...

# Auf dem Host läuft kein zusätzliches nginx:
systemctl status nginx 2>/dev/null | head -1
# → Unit nginx.service could not be found.   ODER   inactive (dead)

Was der Wizard macht: schreibt den Admin-Vhost ins Volume /etc/vesana/nginx-overrides/admin-vhost.conf, das Stack-nginx liest ihn beim Restart über das include. Compose-Skript publiziert ggf. den zusätzlichen Admin-Port und macht restart nginx selbst.

Was du machst: Skript pasten, fertig.

vor-nginx — nginx (oder Caddy/Traefik) auf dem Host terminiert TLS

Du hast einen Reverse-Proxy außerhalb des Compose-Stacks. Der hört auf Port 443 und reicht HTTP an den Stack durch — entweder an den Stack-internen nginx oder direkt an die API. Häufig bei Setups mit mehreren Diensten auf einem Host (Vesana plus andere Apps), oder wenn der Reverse-Proxy schon vor Vesana da war.

So erkennst du das:

# Auf dem Host läuft ein nginx (oder Caddy/Traefik) als Service:
systemctl status nginx
# → active (running)

# Der hört auf Port 443:
ss -tlnp | grep ':443 '
# → LISTEN ... users:(("nginx", ...))

# Im Compose-Stack ist nginx nicht auf 443 gemappt:
docker ps | grep nginx
# → 0.0.0.0:8181->443/tcp   ODER   gar nicht 443 sondern z.B. 8180->80

Caddy / Traefik / HAProxy zählen genauso als „vor-nginx" — der Wizard generiert nginx-Vhost-Snippets, aber das Konzept (Reverse-Proxy auf Host, Backend im Container) ist identisch. Bei Caddy/Traefik musst du den generierten nginx-Vhost in deine eigene Config übersetzen.

Was der Wizard macht: generiert ein Skript, das die Vhost-Datei nach /etc/nginx/sites-available/<host>-admin legt, in sites-enabled verlinkt und nginx reloaded. Plus Compose-Skript, das den api-admin-Container auf 127.0.0.1:8082 published.

Was du machst: Vhost-Skript als root pasten, dann Compose-Skript pasten. Bei Caddy/Traefik den Vhost manuell übersetzen.

bare-metal — kein Compose-Stack

Du betreibst Vesana direkt via systemd auf dem Host (kein Docker). Selten, aber gibt's. Hier kann der Wizard wenig automatisieren — du startest den zweiten API-Prozess selbst.

So erkennst du das:

# Es gibt KEINEN api-Container:
docker ps | grep vesana-api

# Stattdessen: systemd-Service:
systemctl status vesana-api
# → active (running)

Was du machst: zweite systemd-Unit für api-admin mit VESANA_API_MODE=admin auf einem zweiten Port, Public-API auf VESANA_API_MODE=public umstellen, Reverse-Proxy auf admin-Port routen. Plan-Referenz: docs/admin-isolation-plan.md §6.8 Variante A.


Welche Wahl für mich?

Frage 1 — Läuft Vesana in Docker Compose?
    Nein  →  bare-metal
    Ja    →  Frage 2

Frage 2 — Hast du AUSSERHALB des Compose-Stacks einen
          Reverse-Proxy (nginx / Caddy / Traefik) der auf
          Port 443 hört?
    Nein  →  stack-only
    Ja    →  vor-nginx

Der Wizard im Admin-Portal hat eine Auto-Detection, die in den meisten Fällen richtig liegt. Bei Misdetection kannst du das Profil in Schritt 1 manuell überschreiben.


Konflikte & Sicherheits-Hinweise

  • Cookie-Domain: Der Admin-Hostname und der User-Hostname sollten dieselbe Parent-Domain teilen, sonst geht das Session-Cookie verloren beim Wechsel. Wizard schlägt automatisch .example.com vor wenn beide auf *.example.com liegen.
  • Custom-Port ≠ Subdomain: Browser isolieren Cookies nicht per Port. Port-Mode (z.B. :8443) macht den Admin-Bereich nur per Permission-Check sicher, nicht per Browser-Origin. Wenn dir das wichtig ist → Subdomain-Mode + eigenes Cert.
  • Notfall-Bypass: Wenn du dich aussperrst (Setting split_container aber Compose-Override noch nicht aktiv), greift der Drift-Fallback: ADMIN-Routen sind nur noch über Localhost erreichbar. Plus VESANA_ADMIN_BYPASS=true als Env-Var (30 min Override) oder python -m api.app.cli.admin_recovery reset-mode unrestricted.

Weiterführend

  • Operator-Runbook (im Repo): docs/split-container-setup.md — Schritt-für-Schritt für eine Variante-A-Aktivierung, inkl. nginx-Templates und Recovery-Pfaden.
  • Plan: docs/admin-isolation-plan.md §6.8 — Architektur-Entscheidungen (Variante A/B/C, Cookie-Scope, Drift-Fallback).
  • Permissions: Rollen & Permissions — wie audit_log.view_* und trash.*_*_scope mit den Modi zusammenwirken.