Container-Hosting (Docker-Extension)¶
Die Docker-Extension bringt Container-Hosting pro Website ins Panel — auf Basis von rootless Podman, isoliert und ohne Docker-Daemon by default. Sie wird als Opt-in-Extension über den Marktplatz installiert; der Panel-Kern bleibt unberührt, solange sie nicht aktiviert ist.
Architektur in einem Satz¶
Ein Container ist ein neuer Website-App-Typ (neben PHP, Node.js, Python, Statisch), an genau eine Website gebunden. Er läuft rootless als der Linux-User dieser Website, ist nur über den nginx-Domain-Proxy der eigenen Website erreichbar (Port intern auf 127.0.0.1), und wird über eine eigene Seite Container in der Sidebar verwaltet.
Installation & Freigabe¶
- Extension aktivieren (Marktplatz) — installiert die Runtime nur auf den gewählten Servern: Podman + die rootless-Helfer (uidmap, fuse-overlayfs, passt) und den CVE-Scanner Grype. Das erfüllt „kein Docker-Daemon by default".
- Paket-Permission — im Paket legt der Admin mit Container-Hosting erlauben (
perm_docker) fest, ob ein Kunde Container nutzen darf, plus Limits: Container, RAM je Container (MB), CPUs je Container, Disk je Container (GB) (jeweils 0 = unbegrenzt).
Einen Container betreiben¶
- Website vom Typ „Container" anlegen (unter Websites). Sie bekommt automatisch einen internen Port und eine nginx-Proxy-Regel: Domain →
127.0.0.1:<Port>. - Auf der Seite Container ein Image bereitstellen. Der Dialog durchsucht Docker Hub live — nichts zu pflegen: eine leere Suche zeigt die am meisten gesternten offiziellen Images, Tippen durchsucht ganz Docker Hub. Ein Klick auf ein Ergebnis füllt die Image-Referenz (plus, bei gängigen Images, einen Vorschlag für den internen Port) und bietet ein Version / Tag-Dropdown (die echten Docker-Hub-Tags des Images, neueste zuerst), sodass statt des impliziten
:latesteine feste Version gepinnt werden kann; Eigenes Image bleibt für eine freie Referenz (weiter über die Registry-Allowlist abgesichert). Der Controller proxyt Docker Hub (vermeidet CORS + teilt ein Rate-Limit) und cacht die Ergebnisse ~30 Min. Podman zieht das Image vondocker.io, Grype scannt es beim Deploy. Der Deploy läuft im Hintergrund (die Zeile zeigt Wird bereitgestellt …) — der erste Deploy auf einem Server installiert zusätzlich die Runtime + Grype-DB und kann einige Minuten dauern. Danach: Start / Stopp / Neustart / Entfernen, Live-Status/Health und Logs.
Sicherheitsmodell (rootless, ehrlich)¶
Jeder Container läuft mit:
- Rootless / User-Namespace pro Website (eigene subuid/subgid-Range) — nie Host-Root.
--cap-drop=ALLplus minimaler Allowlist (CHOWN, SETUID, SETGID, NET_BIND_SERVICE),--security-opt=no-new-privileges.- seccomp Default-Deny-Profil (Podman-Standard, zusätzlich explizit gepinnt).
- Ressourcen-Limits aus dem Paket (
--memory,--cpus,--pids-limit). - Netzwerk: Port nur auf
127.0.0.1veröffentlicht (nur über den eigenen Domain-Proxy erreichbar); Host-Isolation via--no-map-gw(der Container kann keine Host-Dienste über das Gateway erreichen — SSRF/Lateral-Movement-Schutz); der bestehende SMTP-Egress-Block (Port 25) verhindert Direkt-zu-MX-Spam.
Grenzen des rootless-Modells (keine Schönfärberei)
- AppArmor wird auf Container nicht angewandt: rootless Podman kann kein AppArmor-Profil laden (fehlendes
CAP_MAC_ADMIN). Die Kernel-Ebene wird durch seccomp + Caps + User-Namespace abgesichert. - Shared Kernel: standardmäßig (Standard-Isolation) teilen sich rootless-Container den Host-Kernel — ein Kernel-Privesc-CVE betrifft alle. Mit gVisor (siehe unten) wird ein User-Space-Kernel dazwischengeschaltet und diese geteilte-Kernel-Exposition entfernt — zum Preis etwas geringerer Performance.
Isolationsstufe (gVisor)¶
Die Extension-Einstellung container_isolation bestimmt die Runtime, die neue Deploys verwenden:
standard(Default) — rootless Podman mitcrun. Container teilen sich den Host-Kernel; die Absicherung erfolgt über seccomp + Caps + User-Namespace (wie oben).gvisor— jeder Container läuft in der gVisor-Sandbox (runsc), einem User-Space-Kernel, der die Syscalls des Containers abfängt, sodass sie nie direkt den Host-Kernel erreichen. Das schließt die geteilte-Kernel-Exposition — ein Kernel-Privesc-CVE im Container bedeutet keine Host-Kompromittierung mehr — zum Preis etwas geringeren Syscall-/IO-Durchsatzes.
gVisor läuft hier vollständig rootless (über einen dünnen Wrapper, der runsc mit --ignore-cgroups --network=host startet). Die Runtime wird bei Bedarf installiert: Der erste gvisor-Deploy auf einem Server lädt und pinnt runsc; lässt sie sich nicht installieren, schlägt der Deploy sauber fehl (gvisor_not_installed), statt still auf Standard-Isolation zurückzufallen. Ein unter gVisor laufender Container zeigt auf der Container-Seite ein gVisor-Badge. Ein Wechsel der Einstellung wirkt auf neue Deploys — ein bestehender Container muss neu deployt werden, um auf (oder von) gVisor umzuziehen.
CVE-Pull-Gate (Grype)¶
Vor dem Start wird jedes Image mit Grype gescannt; das Ergebnis (Anzahl je Schweregrad) hängt an jeder Deploy-Antwort. Über die Panel-Einstellung container_cve_gate wird das Verhalten gesteuert:
off(Standard) — nur scannen und anzeigen, nie blockieren.critical— Deploy blockieren, wenn kritische CVEs vorhanden sind.fixable-critical— nur blockieren, wenn kritische CVEs mit verfügbarem Fix vorhanden sind.
Admins/Reseller können einen Block mit force überschreiben; Kunden nicht. Bei kritischen CVEs im Image wird die Benachrichtigung container_vulnerability_detected an Admin und Kunde ausgelöst.
Im Security Advisor zeigt eine „Container-CVEs"-Karte, die jedes bereitgestellte Container-Image mit Grype neu scannt und die Schweregrad-Zähler je Website auflistet. Die Karte erscheint nur, wenn die Docker-Extension aktiviert ist. Ein Hintergrund-Worker scannt zusätzlich alle 12 h neu und löst container_vulnerability_detected aus, wenn die Gesamtzahl kritischer CVEs steigt — so werden CVEs erkannt, die über die Zeit in einem Base-Image auftauchen (ohne Redeploy).
Der Site Doctor prüft zusätzlich die Live-Gesundheit jeder Container-Website: ein gestoppter oder ungesunder Container wird mit Ein-Klick-Neustart markiert, ein fehlender Container verlinkt zur Container-Seite für einen Redeploy. Der Auto-Worker löst site_issue_detected beim ersten Auftreten eines Container-Problems aus.
Abhängigkeiten & später hinzugefügte Server¶
Beim Aktivieren der Extension wird die Runtime an alle aktiven Server ausgerollt und idempotent installiert (Podman + rootless-Helfer + Grype + subordinate-id-Ranges). Ein nach der Aktivierung hinzugefügter Server erhält diesen Fan-out nicht — daher installiert der Agent Podman beim ersten Container-Deploy selbst (und füllt subuid/subgid nach), statt zu scheitern. Ergebnis: Jeder Server, der jemals einen Container hostet, hat die vollständige Runtime — egal ob bei Aktivierung vorhanden oder später hinzugefügt.
Persistentes Daten-Volume, Backup & Restore¶
- Daten-Volume — beim Deploy einen Volume-Pfad setzen (z. B.
/data), um dem Container ein persistentes Volume zu geben. Es ist ein Bind-Mount voncontainer-data/unterhalb des Website-Homes, gemountet mit:U(Podman chownt es auf den Container-User). Bei der rootless-Standard-Abbildung gehören die Schreibvorgänge eines als root laufenden Containers am Host dem Site-User. Ohne Volume-Pfad ist der Container flüchtig (Daten gehen beim Neuanlegen verloren). - Vollständige Definition gespeichert — Image, Env, interner Port und Volume-Pfad werden auf der Website gespeichert, sodass der Container allein aus dem DB-Stand rekonstruierbar ist.
- Backup — das Volume wird im normalen Datei-Backup der Website als reine Dateien mitgesichert. Der rootless-Image/Container-Store (
~/.local/share/containers) wird ausgeschlossen (nachladbare Image-Layer, keine Daten). - Auto-Redeploy beim Restore — nach einem Datei-Restore einer Container-Site legt das Panel den Container aus der gespeicherten Definition neu an, wieder am wiederhergestellten Volume. Der nginx-Domain-Proxy zeigt bereits auf den festen Host-Port, sodass die Site automatisch wieder erreichbar ist.
- Aufräumen — beim Löschen der Website (oder des Kunden) wird der Container sauber gestoppt+entfernt, die Lingering-Session deaktiviert und die subuid-Range freigegeben — keine verwaisten Container.
Registry-Allowlist & Image-Verwaltung¶
- Kuratierte Registry-Allowlist — die Extension-Einstellung
container_registry_allowlist(kommagetrennt, leer = alle) beschränkt, aus welchen Registries Images stammen dürfen. Ein Deploy aus einer nicht gelisteten Registry wird abgewiesen (registry_not_allowed). Dockers implizitedocker.io-Regel greift (nginx→docker.io). - Image-Verwaltung — die Container-Seite listet die lokalen Images je Website und bietet „Images bereinigen", um ungenutzte/verwaiste Images zu entfernen und Disk freizugeben, der gegen die Paket-Quota zählt.
Logs¶
Die Aktion Logs zeigt die stdout/stderr-Ausgabe des Containers (die letzten Zeilen, aktualisierbar). Container nutzen den k8s-file-Log-Driver, damit podman logs bei rootless Containern zuverlässig funktioniert — der Host-Default (journald) liefert für rootless Container keine Logs zurück.
Ein vor dem Fix erstellter Container zeigt keine Logs
Container, die mit einem älteren Agent bereitgestellt wurden, laufen mit journald und zeigen eine leere Log-Ansicht. Container einmal neu bereitstellen (gleiches Image) — danach fließen die Logs. Neue Deploys nutzen automatisch k8s-file.
Noch nicht enthalten (Phase 2)¶
- Datenbank-konsistente Volume-Snapshots (Quiesce vor dem Backup).
- Compose / Multi-Container-Apps pro Website.
- Docker-Hub Pull-Through-Cache zur Beschleunigung wiederholter Pulls.