Sandboxing von Containern - Ausführen von nicht-vertrauenswürdigem Code

Sandboxing von Containern

Ausführen von nicht-vertrauenswürdigem Code

Rocco Gagliardi
von Rocco Gagliardi
am 18. November 2021
Lesezeit: 12 Minuten

Keypoints

So funktioniert Sandboxing in Containern

  • Container sind keine virtuellen Maschinen
  • Wählen Sie die richtige Container-Laufzeit für die richtige Aufgabe
  • Verwenden Sie Container mit Sandbox, um nicht vertrauenswürdigen Code auszuführen
  • Berücksichtigen Sie sorgfältig die Auswirkungen von Sandbox-Laufzeiten auf die Leistung

Über Container zu sprechen, kann sehr kompliziert sein. Es gibt eine Menge Software, Schnittstellen, Standards und Spezifikationen, die alle sehr komplex sein können. Kubernetes (und Derivate wie OpenShift) wurden zum Standard für die Ausführung von containerisierten Anwendungen, aber das Ziel von Kubernetes ist es, die Container zu orchestrieren und nicht, sie auszuführen. Selbst Kubernetes oder OpenShift benötigen eine komplexe zugrunde liegende Technologieschicht. Die Bestandteile dieser Schicht können nach und nach geändert oder abgestimmt werden. In diesem Beitrag werden wir erörtern, wie Sie dem Host, auf dem nicht vertrauenswürdiger Code in einem Container ausgeführt wird, zusätzliche Schutzschichten hinzufügen können.

Der Begriff Container-Laufzeit kann sehr verwirrend sein. Im Allgemeinen bezieht sich der Begriff auf die Software, die für die Ausführung des Containers verantwortlich ist, aber einige Laufzeiten (sogenannte High-Level-Laufzeiten) sind eine Reihe von Tools für die Ausführung und Verwaltung von Containern. Ian Lewis erklärt den Begriff Container Runtimes im Detail.

In vielen Artikeln im Internet wird die Geschichte der Container-Runtimes erzählt und warum Kubernetes Docker als Standard-Runtime abgelöst hat. Docker begann als eine Reihe verschiedener einfacher Tools (gemäss der Unix-Philosophie), die jeweils eine ganz bestimmte Aufgabe hatten und im Allgemeinen auf die Kommunikation mit Peripheriegeräten ausgerichtet waren, z.B. den Umgang mit Netzwerken oder Speicher. Im Laufe der Zeit entwickelten Docker, CoreOS, Google und andere angesichts der verschiedenen Probleme, die dieser Ansatz bei gross angelegten Umsetzungen verursachte, die Open Container Initiative (OCI), die offene Industriestandards für Containerformate und Laufzeiten definiert. Die OCI enthält derzeit zwei Spezifikationen: Die Runtime Specification (runtime-spec), die definiert, wie ein Image ausgeführt wird, und die Image Specification (image-spec), die definiert, wie Informationen in ein lauffähiges Image gepackt werden (Manifest, Dateisystem, Konfiguration).

Docker hat das Containerformat und die Laufzeit runC entwickelt und der OCI zur Verfügung gestellt, um als Referenzimplementierung zu dienen. Um mit den verschiedenen Laufzeiten zu kommunizieren, wurde ein weiterer Standard entwickelt: Container Runtime Interface (CRI), der es den Administratoren ermöglicht, verschiedene Laufzeiten zu wählen, solange sie OCI-kompatibel sind.

Nachdem die Standards für die verschiedenen Ebenen definiert wurden, wurden für jede Ebene verschiedene Tools entwickelt, so dass wir für jeden Bedarf die beste Kombination wählen können.

Container-Security

Container wurden lange Zeit mit einer Art Sandkasten verwechselt, aber Container sind keine Sandkästen, sie schützen das Hostsystem nicht standardmässig, und es sind Konfiguration und Tuning erforderlich, um ihr Potenzial unter Kontrolle zu halten.

Der typische Konfigurationsprozess umfasst:

Beispielsweise kann es gefährlich sein, Zugriff auf alle Syscalls (348) zu gewähren, die der Kernel anbietet, wenn Sie keine vollständige Kontrolle über den Code haben, der innerhalb des Containers läuft. Und selbst dann wäre es besser, die Verwendung aller unnötigen Ressourcen zu verhindern.

Docker war in seinen Anfängen sehr grosszügig mit der Rechteverwaltung, gepaart mit der Verwirrung der Rollen (Docker kann alles: Images ausführen, Images, Volumes, Netzwerk und so weiter verwalten). Dies sind einige der Gründe, die zur Entwicklung anderer Laufzeiten geführt haben, die darauf ausgelegt sind, den Funktionsumfang zu minimieren und über klar definierte Rollen zu verfügen.

Native oder virtualisierte Laufzeitumgebung

Die OCI-Familie von Laufzeiten kann in zwei Gruppen unterteilt werden:

