Blog
Sollte jeder Mandant seine eigene VM bekommen?
Wählen Sie zwischen Containern pro Mandant, VMs und Hybrid-Setups mit einem risikobasierten Entscheidungsrahmen und den Härtungsschritten, die jede Option vertretbar machen.
Zusammenfassung
Multi-tenant-Hosting zwingt Sie zu entscheiden, wie weit Mandanten ineinander greifen können. Container nutzen Linux-Namespaces und Cgroups, um Prozesse und Ressourcen zu isolieren, teilen sich aber den Host-Kernel. Virtuelle Maschinen fügen eine Grenze auf Hardware-Ebene hinzu, auf Kosten von Geschwindigkeit und Betriebsaufwand. Ein hybrider Ansatz – Container in VMs – kann beides bieten, verdoppelt aber die Oberfläche, die Sie patchen müssen. Dieser Artikel führt Sie durch eine risikobasierte Entscheidung, einen direkten Vergleich und die Docker-Härtungsschritte, die auch in einer VM wichtig sind. Am Ende wissen Sie, welches Isolationsmodell zu Ihren Mandanten passt und was Sie vor dem Start konfigurieren sollten.
Ihre Multi-Tenant-App ist fast fertig. Sie haben eine Docker-Compose-Datei, die pro Kunde einen Stack hochfährt, und sie ist schnell. Dann fragt ein Freund, der ein Hosting-Unternehmen betreibt: 'Gibst du jedem Mandanten seine eigene VM?' Sie erstarren. Auf diese Frage waren Sie nicht vorbereitet. Dieser Artikel gibt Ihnen eine Möglichkeit, sie noch heute zu beantworten, ohne Sicherheitsteam. Sie machen das allein, also muss die Entscheidung einfach genug sein, um sie um 2 Uhr nachts zu verteidigen.
Hören Sie auf, nach dem 'besten' Modell zu suchen. Schreiben Sie zunächst auf, was passiert, wenn der Code eines Mandanten Ihren Host übernimmt. Definieren Sie den Schadenradius, bevor Sie ein Werkzeug auswählen. Diese Übung verrät Ihnen mehr als jeder Benchmark.
Der Kernel ist der Mitbewohner, den Sie nicht rauswerfen können
Container sind effizient, weil sie sich den Host-Kernel teilen. Dieses Teilen ist der ganze Trick und das ganze Risiko. Linux-Namespaces geben jedem Container seine eigene Sicht auf Prozesse, Netzwerk und Dateisystem. Control Groups (Cgroups) ermöglichen es Ihnen, CPU, Speicher und Datenträger-I/O zu begrenzen, sodass ein Mandant die anderen nicht aushungern kann. Aber keines erzeugt eine Hardware-Wand.
Stellen Sie sich einen Container als Prozess mit einem wirklich guten gefälschten Ausweis vor. Er glaubt, auf einer eigenen Maschine zu sein. Der Kernel ist jedoch eine einzige Kopie von Linux, die auf Ihrem Host läuft. Wenn ein Mandant eine Kernel-Schwachstelle ausnutzt, werden Namespaces zu Metadaten und nichts weiter. Ein Angreifer, der Kernel-Funktionen aufrufen kann, kann andere Namespaces auf demselben Kernel erreichen. Das ist der Container-Escape, von dem Sie immer hören.
Angenommen, Sie betreiben ein kleines B2B-Tool mit einem Container pro Kunde. Ein Kunde installiert ein zwielichtiges Plugin mit einem Remote-Code-Execution-Bug. Mit den Standard-Docker-Einstellungen läuft dieser Prozess als root im Container. Root in einem Container ist immer noch UID 0, und der Kernel unterscheidet diese UID nicht von Host-Root, es sei denn, Sie mappen Benutzer explizit. Der Angreifer kann versuchen auszubrechen, und der gemeinsame Kernel ist sein Ziel.
Der Ausfall muss nicht dramatisch sein. Ein einzelner Mandant, der Speicher leakt, kann den Host in den Swap treiben und alle anderen Mandanten verlangsamen. Ohne Cgroup-Limits ist eine einzelne fehlverhaltende Schleife ein Verfügbarkeitsangriff. Mit ihnen ist es ein blockierter Prozess und ein Alarm.
Heißt das, Container sind unsicher? Nein. Es bedeutet, dass Sie den Kernel als gemeinsame Vertrauenszone behandeln müssen. Bevor Sie wählen, schreiben Sie eine Risikoerklärung von einem Absatz: 'Wenn der Container eines Mandanten kompromittiert ist, kann der Angreifer auf Folgendes zugreifen: [list]. Die geschäftlichen Kosten wären: [amount or impact].' Wenn dieser Absatz Ihnen Angst macht, sind Sie nicht paranoid. Sie sind ehrlich.
Für einen tieferen Einblick in das Isolationsspektrum, von gemeinsam genutzten Containern bis zu vollständig getrennten Stacks, lesen Sie unseren Leitfaden zum Entwurf einer Multi-Tenant-Docker-Architektur.
Drei Möglichkeiten, es zu teilen (Wählen Sie eine, bevor Sie bereitstellen)
Es gibt wirklich drei Architekturen für Multi-Tenant-Isolation. Jede 'Best Practice' ist eine Kombination dieser.
| Ansatz | Isolationsbarriere | Am besten geeignet, wenn | Schwierigste Einschränkung |
|---|---|---|---|
| Container pro Mandant | Kernel-Namespaces + Cgroups | Viele kleine Mandanten, geringes Risiko pro Mandant, Bedarf an Dichte | Ein Kernel-Exploit kann alle Mandanten auf diesem Host brechen |
| Eine VM pro Mandant | Hypervisor/Hardware-Virtualisierung | Regulierte Daten, feindliche Mandanten, hoher Wert pro Mandant | Schwerer, langsamer bereitzustellen, Sie patchen ein OS pro Mandant |
| Container in VMs | VM-Grenze um containerisierte Workloads | Dichte plus eine harte Hülle zwischen Gruppen | Kosten und Betriebsaufwand fast doppelt so hoch |
Container pro Mandant. Dies ist der Standard für die meisten SaaS-Gründer. Jeder Mandant erhält seinen eigenen Container oder einen kleinen Compose-Stack. Die Bereitstellung ist sofort, Images sind klein, CI/CD ist unkompliziert. Ressourcenlimits verhindern, dass laute Nachbarn den Server auffressen. Der Kompromiss ist der gemeinsame Kernel. Wenn Sie Workloads nicht privilegiert halten und den Host regelmäßig patchen können, ist dies oft der richtige erste Schritt.
Stecken Sie nicht zwei Mandanten in denselben Container. Das ist ein gemeinsamer Kernel plus eine gemeinsame Laufzeit plus ein gemeinsames Dateisystem. Wenn ein Mandant eine Datei hochlädt, die einen Prozess erzeugt, befindet sich der andere Mandant bereits in derselben Prozesstabelle. Ein Container ist Ihre Isolierungseinheit; machen Sie einen Mandanten pro Container.
Was ist mit der Datenbank? Wenn sich jeder Mandant mit denselben Anmeldeinformationen mit einer MongoDB- oder PostgreSQL-Instanz verbindet, haben Sie bereits eine große gemeinsame Komponente hinzugefügt. Geben Sie jedem Mandanten separate Anmeldeinformationen und idealerweise eine separate Datenbank oder ein separates Schema. Container isolieren die App; die Datenbank ist oft das erste Leck, das ein Angreifer testet.
Eine VM pro Mandant. Geben Sie jedem Mandanten eine vollständige virtuelle Maschine. Der Hypervisor fügt eine Grenze auf Hardware-Ebene hinzu, genau das, was ein Kernel-Exploit überqueren muss, um den Host zu erreichen. Dies ist wichtig für regulierte Umgebungen oder wenn Mandanten nicht vertrauenswürdig sind. Die Kosten sind Dichte und Zeit. Sie verwalten jetzt eine Flotte von Betriebssystemen, nicht nur Container. Jede VM benötigt Updates, Sicherheitsagenten und Überwachung. Für einen Solo-Gründer ist das echte Arbeit.
Muster, die auf dieser Ebene funktionieren: Verwenden Sie Infrastructure-as-Code, um eine VM aus demselben Basis-Image zu erstellen, baken Sie Updates in neue Images ein, anstatt Live-Systeme zu patchen, und beenden Sie Workloads, die Sie nicht erkennen. Halten Sie den Verwaltungsport der VM für das Internet geschlossen.
Container in VMs. Dieser Hybrid wird in Anfänger-Tutorials selten behandelt. Sie legen eine kleine VM um jeden Mandanten (oder eine kleine Gruppe von Mandanten) und führen dann Container in dieser VM aus. Die VM ist ein Blast-Radius-Behälter; die Container sind nur bereitstellbare Einheiten. Das gibt Ihnen die harte Kante der Virtualisierung und die Reproduzierbarkeit von Images. Es kostet mehr, weil Sie für Virtualisierungs-Overhead und Container-Flexibilität zahlen, aber es kann das vernünftigste langfristige Modell sein, wenn Sie Mandanten nicht vollständig vertrauen können.
Ein häufiges Mikro-Beispiel: Ein Mandant betreibt eine Node-API und einen Hintergrund-Worker. Verwenden Sie anstelle eines großen Containers mit beiden Prozessen eine VM und dann zwei Container mit unterschiedlichen Ressourcenlimits, einem gemeinsamen Netzwerk und ohne direkte Internet-Exposition für den Worker. Die VM liefert die harte Kante; die Container liefern Struktur.
Welches sollten Sie wählen? Die Tabelle ist Ihre Shortlist. Die nächsten Abschnitte machen die Entscheidung konkret.
Wenn Sie sich für Container entscheiden, tun Sie diese sechs Dinge oder lassen Sie es bleiben
Container pro Mandant sind in Ordnung, wenn Sie jeden Container als potenziellen Angreifer behandeln. Das beginnt mit Konfiguration, nicht mit Wunschdenken.
0. Begrenzen Sie Ressourcen, bevor Sie jemandem vertrauen. Cgroups sind ein Fairness-Mechanismus und eine Verfügbarkeitsverteidigung. Setzen Sie --memory und --cpus pro Container. Ein Mandant, der Speicher leakt, sollte an seine eigene Grenze stoßen, nicht an die Ihres Servers. Dies ist keine Sicherheitsgrenze, aber ein lauter Nachbar ist ein Angriff ohne eine einzige Zeile Code. Ein praktischer Anfang: --memory 512m --cpus 0.5. Für einen Worker-Prozess starten Sie niedriger und skalieren hoch.
1. Als Nicht-Root-Benutzer ausführen. Lassen Sie den Container-Prozess niemals UID 0 verwenden, es sei denn, Sie benötigen es unbedingt. Legen Sie einen Benutzer im Dockerfile fest und übergeben Sie --user als zusätzlichen Schutz. Ein Exploit, der als unprivilegierter Benutzer läuft, hat weit weniger Wege zum Kernel. Erstellen Sie in Ihrem Dockerfile einen Benutzer: RUN useradd -u 10001 app und USER app. Überspringen Sie das nicht, um Zeit zu sparen.
2. Entfernen Sie jede Fähigkeit, die Sie nicht benötigen. Linux-Capabilities teilen die Macht von root in kleine Stücke. Die meisten Web-Apps benötigen fast keine. Beginnen Sie mit --cap-drop=ALL und fügen Sie nur das hinzu, was Sie nachweislich benötigen. Ein Container ohne CAP_SYS_ADMIN ist viel schwerer für Namespace-Tricks zu verwenden. Wenn Ihre App versucht, einen privilegierten Port zu binden, führen Sie sie auf einem hohen Port aus und setzen Sie einen Proxy davor, anstatt NET_BIND_SERVICE zu gewähren.
3. Machen Sie das Dateisystem schreibgeschützt. Ihre App sollte nicht in ihre eigene Container-Ebene schreiben. Mounten Sie ein tmpfs für Zustand. Ein Angreifer, der nicht auf die Festplatte schreiben kann, hat es viel schwerer, Persistenz zu etablieren. Eine kompromittierte PHP-App, die versucht, eine Webshell zu schreiben, schlägt fehl, wenn das Root-Dateisystem schreibgeschützt ist. Sie können ein benanntes Volume für ein beschreibbares Verzeichnis mounten, das Ihre App wirklich benötigt.
4. Seccomp und AppArmor oder SELinux anwenden. Diese schicken riskante Syscalls auf den Müllhaufen. Docker bringt ein Standard-Seccomp-Profil mit; verwenden Sie es. Fügen Sie ein AppArmor-Profil für eine weitere Ebene hinzu. Sie müssen nicht jeden Syscall beherrschen. Sie müssen verweigern, was ein normaler Web-Worker nie benötigt. Führen Sie niemals mit --privileged aus. Dieses Flag deaktiviert fast jede Verteidigung, die Sie gerade eingerichtet haben.
5. Segmentieren Sie das Netzwerk. Geben Sie nicht jedem Container eine Route zu jedem anderen Container. Standardmäßig verweigern, dann nur die Ports öffnen, die Sie benötigen. Ein kompromittierter Datenbankcontainer sollte Ihr Admin-Panel nicht scannen können. Wenn Mandanten in getrennten Netzwerken sind, kann ein Einbruch in einem Netzwerk nicht seitwärts wandern.
Ein praktischer Anfang:
docker run --user 10001 --cap-drop=ALL --security-opt no-new-privileges --read-only --tmpfs /tmp:rw,size=64M --security-opt seccomp=default.json --memory 512m --cpus 0.5 myimage
Setzen Sie dieselben Flags in eine Compose-Datei und wenden Sie sie auf jeden Mandanten an. Das ist nicht vollständig, aber es ist ein viel stärkerer Standard als das, was docker run standardmäßig bietet.
Für eine detailliertere Anleitung verwenden Sie unseren Schritt-für-Schritt-Härtungsleitfaden für Docker-Container im Multi-Tenant-Hosting.
Docker Enhanced Container Isolation ist die Ausnahme, die Sie kennen sollten
Wenn Sie in einer verwalteten Docker-Umgebung laufen, suchen Sie nach Docker Enhanced Container Isolation (ECI). Es verwendet User-Namespace-Isolation und eine sichere Container-Laufzeit unter der Haube. Root in einem Container wird auf einen unprivilegierten Benutzer auf dem Host abgebildet, sodass selbst ein als Root laufender Container keine Host-Root-Rechte erhält. Es blockiert auch gefährliche Capabilities und Syscalls standardmäßig. Das können Sie mit Vanilla-Docker nicht mit ein paar Flags nachbauen. Wenn Ihre Plattform es unterstützt, aktivieren Sie es. Es ersetzt nicht die Notwendigkeit von Nicht-Root-Benutzern und Ressourcenlimits, aber es verändert die Risikoberechnung.
Sie können einen Teil davon mit User-Namespace-Remapping (userns-remap) im Docker-Daemon annähern. Das ist nicht so vollständig wie eine sichere Laufzeit, aber besser als nichts. Wenn Sie es verwenden, überprüfen Sie, ob das UID-Mapping funktioniert, bevor Sie ihm vertrauen.
Der VM-Trugschluss: Der Umstieg auf virtuelle Maschinen ist keine Härtung
Hier ist der konträre Teil, und es ist der Teil, den die meisten überspringen. Wenn Sie zu einer VM pro Mandant wechseln und dann Ihre normalen Container darin bereitstellen, haben Sie Ihr Container-Sicherheitsproblem nicht beseitigt. Sie haben einen breiten Käfig hinzugefügt. Der Container-Escape funktioniert immer noch; der Angreifer landet nur in der VM statt auf dem Host. Das ist eine echte Verbesserung, aber Sie brauchen trotzdem die sechs Schritte.
Die andere Falle ist die Annahme, dass die VM selbst sicher ist. Ein Standard-Image mit einem schwachen SSH-Passwort, ungepatchten Basispaketen oder einem offenen Verwaltungsport ist ein Geschenk. Die Hypervisor-Grenze ist nur dann relevant, wenn der Gast gehärtet und aktualisiert ist. Andernfalls ist Ihre 'sichere VM' ein schnellerer Weg zur Kompromittierung, weil Sie sich sicher fühlen und aufhören zu überprüfen.
Was eine VM Ihnen bringt, ist ein reduzierbarer Schadenradius. Das Desaster eines Mandanten bleibt in einer VM. Was es Sie kostet, ist Ihre Zeit. Sie werden zum Sysadmin für so viele Betriebssysteme, wie Sie Mandanten haben. Wenn Sie ein Solo-Gründer sind, der ein Produkt ausliefert, fragen Sie sich, ob Sie die Stunden haben, um eine Flotte zu patchen und zu überwachen. Wenn ja, kann eine VM pro Mandant die richtige Entscheidung sein. Wenn nein, sind Container mit starker Härtung vielleicht ehrlicher.
Denken Sie auch daran, dass Ihr Hypervisor-Host ein kritisches Ziel ist. Ein kompromittierter Hypervisor kann alle Gäste sehen. Patchen Sie den Host, nicht nur die Gäste. Die VM entschuldigt Sie nicht vom Host-Patching; es erhöht den Einsatz für das Versäumen.
Eine Einschränkung zum Hybrid: Gehen Sie nicht davon aus, dass Container in einer VM Ihnen 'zwei Sicherheitsebenen' gratis geben. Die VM fügt eine Grenze hinzu; der Container braucht immer noch Nicht-Root, Capabilities und Seccomp. Sonst ist die erste Ebene nur so stark wie der schwächste Container.
Vier Fragen, die die Debatte in zehn Minuten klären
Optimieren Sie nicht im Abstrakten. Stellen Sie sich diese vier Fragen der Reihe nach. Schreiben Sie die Antworten auf.
1. Worauf hat mein Mandant Zugriff? Wenn ein Mandant nur auf seine eigene Web-App und Datenbank zugreifen kann, sind Container pro Mandant mit strengen Netzwerkregeln vertretbar. Wenn die Daten eines Mandanten reguliert oder finanziell sensibel sind, tendieren Sie zu VMs.
2. Wie viel würde mich die Kompromittierung eines Mandanten kosten? Rechnen Sie verlorene Kunden, rechtliche Risiken und Vertrauen zusammen. Wenn die Zahl größer ist als die Kosten für den Betrieb von VMs, geben Sie das Geld aus. Wenn nicht, sind Container eine rationale Wahl.
3. Wie viele Mandanten habe ich und wie viel zahlen sie? Viele kleine Abonnenten: Containerdichte ist wichtig. Eine Handvoll großer Konten: Geben Sie jedem eine VM und berechnen Sie entsprechend. Mandanten, die weniger als einen Kaffee zahlen, sollten nicht jeweils ein zu verwaltendes Betriebssystem erfordern.
4. Kann ich Dinge nach Plan patchen? Container teilen sich einen Host-Kernel, daher schützt das Patchen des Hosts alle. VMs vervielfachen Ihre Patch-Ziele. Wenn Sie wissen, dass Sie Updates überspringen werden, wählen Sie die Architektur mit weniger beweglichen Teilen und härteren Standardeinstellungen.
Ihre Antworten werden sich häufen. Zwei oder mehr VM-fokussierte Antworten bedeuten, dass Sie nicht standardmäßig Container pro Mandant verwenden sollten. Drei oder mehr container-fokussierte Antworten bedeuten, dass VMs verfrüht sind. Ein kontraintuitives Ergebnis: Ein Mandant mit geringem Umsatz und Zugang zu sensiblen Daten braucht trotzdem die VM, weil die regulatorischen Kosten nichts damit zu tun haben, wie viel er zahlt.
Setzen Sie das Minimum ein, dem Sie vertrauen können, und erarbeiten Sie sich dann mehr Isolation
Ihre erste Architektur muss nicht Ihre endgültige sein. Beginnen Sie mit dem engsten Setup, das Sie tatsächlich warten können, und fügen Sie dann Isolation hinzu, wenn Ihre Mandantenbasis es rechtfertigt. Für die meisten Solo-Betreiber bedeutet das Container pro Mandant mit Nicht-Root, begrenzten Capabilities, schreibgeschützten Dateisystemen, Seccomp und Netzwerksegmentierung. Für regulierte oder hochwertige Mandanten gehen Sie direkt zu einer VM pro Mandant, mit Containern nur als Verpackungsebene darin.
Was auch immer Sie wählen, schreiben Sie die Entscheidung auf und überprüfen Sie sie vierteljährlich. Wenn Sie Ihre erste Frage 'Sollten wir diesen Mandanten auf eine VM umstellen?' bekommen, haben Sie eine Antwort und die Checkliste, um sie zu untermauern. Das bedeutet Isolation tatsächlich: ein Kompromiss, den Sie verwalten, keine Technologie, die Sie kaufen.
Vor dem Start gehen Sie unsere praktische Docker-Isolations-Sicherheitscheckliste durch – sie verwandelt diese Entscheidungen in eine Liste, die Sie überprüfen können, bevor Sie einem Kunden eine Seite zeigen.

