Kommandozeilentools für Container – Snyk mit Buildah, Podman und Skopeo
9. Dezember 2020
0 Min. LesezeitDas Container-Ökosystem ist gereift. Eines gibt es dabei mehr als genug: Auswahlmöglichkeiten – sowohl bei der eingesetzten Software als auch bei der Art, wie wir sie miteinander verbinden.
Eine dieser Möglichkeiten ist die Kombination aus Buildah, Podman und Skopeo – drei Open-Source-Kommandozeilentools, die ihren Ursprung im RedHat-Ökosystem haben. Wie der Name schon sagt, bietet Buildah zahlreiche Funktionen zum Erstellen von OCI-konformen Containern und Images. Podman dient der Verwaltung und Ausführung von OCI-Containern, während Skopeo ein Tool für die Arbeit mit Container-Registries ist.
In diesem Blogbeitrag sehen wir uns an, wie offene Standards dafür sorgen, dass Snyks Container-Scanning mit all diesen Technologien funktioniert.
Sehen wir uns zunächst Skopeo an. Mit diesem Kommandozeilentool lassen sich Container-Images zwischen Repositories verschieben und lokal speichern. Skopeo kann Images sowohl als Docker-Archive als auch im OCI-Archivformat ausgeben. Beide Formate unterstützt Snyk beim Scannen des Dateisystems. Laden wir damit ein Image von Quay.io herunter und scannen es lokal aus unserem Dateisystem :
Wie Sie sehen, verwendet dieses Image eine nicht mehr unterstützte Ubuntu-Version und enthält daher mehrere Sicherheitslücken, die nicht behoben werden. Mit Skopeo können wir also Images aus jeder standardkonformen Container-Registry in einem von Snyk unterstützten Format herunterladen und lokal scannen.
Buildah
Als Nächstes sehen wir uns Buildah an. Buildah ist ein Kommandozeilentool zum Erstellen von Images, nicht zu deren Ausführung. Es ist daher für den Einsatz mit Podman vorgesehen. Außerdem ist es für den Rootless-Betrieb ausgelegt: Mithilfe von User-Namespaces im Linux-Kernel wird eine rootähnliche Shell isoliert, die nur über Berechtigungen auf Benutzerebene verfügt. So können Administratoren Entwicklerinnen und Entwicklern das Erstellen und Verwalten von Container-Images ermöglichen und gleichzeitig im Build-Umfeld das Prinzip der geringsten Berechtigungen einhalten.
Buildah kann zwar ähnlich wie Docker selbst mit Dockerfiles arbeiten, ermöglicht aber auch die Entwicklung von Containern und Images mit anderen Workflows, zum Beispiel mit Bash oder Python. Das bietet mehr Flexibilität und kann das Debugging von Container-Builds vereinfachen. Buildah ermöglicht etwa das Einbinden des Zwischencontainers während des Build-Prozesses. So können Skripte vor dem Fortfahren die korrekte Ausführung überprüfen, Software extern erstellen oder sogar Benutzereingaben entgegennehmen. Damit gibt es eine Alternative zu den mitunter komplexen mehrstufigen Build-Prozessen, die sich bei Docker als Best Practice etabliert haben.
Sehen wir uns an, wie Buildah in der Praxis eingesetzt wird.
Zunächst ist wichtig zu wissen, dass wir unsere Container einfach mit Dockerfiles erstellen können, wenn dieser Workflow für Sie am besten passt. Hier ein Beispiel für ein Dockerfile :
Hier verwenden wir das CentOS-8-Image als Basis, klonen den GNU-Hello-Quellcode, installieren einige Voraussetzungen und erstellen und installieren anschließend die Binärdatei.
Mit Buildah können wir das mithilfe der Option bud ( build-using-dockerfile) automatisch erstellen :
Das ist eindeutig keine gute Vorgehensweise: Unser finales Image enthält sowohl den Quellcode als auch die Build-Voraussetzungen und verstößt gegen die meisten Best Practices für die sichere Image-Entwicklung. Mit Docker wäre ein mehrstufiger Build hier die Best Practice. Wir könnten Buildah mit einem mehrstufigen Dockerfile verwenden. Noch interessanter ist jedoch, dass wir auch native Buildah-Befehle und entsprechende Skripte nutzen können. Um genau das nachzubilden, was wir oben mit dem Dockerfile gemacht haben, würden wir folgendes Skript ausführen :
Buildah ermöglicht es uns außerdem, während des Build-Prozesses den Zwischencontainer einzubinden. So können wir den Quellcode auf dem Host erstellen und einfach die resultierende Binärdatei in unseren Container kopieren, bevor wir daraus ein Image erstellen. Auf diese Weise können wir während der Builds die Ressourcen des Hosts nutzen und beispielsweise auch den Paketmanager des Hosts verwenden, um Pakete in unserem Container zu installieren. Dafür benötigen wir keine Root-Berechtigungen: Buildah unterstützt User-Namespaces, mit denen Sie das Einbinden ohne Berechtigungen ermöglichen können. Dazu dient der Befehl unshare :
Wie Sie im obigen Beispiel sehen, erhalten wir nach Ausführung des unshare-Befehls eine Root-Shell. Diese befindet sich jedoch in einem User-Namespace und verfügt nicht über vollständige Root-Berechtigungen. So können wir das Container-Dateisystem während des Build-Prozesses einbinden. Da es sich um ein Bash-Skript handelt, können wir außerdem die Funktionen einer Programmiersprache nutzen – etwa Bedingungen und Schleifen oder sogar Benutzereingaben während des Prozesses abfragen.
Podman
Sobald wir dieses Skript ausgeführt und unser Image erstellt haben, können wir es auch mit Snyk scannen. Hier kommt Podman ins Spiel. Podman ist eine daemonlose Container-Engine für die Entwicklung, Verwaltung und Ausführung von OCI-Containern. Da Buildah und Podman auf offenen Standards basieren, konnte Snyk schon immer Images scannen, die mit Podman erstellt oder heruntergeladen wurden: Dazu speichert Podman das Image auf dem Datenträger, wo Snyk es aus dem Dateisystem scannt. Podman kann Images sowohl als Standard-Docker-Archiv als auch im OCI-Archivformat speichern. Beide Formate unterstützt Snyk.
Da unser Basis-Image ein öffentliches Image auf Dockerhub ist, könnten wir es mit der Snyk CLI auch einfach so scannen :
In diesem Fall greift Snyk direkt auf Dockerhub zu und scannt einfach das Upstream-Image.
Wenn das Image jedoch nur lokal in Podman vorhanden ist oder sich in einem privaten Repository auf Dockerhub befindet, für das eine Anmeldung erforderlich ist, funktioniert keine dieser Methoden. Versuchen wir beispielsweise, unser erstelltes Image direkt aus Podman zu scannen:
Für solche Anwendungsfälle verwendet Snyk den lokalen Docker-Client – entweder über die Registry-API oder über den Docker-Socket. Da sich auf dem System keine Docker-Binärdatei befindet, hat Snyk hier versucht, die Registry-API zu verwenden, konnte aber keine Verbindung herstellen. Standardmäßig weiß Snyk nicht, dass diese Images lokal in Podman vorhanden sind oder wie Podman für den Zugriff auf Upstream verwendet werden kann.
Zum Glück bietet Podman mit dem Paket podman-docker einige einfache Kompatibilitätsfunktionen. Es stellt eine gefälschte Docker-Binärdatei bereit, die im Grunde ein Shell-Wrapper für die Podman-Binärdatei ist, und erstellt einen symbolischen Link zwischen dem Podman-API-Socket und dem Standardspeicherort der Docker-Socket-Datei. Standardmäßig verweist dieser Link auf /var/run/podman/podman.sock, was dem Standard entspricht, wenn die Podman-API als Root ausgeführt wird.
Das funktioniert immer noch nicht, aber diesmal erhalten wir eine andere Fehlermeldung. Snyk hat eine Binärdatei namens docker erkannt und versucht nun, eine Verbindung zum Standardspeicherort des privilegierten Docker-Sockets herzustellen. Da die Podman-API noch nicht ausgeführt wird, kann keine Verbindung hergestellt werden und der Vorgang schlägt erneut fehl.
Wie bereits erwähnt, erstellt das Paket podman-docker einen symbolischen Link von /var/run/podman/podman.sock zu /var/run/docker.sock – unter der Annahme, dass die Podman-API mit Root-Berechtigungen ausgeführt wird.
Ein wesentliches Problem mit dem Docker-Socket besteht darin, dass er standardmäßig mit Root-Berechtigungen läuft. Das gilt in bestimmten Umgebungen als Sicherheitsrisiko. In den meisten lokalen Entwicklungsumgebungen müssen wir die Podman-API nicht auf diese Weise ausführen und können stattdessen an anderer Stelle einen Socket ohne erhöhte Berechtigungen verwenden. Den Speicherort können wir Snyk über die Umgebungsvariable DOCKER_HOST mitteilen. Sie unterstützt die URL-Formate tcp:// und unix://.
Führen wir die Podman-API in einer anderen Shell mit einem Socket ohne erhöhte Berechtigungen aus:
Der Prozess läuft im Vordergrund. Sie müssen ihn daher nach dem Test mit Strg+C beenden. Nun müssen wir die Umgebungsvariable DOCKER_HOST festlegen:
Versuchen wir abschließend, unser lokales Image erneut mit Snyk zu scannen :
Geschafft! Snyk funktioniert nun korrekt und kommuniziert über den Socket ohne erhöhte Berechtigungen mit Podman.
Wenn Sie DOCKER_HOST dauerhaft in Ihrer Shell konfigurieren möchten, können Sie den Export-Befehl in Ihre Datei .bashrc aufnehmen. So bleibt die Einstellung erhalten.
Abschließend möchten wir diese Konfiguration bei jeder Anmeldung automatisch verfügbar haben. Dazu können wir die Socket-Aktivierungsfunktionen von systemd nutzen, um unseren Socket bei der Anmeldung automatisch zu starten, sobald eine Verbindung hergestellt werden soll.
Zunächst müssen wir den Socket konfigurieren :
Anschließend verknüpfen wir den Socket mit einem Dienst :
Zum Schluss laden wir die systemd-Konfiguration neu und aktivieren den Dienst!
Fazit
Damit funktioniert das Image-Scanning mit der Snyk CLI und Podman genauso wie mit Docker. Entwicklerinnen und Entwickler können so im Rahmen ihres Workflows unkompliziert umfassende Sicherheitsscans lokaler Docker- oder OCI-Images durchführen – ohne erhöhte Berechtigungen. Außerdem haben wir gesehen, wie sich Skopeo und Buildah dank offener Standards mit Snyk einsetzen lassen!
Sie haben noch kein Snyk-Konto? Registrieren Sie sich kostenlos und scannen Sie mit Snyk sowohl Container-Images als auch Open-Source-Abhängigkeiten.
Starten Sie mit Capture the Flag
Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.