Die Verwendung virtualisierter Laufzeiten könnte die Lösung sein, um die Sicherheit der Virtualisierung (VMs) und die Geschwindigkeit des Containers zu nutzen, aber es gibt einige Nachteile: Im Gegensatz zu nativen Laufzeiten beeinträchtigen Laufzeiten in Sandkästen die Leistung während der gesamten Lebensdauer eines containerisierten Prozesses. In Container-Sandboxen gibt es eine zusätzliche Abstraktionsebene: Der Prozess läuft auf dem Unikernel, der Anweisungen an den Host-Kernel weitergibt. Der Zugriff auf das Dateisystem, der über einen Proxy erfolgt, oder auf Geräte wird ebenfalls kontrolliert und von der Leistung beeinflusst.

In der virtualisierten Familie gibt es eine zusätzliche Unterscheidung: VMs und Syscall-Filter.

Kata Container implementiert die VM-Virtualisierung. Die wichtigsten Funktionen lassen sich wie folgt zusammenfassen:

Solange die Leistung der sicherheitsorientierten Laufzeiten nicht an die der nativen Laufzeiten heranreicht, werden wir eine Mischung aus all diesen Lösungen sehen.

gVisor

Im Jahr 2018 hat Google gVisor veröffentlicht, einen User-Space-Kernel für Container. gVisor präsentiert sich den Anwendungen als normaler Kernel, stellt aber eine geringere Anzahl von Syscalls zur Verfügung, verwendet eine andere Sprache (Go) und reduziert so die Möglichkeit, dass in beiden Kerneln dieselben Fehler auftreten. gVisor filtert das Dateisystem und den Netzwerkzugriff über einen speziellen und separaten Mechanismus.

Die wichtigsten Merkmale lassen sich wie folgt zusammenfassen:

Wie bereits erwähnt, gibt es Nachteile bei der Verwendung von gVisor, vor allem hinsichtlich der Leistung. Für weitere Analysen lesen Sie bitte Die wahren Kosten von Containing: Eine gVisor-Fallstudie.

Anwendung

Die Installation ist ziemlich einfach. Nach der Installation kann eine neue Laufzeitumgebung zur Docker-Konfiguration hinzugefügt werden:

{
   “runtimes”: {
       “runsc”: {
            “path”: “/usr/local/bin/runsc”,
            “runtimeArgs”: [
                “—overlay”,
                “—network=sandbox”,
                “—debug-log=/tmp/runsc/”,
                “—debug”,
                “—strace”
           ]
       }
   }
}

Angabe eines zusätzlichen Schutzes:

Verwenden Sie runsc flags, um die Dokumentation der Laufzeitoptionen anzuzeigen.

Die Ausführung desselben Containers mit runc und runsc zeigt den Unterschied zwischen nativ (Weitergabe von Aufrufen an den Host-Kernel) und sandboxed (präsentiert sich als anderer Kernel):

[root@localhost cnt]# docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
alpine       latest    14119a10abf4   2 months ago   5.6MB

[root@localhost cnt]# docker run —rm —runtime=runc alpine uname -r 4.18.0-348.el8.x86_64

[root@localhost cnt]# docker run —rm —runtime=runsc alpine uname -r 4.4.0

runsc gibt sich gegenüber der Anwendung als Kernel 4.4.0 aus.

Gleichzeitig funktionieren einfache Angriffe wie Root-Mount nicht:

[roga@localhost cnt]$ docker run —rm -it —runtime=runc -v /:/pesc pesc /bin/bash
root@1770c2eb6e12:/pesc# exit
exit
[roga@localhost cnt]$ docker run —rm -it —runtime=runsc -v /:/pesc pesc /bin/bash
docker: Error response from daemon: OCI runtime create failed: reading spec: Mount.Destination must be an absolute path: &{] bind /var/lib/docker/volumes/f2a1cf46b1780a3becea9e75278f0f373948975784c3679f5f73d749147904f0/_data [rbind]}: unknown.

[roga@localhost runsc]$ cat runsc.log.20211028-080751.680195.create I1028 08:07:51.680381 4892 main.go:218] *************************** I1028 08:07:51.680410 4892 main.go:219] Args: [/usr/local/bin/runsc —overlay —network=sandbox —debug-log=/tmp/runsc/ —debug —strace —root /var/run/docker/runtime-runc/moby —log /run/containerd/io.containerd.runtime.v2.task/moby/a4b3f7175ca8ca397b08a676bf2df320579cda792babe3d50593f86c80149174/log.json —log-format json create —bundle /run/containerd/io.containerd.runtime.v2.task/moby/a4b3f7175ca8ca397b08a676bf2df320579cda792babe3d50593f86c80149174 —pid-file /run/containerd/io.containerd.runtime.v2.task/moby/a4b3f7175ca8ca397b08a676bf2df320579cda792babe3d50593f86c80149174/init.pid —console-socket /tmp/pty948542374/pty.sock a4b3f7175ca8ca397b08a676bf2df320579cda792babe3d50593f86c80149174] I1028 08:07:51.680438 4892 main.go:220] Version release-20211019.0 I1028 08:07:51.680444 4892 main.go:221] GOOS: linux I1028 08:07:51.680450 4892 main.go:222] GOARCH: amd64 I1028 08:07:51.680456 4892 main.go:223] PID: 4892 I1028 08:07:51.680463 4892 main.go:224] UID: 0, GID: 0 I1028 08:07:51.680469 4892 main.go:225] Configuration: I1028 08:07:51.680475 4892 main.go:226] RootDir: /var/run/docker/runtime-runc/moby I1028 08:07:51.680485 4892 main.go:227] Platform: ptrace I1028 08:07:51.680492 4892 main.go:228] FileAccess: exclusive, overlay: true I1028 08:07:51.680500 4892 main.go:229] Network: sandbox, logging: false I1028 08:07:51.680507 4892 main.go:230] Strace: true, max size: 1024, syscalls: I1028 08:07:51.680513 4892 main.go:231] VFS2 enabled: false, LISAFS: false I1028 08:07:51.680518 4892 main.go:232] *************************** W1028 08:07:51.681354 4892 specutils.go:112] noNewPrivileges ignored. PR_SET_NO_NEW_PRIVS is assumed to always be set. W1028 08:07:51.681388 4892 error.go:48] FATAL ERROR: reading spec: Mount.Destination must be an absolute path: &{[ bind /var/lib/docker/volumes/f51e3dcbea53b32e7682bc8a58e63a493decde9115488f6fc98a772972cd4b22/_data [rbind]} W1028 08:07:51.681447 4892 main.go:257] Failure to execute command, err: 1

Anwendungsfälle

Im Allgemeinen ist der perfekte Anwendungsfall für gVisor, wenn ein Container nicht vertrauenswürdigen Code ausführen muss. In diesem Fall kann gVisor mit einem einfachen Schalter in der Docker- oder Kubernetes-Konfiguration zusätzliche Sicherheitsebenen hinzufügen.

Für eine typische Kundenumgebung, in der kein nicht vertrauenswürdiger Code gehostet werden muss, könnte eine Mischung aus gVisor für offene Anwendungen und Standard-Laufzeiten für Anwendungen mit überarbeitetem oder vertrauenswürdigem Code in Frage kommen. Im Falle eines Kubernetes-Clusters ist es möglich, eine Untergruppe von Knoten für die Ausführung von sensiblem Code zu erstellen und gVisor auf diesen Knoten zu installieren. Der Rest der Knoten kann Container mit standardmässigen, leistungsfähigeren Laufzeiten ausführen.

Zusammenfassung

Container sind sehr beliebt, da sie leistungsstark und flexibel sind, aber die komplexe Technologie, die die Umgebung steuert, stellt viele Herausforderungen an die Sicherheit. Wenn Sie nicht vertrauenswürdigen Code auf Ihren Containern ausführen müssen, wäre es sinnvoll, eine zweite Schutzschicht zu implementieren. In diesem Fall ist gVisor eine gute Wahl für den Anfang.

Es erfordert zwar eine ausführliche Prüfung der Kompatibilität mit der Anwendung und eine sorgfältige Abwägung der Auswirkungen auf die Leistung, aber durch die Verwendung einer anderen Sprache (go) und die strenge Kontrolle der Syscalls bietet gVisor eine solide zweite Verteidigungslinie für eine Umgebung, die dem Code nicht vertraut.

In Anbetracht der sicheren containerisierten Umgebungen mit aktualisierter Bedrohungsmatrix für Kubernetes kann gVisor dazu beitragen, einige Techniken zu entschärfen, die in den Taktiken der Ausführung, Persistenz und Rechteerweiterung verwendet werden. Wir werden solche Implementierungen in Zukunft noch häufiger sehen.

Über den Autor

Rocco Gagliardi

Rocco Gagliardi ist seit den 1980er Jahren im Bereich der Informationstechnologie tätig. In den 1990er Jahren hat er sich ganz der Informationssicherheit verschrieben. Die Schwerpunkte seiner Arbeit liegen im Bereich Security Frameworks, Routing, Firewalling und Log Management.

Links

Sie wollen die Sicherheit Ihrer Firewall prüfen?

Unsere Spezialisten kontaktieren Sie gern!

×
Übergang zu OpenSearch

Übergang zu OpenSearch

Rocco Gagliardi

Graylog v5

Graylog v5

Rocco Gagliardi

auditd

auditd

Rocco Gagliardi

Security Frameworks

Security Frameworks

Rocco Gagliardi

Sie wollen mehr?

Weitere Artikel im Archiv

Sie brauchen Unterstützung bei einem solchen Projekt?

Unsere Spezialisten kontaktieren Sie gern!

Sie wollen mehr?

Weitere Artikel im Archiv