Security Delight - Technologie zum Verschlüsseln von Container-Images

Neulich kam eine interessante Aufgabe auf - Sie müssen einen Weg finden, um die Originaldaten des Containers zu schützen (lesen Sie: Sie können nicht lesen, was sich darin befindet), wenn er gestoppt wird. Die Idee der Verschlüsselung kam mir sofort in den Sinn und meine Finger begannen, die geschätzten Wörter in Google einzugeben. Während ich das Thema verstand, stieß ich auf einen ziemlich interessanten Artikel, den ich Ihnen gerne zur Verfügung stelle.







Vertraulichkeit von Daten und Code in Containerbildern



In den letzten Jahren hat sich in der Cloud-Branche eine große Verschiebung von der Bereitstellung monolithischer Anwendungen auf virtuellen Maschinen hin zur Aufteilung von Anwendungen in kleinere Komponenten (Microservices) und deren Verpackung in Container vollzogen. Die Popularität der Containerisierung ist heute weitgehend auf die Arbeitsweise von Docker zurückzuführen. Docker ist das Unternehmen, das zur Hauptantriebskraft für Container geworden ist: Es bietet ein benutzerfreundliches Tool zum Erstellen und Ausführen von Docker-Containern und eine Docker-Containerregistrierung für die Distributionsherausforderung.



Der Erfolg der Containerisierungstechnologie hängt hauptsächlich von der Sicherheit der Container in verschiedenen Phasen ihres Lebenszyklus ab. Eines der Sicherheitsbedenken ist das Vorhandensein von Sicherheitslücken in einzelnen Containern. Um sie zu identifizieren, werden die DevOps-Pipelines, die zum Erstellen von Containern verwendet werden, durch Scanner ergänzt, die nach Paketen mit möglichen Schwachstellen in Containern suchen und deren Eigentümer oder Techniker benachrichtigen, wenn sie gefunden werden. Der Vulnerability Advisor in der IBM Cloud ist ein Beispiel für ein solches Dienstprogramm.



Ein weiterer Aspekt der Sicherheit besteht darin, sicherzustellen, dass der zu startende Container der gewünschte ist und nicht geändert wurde. Dieses Problem wird durch die Verwendung digitaler Signaturen behoben, die im Notar gespeichert sind und die Container vor Änderungen schützen.Docker Notary ist ein Beispiel für ein öffentliches Repository, in dem Bildsignaturen gespeichert werden. Mit Notar kann ein Kunde die Signatur des Container-Images überprüfen, um sicherzustellen, dass das Container-Image nicht geändert wurde, seit es mit dem Schlüssel des Eigentümers oder Servicetechnikers signiert wurde.



Ein weiteres potenzielles Sicherheitsproblem ist die Containerisolation. Linux-Laufzeitsicherheitstechnologien wie Namespaces, cgroups, Linux-Funktionen sowie SELinux-, AppArmor- und Seccomp-Profile helfen dabei, Containerprozesse zu begrenzen und Container zur Laufzeit voneinander zu isolieren.



Dieser Artikel befasst sich mit dem noch immer aktuellen Problem der Unternehmenssicherheit in Bezug auf den Datenschutz von Daten und Code in Containerbildern. Das wichtigste Sicherheitsziel bei der Arbeit mit Container-Images besteht darin, die Erstellung und Verteilung verschlüsselter Container-Images so zu ermöglichen, dass sie nur einer bestimmten Gruppe von Empfängern zur Verfügung stehen. In diesem Fall haben andere möglicherweise Zugriff auf diese Bilder, können sie jedoch nicht ausführen oder die darin enthaltenen vertraulichen Daten nicht anzeigen. Die Containerverschlüsselung basiert auf vorhandenen Kryptografien wie Rivest-Shamir-Adleman (RSA) -Verschlüsselungstechnologien, elliptischen Kurven und Advanced Encryption Standard (AES), auch bekannt als Rijndael, einem symmetrischen Blockverschlüsselungsalgorithmus.



Einleitend



Um diesen Artikel optimal nutzen zu können, sollten Sie mit Linux-Containern und Container-Images vertraut sein und die Grundlagen der Sicherheit kennen.



Verwandte Arbeiten zu Verschlüsselung und Containern



Soweit wir wissen, gibt es keine Arbeit im Bereich der Verschlüsselung von Containerbildern. Es gibt jedoch viele Implementierungen und Produkte, die Datenschutz und Diebstahlschutz durch Dateisystem-, Blockgeräte- oder Hardwareverschlüsselung unterstützen. Letzteres wird mit selbstverschlüsselnden Festplatten implementiert. Es gibt auch verschlüsselte Images von virtuellen Maschinen.



Verschlüsselte Dateisysteme existieren auf vielen Betriebssystemen in Unternehmen und können das Mounten verschlüsselter Partitionen und Verzeichnisse unterstützen. Verschlüsselte Dateisysteme können sogar das Booten von einem verschlüsselten Startlaufwerk unterstützen. Linux unterstützt die Blockgeräteverschlüsselung mit dem dm-encrypt-Treiber. ecryptfs ist ein Beispiel für ein verschlüsseltes Dateisystem. Andere für Linux verfügbare DateiverschlüsselungslösungenOpen Source. Unter Windows wird die Verschlüsselung vom NTFS v3.0-Dateisystem unterstützt. Darüber hinaus erstellen viele Hersteller selbstverschlüsselnde Discs. Für Images von virtuellen Maschinen gibt es eine ähnliche Lösung wie für verschlüsselte Festplatten. Die Open Source-Emulatorprodukte QEMU Machine (PC) und VMware Virtualization unterstützen verschlüsselte Images von virtuellen Maschinen.



Die Datenverschlüsselung zielt normalerweise darauf ab, sich vor Datendiebstahl zu schützen, während das System offline ist. Eine verwandte Technologie signiert das Container-Image mit einem Schlüssel, der vom Client und vom Docker Notary-Server bereitgestellt wird. Der Docker Notary Server arbeitet in unmittelbarer Nähe zur Container-Image-Registrierung. Benutzer des Docker-Client-Tools haben die Möglichkeit, das Container-Image zu signieren und die Signatur über den Docker-Notar auf ihre Konten hochzuladen. Während dieses Vorgangs wird die Signatur über den Pfadnamen an das Bild und seine Versionen an das Container-Image gebunden. Die Signatur wird mit einer Hash-Funktion erstellt, die anhand der Beschreibung des gesamten Bildinhalts berechnet wird. Diese Beschreibung wird als Container-Image-Manifest bezeichnet.Die Technologie zum Signieren von Containerbildern löst das Problem des Schutzes von Containerbildern vor unbefugtem Zugriff und hilft bei der Bestimmung des Ursprungs des Containerbilds.



Struktur



Das Docker-Ökosystem wurde entwickelt, um Containerbildformate mithilfe der Open Container Initiative (OCI) -Standardgruppe zu standardisieren, die nun das Containerlaufzeitformat (Laufzeitspezifikation) und das Containerbildformat (Bildspezifikation) steuert. Da für die Arbeit des Teams eine Erweiterung des vorhandenen Container-Image-Formats erforderlich war, haben wir eine Erweiterung des Standards identifiziert, um verschlüsselte Images zu unterstützen. In den folgenden Abschnitten werden das vorhandene Container-Image und das Erweiterungsformat beschrieben.



Auf der obersten Ebene kann ein Container aus einem JSON-Dokument (JavaScript Object Notation) bestehen, bei dem es sich um eine Liste von Bildmanifesten handelt. Sie können diese Liste von Manifesten beispielsweise verwenden, wenn mehrere Architekturen oder Plattformen für das Container-Image verwendet werden. Die Manifestliste enthält Links zu Container-Manifesten, einen für jede Kombination aus Architektur und Betriebssystem. Zu den unterstützten Architekturen gehören beispielsweise amd64, arm und ppc64le, und zu den unterstützten Betriebssystemen gehören Linux oder Windows. Ein Beispiel für eine Liste von Manifesten ist im folgenden Screenshot dargestellt:







Das Feld mediaType beschreibt das genaue Format des angegebenen Dokuments. Diese Liste von Manifesten ermöglicht die zukünftige Erweiterung und Auswahl des geeigneten Parsers für das betreffende Dokument.



Die Ebene unter der Liste der Manifeste ist das Manifest. Das Manifest ist auch ein JSON-Dokument und enthält eine geordnete Liste von Verweisen auf Bildebenen. Diese Links enthalten mediaType, das das Format der Ebene beschreibt. Das Format kann beschreiben, ob und wie die Ebene komprimiert wird. Beispielsweise kann jede Ebene als .tar-Datei gespeichert werden, die Dateien enthält, die zu einem bestimmten Zeitpunkt im Build hinzugefügt wurden, als Docker-Build in einer Docker-Datei ausgeführt wurde. Ebenen werden häufig mit komprimierten .gzip-Dateien gepackt, um die Speichereffizienz zu verbessern. Ein Beispiel für ein Manifestdokument ist im folgenden Screenshot dargestellt:







Wie gezeigt, werden Manifeste und Ebenen durch einen "Digest" referenziert, bei dem es sich normalerweise um eine sha256-Hash-Funktion in JSON-Dokumenten handelt. Manifeste und Ebenen werden normalerweise als Dateien im Dateisystem gespeichert. Dateinamen sind häufig Hash-Funktionen über dem Inhalt, die das Auffinden und Laden erleichtern. Die Konsequenz dieser Hash-Methode ist, dass eine kleine Änderung des referenzierten Dokuments Änderungen in allen Dokumenten verursacht, die darauf verweisen, bis hin zur Liste der Manifeste.



Im Rahmen des Projekts unseres Teams haben wir eine Bildverschlüsselung basierend auf einem hybriden Verschlüsselungsschema unter Verwendung öffentlicher und symmetrischer Schlüssel erstellt. Symmetrische Schlüssel werden für die Verschlüsselung von Massendaten verwendet (für die Verschlüsselung auf mehreren Ebenen), und öffentliche Schlüssel werden zum Packen symmetrischer Schlüssel verwendet. Wir haben drei verschiedene Verschlüsselungstechnologien für öffentliche Schlüssel verwendet: OpenPGP, JSON Web Encryption (JWE) und PKCS # 7.



OpenPGP



OpenPGP ist eine Verschlüsselungs- und Signaturtechnologie, die üblicherweise zum Verschlüsseln und Signieren von E-Mail-Nachrichten verwendet wird. Open Source-Communities verwenden es häufig auch, um Commits (Tags) des Quellcodes in Git-Repositorys zu signieren. Es ist ein Internetstandard, der von der IETF in RFC480 definiert wurde und als offene Version der vorherigen proprietären PGP-Technologie angesehen werden kann.



OpenPGP hat ein eigenes Format für RSA-Schlüssel. Schlüssel werden normalerweise in einer Schlüsselbunddatei gespeichert und können aus einfachen OpenPGP-Schlüsseldateien importiert werden. Der bequemste Aspekt des OpenPGP-Schlüsselbunds ist, dass öffentliche Schlüssel mit den E-Mail-Adressen ihrer Eigentümer verknüpft werden können. Sie können mit mehreren Empfängern einer Nachricht arbeiten, indem Sie einfach eine Liste von Empfängern anhand ihrer E-Mail-Adressen auswählen, die dann in den öffentlichen Schlüsseln für diese Empfänger angezeigt werden. Darüber hinaus wurde ein Vertrauensnetz um diese Technologie aufgebaut: Sie können die öffentlichen Schlüssel vieler Benutzer finden, sortiert nach ihren E-Mail-Adressen. Beispielsweise werden diese Schlüssel häufig zum Signieren von Git-Tags verwendet.



Sie können das OpenPGP-Format für verschlüsselte Nachrichten verwenden, um eine Massennachricht an mehrere Empfänger zu verschlüsseln. Der OpenPGP-Nachrichtenkopf enthält einen Block für jeden Empfänger. Jeder Block enthält eine 64-Bit-Schlüsselkennung, die dem Entschlüsselungsalgorithmus mitteilt, wo versucht werden soll, den entsprechenden privaten Schlüssel zu entschlüsseln. Nachdem der verschlüsselte Blob im Block entschlüsselt wurde, wird der symmetrische Schlüssel angezeigt, mit dem die Massennachricht entschlüsselt werden kann. Der verschlüsselte öffentliche Schlüsselblob jedes Empfängers weist denselben symmetrischen Schlüssel auf.



Wir haben OpenPGP auf ähnliche Weise verwendet, aber in diesem Fall ist die verschlüsselte Nachricht, die es sendet, keine Schicht. Stattdessen enthält es ein JSON-Dokument, das wiederum einen symmetrischen Schlüssel enthält, mit dem sowohl die Ebene als auch der Initialisierungsvektor verschlüsselt und entschlüsselt werden. Wir nennen diesen Schlüssel den Layer-Verschlüsselungsschlüssel (LEK) und er ist eine Form des Datenverschlüsselungsschlüssels. Der Vorteil dieser Methode ist, dass wir nur einen LEK benötigen. Mit LEK verschlüsseln wir die Ebene für einen oder mehrere Empfänger. Jeder Empfänger (Container-Image) kann einen anderen Schlüsseltyp haben und muss kein OpenPGP-Schlüssel sein. Beispielsweise könnte es sich um einen einfachen RSA-Schlüssel handeln. Solange wir diesen RSA-Schlüssel zum Verschlüsseln des LEK verwenden können, können wir mit mehreren Empfängern mit unterschiedlichen Schlüsseltypen arbeiten.



JSON-Webverschlüsselung (JWE)



JSON Web Encryption, auch bekannt als JWE, ist ein weiterer IETF-Internetstandard und in RFC7516 definiert . Es ist ein neuerer Verschlüsselungsstandard als OpenPGP und verwendet daher neuere Low-Level-Chiffren, die strengere Verschlüsselungsanforderungen erfüllen.



In größerem Maßstab funktioniert JWE ähnlich wie OpenPGP, da es auch eine Empfängerliste und Massenversand einer Nachricht verwaltet, die mit einem symmetrischen Schlüssel verschlüsselt ist, auf den jeder Empfänger der Nachricht Zugriff hat. Empfänger einer JWE-Nachricht können verschiedene Schlüsseltypen haben, z. B. RSA-Schlüssel, bestimmte Schlüsseltypen für elliptische Kurven zur Verschlüsselung und symmetrische Schlüssel. Da dies ein neuerer Standard ist, ist es weiterhin möglich, das JWE zu erweitern, um Schlüssel in Hardwaregeräten wie TPMs oder Hardware-Sicherheitsmodulen (HSMs) mithilfe von PKCS # 11 oder KMIP-Schnittstellen (Key Management and Interoperability Protocol) zu unterstützen. JWEs werden ähnlich wie OpenPGP verwendet, wenn die Empfänger RSA-Schlüssel oder elliptische Kurven haben.In Zukunft könnten wir es erweitern, um symmetrische Schlüssel wie KMIP in HSM zu unterstützen.



PKCS # 7



PKCS # 7, auch als Cryptographic Message Syntax (CMS) bekannt, ist in IEFT RFC5652 definiert . Laut Wikipedia über das CMS "kann es verwendet werden, um jede Form digitaler Daten digital zu signieren, zu verarbeiten, zu authentifizieren oder zu verschlüsseln."



Es ähnelt den beiden zuvor beschriebenen Technologien darin, dass es mehrere Empfänger und die Verschlüsselung von Massennachrichten ermöglicht. Daher haben wir es wie andere Technologien verwendet, jedoch nur für Empfänger, die Zertifikate für Verschlüsselungsschlüssel bereitstellen.



Um die zuvor beschriebenen Verschlüsselungstechnologien zu unterstützen, haben wir das Manifestdokument um die folgenden Informationen erweitert:



  • OpenPGP-, JWE- und PKCS # 7-Nachrichten werden in einer Anmerkungszuordnung gespeichert, die Teil des Manifests ist.
  • Jeder angegebene Layer enthält eine Karte. Eine Annotation Map ist im Grunde ein Wörterbuch mit Zeichenfolgen als Schlüssel und Zeichenfolgen als Werte (Schlüssel-Wert-Paare).


Zur Unterstützung der Bildverschlüsselung haben wir die folgenden Schlüssel definiert:



  • org.opencontainers.image.enc.keys.openpgp
  • org.opencontainers.image.enc.keys.jwe
  • org.opencontainers.image.enc.keys.pkcs7


Der Wert, auf den jeder Schlüssel verweist, enthält eine oder mehrere verschlüsselte Nachrichten für die entsprechende Verschlüsselungstechnologie. Da diese Nachrichten im Binärformat vorliegen können, sind sie base64-codiert. Eine verschlüsselte Schicht muss mindestens eine solche Anmerkung haben, kann aber alle haben, wenn der Empfänger über eine ausreichende Anzahl verschiedener Schlüsseltypen verfügt.



Um festzustellen, dass die Ebene mit LEK verschlüsselt wurde, haben wir die vorhandenen Medientypen mit dem Suffix '+ verschlüsselt' erweitert, wie in den folgenden Beispielen gezeigt:



  • application / vnd.docker.image.rootfs.diff.tar + verschlüsselt
  • application / vnd.docker.image.rootfs.diff.tar.gzip + verschlüsselt


Diese Beispiele zeigen, dass die Ebene in einer .tar-Datei komprimiert und verschlüsselt wird - oder beide in einer .tar-Datei komprimiert und in eine .gzip-Datei komprimiert und verschlüsselt werden. Der folgende Screenshot zeigt ein Beispiel für ein Manifest, das mit verschlüsselten Ebenen verknüpft ist. Es wird auch eine Anmerkungskarte angezeigt, die die verschlüsselte JWE-Nachricht enthält.







Schichtverschlüsselung mit symmetrischen Schlüsseln



Für die symmetrische Verschlüsselung mit LEK hat unser Team eine Verschlüsselung ausgewählt, die authentifizierte Verschlüsselung unterstützt und auf dem AES-Verschlüsselungsstandard mit 128- und 256-Bit-Schlüsseln basiert.



Beispielimplementierung: Containerd



Wir haben unsere Variation in einem neuen Container-Laufzeitprojekt namens Containerd implementiert . Der Quellcode von Golang kann über den Link eingesehen werden . Der Docker-Daemon verwendet Containerd, um einige seiner Dienste auszuführen, und Kubernetes verfügt über ein Plugin, mit dem Containerd direkt verwendet werden kann. Wir hoffen daher, dass unsere Erweiterungen zur Unterstützung verschlüsselter Container-Images für beide nützlich sind.



Die Implementierung der mehrstufigen Verschlüsselung mit LEK erfolgt auf der niedrigsten architektonischen Ebene der Erweiterungen. Eine der Implementierungsanforderungen bestand darin, volumetrische Schichten von mehreren Gigabyte aufzunehmen, während die von dem Prozess, der die kryptografische Operation auf der Schicht ausführt, belegte Speichermenge nur wenige Megabyte groß blieb.



Die Unterstützung für authentifizierte Verschlüsselungsalgorithmen in Golang verwendet ein Byte-Array als Eingabe und führt die gesamte Phase der Verschlüsselung (Versiegelung) oder Entschlüsselung (Öffnung) durch, wodurch die Übertragung und das Hinzufügen zusätzlicher Arrays zum Stream verhindert wird. Da diese Krypto-API das Laden der gesamten Schicht in den Speicher oder das Erfinden eines Schemas zum Ändern des Initialisierungsvektors (IV) für jeden Block erforderte, entschieden wir uns, die authentifizierte Verschlüsselung von golang mit Unterstützung für verknüpfte Daten (AEAD) nicht zu verwenden. Stattdessen haben wir die missbräuchliche Golang-Kryptobibliothek verwendet , die AEAD in Streams unterstützt(Blöcke) und implementiert ein eigenes Schema zum Ändern von IV in jedem Block. In unserer Implementierung haben wir die Schicht in 1-MB-Blöcke aufgeteilt, die wir einzeln zur Verschlüsselung übertragen. Mit diesem Ansatz können Sie die Speichermenge reduzieren, wenn Sie eine authentifizierte Verschlüsselung verwenden. Auf der Entschlüsselungsseite machen wir das Gegenteil und achten auf die von der Open () - Funktion zurückgegebenen Fehler, um sicherzustellen, dass die Verschlüsselungsblöcke nicht manipuliert wurden.



Oberhalb der symmetrischen Verschlüsselung verschlüsseln asymmetrische kryptografische Schemata die LEK-Ebene und den Initialisierungsvektor (IV). Um kryptografische Schemata hinzuzufügen oder zu entfernen, registrieren wir jede asymmetrische kryptografische Implementierung. Wenn die API für asymmetrischen kryptografischen Code aufgerufen wird, um die Schicht zu verschlüsseln, rufen wir die registrierten kryptografischen Handler nacheinander auf und übergeben die öffentlichen Schlüssel der Empfänger. Nachdem alle Empfängerschlüssel für die Verschlüsselung verwendet wurden, kehren wir zur Anmerkungszuordnung mit den asymmetrischen Kryptoalgorithmus-IDs als Zuordnungsschlüssel und den Werten zurück, die die in OpenPGP, JWE und PKCS # 7 codierten Nachrichten enthalten. Jede Nachricht enthält gepackte LEK und IV. Anmerkungskarten werden im Manifestdokument gespeichert, wie im vorherigen Screenshot gezeigt.



Wir können einem bereits verschlüsselten Bild auch Empfänger hinzufügen. Bildautoren fügen Empfänger hinzu, wenn sie in der Liste enthalten sind. Der private Schlüssel wird für die Empfängerliste verwendet, die zum Entpacken der LEK- und IV-Ebenen erforderlich ist. Anschließend packen wir LEK und IV mit dem neuen Empfängerschlüssel in eine neue Nachricht und fügen diese Nachricht der Annotation Map hinzu.



Wir haben drei Arten von asymmetrischen Verschlüsselungsschemata für verschiedene Arten von Schlüsseln verwendet. Wir verwenden OpenPGP-Schlüssel, um OpenPGP-Nachrichten zu verschlüsseln. Für das von uns verwendete PKCS # 7 sind x.509-Zertifikate für Verschlüsselungsschlüssel erforderlich. JWE verarbeitet alle anderen Schlüsseltypen wie einfache RSA-Schlüssel, elliptische Kurven und symmetrische Schlüssel. Wir haben eine Erweiterung für JWE entwickelt, die kryptografische Operationen mit Schlüsseln ermöglicht, die vom KMIP-Server verwaltet werden.



Die Containerd-Laufzeit enthält ein CTR-Client-Tool, mit dem Sie interagieren können. Wir haben ctr erweitert, um das Testen unserer Änderungen zu ermöglichen und Containernutzern Zugriff zu gewähren. ctr implementiert bereits einen Unterbefehl, der Bildoperationen unterstützt, z. B. die Interaktion mit der Bildregistrierung durch Abrufen und Senden von Bildern.



Wir haben diesen Unterbefehl um Funktionen erweitert, um Bilder zu verschlüsseln und die Verschlüsselung bestimmter Schichten bestimmter Architekturen mithilfe eines bestimmten Schlüsselsatzes zu ermöglichen. Mit diesem Ansatz können Benutzer nur die Ebenen verschlüsseln, die vertrauliche Daten enthalten, und andere Ebenen unverschlüsselt lassen. Letzteres kann dedupliziert werden, dies ist jedoch für verschlüsselte Schichten kaum möglich.



Ebenso können wir die einzelnen Schichten einzelner Architekturen entschlüsseln. Wir haben einen Layerinfo-Unterbefehl hinzugefügt, der den Verschlüsselungsstatus jeder Schicht und die dafür verwendeten Verschlüsselungstechnologien anzeigt. Für OpenPGP können wir auch die für die Entschlüsselung erforderlichen Schlüssel-IDs anzeigen oder sie mithilfe eines Schlüsselbunds in die E-Mail-Adressen ihrer Empfänger konvertieren.



Darüber hinaus können Sie Containerbilder exportieren und importieren. Wir haben die Unterstützung für die Schichtverschlüsselung beim Export und die Entschlüsselung beim Import implementiert. Obwohl wir die Ebenen entschlüsseln, um das Rootfs-Dateisystem des Containers zu erstellen, bleiben die verschlüsselten Ebenen und die ursprünglichen Metadatendateien wie ihre Manifeste erhalten. Mit diesem Ansatz können Sie ein verschlüsseltes Bild exportieren und Berechtigungsprüfungen durchführen, wenn Benutzer einen Container mit einem verschlüsselten Bild starten möchten.



Wenn ein einfaches (unverschlüsseltes) Bild aus der Registrierung abgerufen wird, wird es automatisch entpackt und entpackt, sodass sofort Container daraus erstellt werden können. Um es für verschlüsselte Bilder einfacher zu machen, empfehlen wir, dass Sie den privaten Schlüssel an das Entpack-Team übergeben, damit diese die Ebenen vor dem Entpacken entschlüsseln können. Wenn das Bild mit mehreren Schlüsseln verschlüsselt ist, können mehrere Schlüssel an den Befehl pull übergeben werden. Diese Übertragung wird ebenfalls unterstützt. Nach dem erfolgreichen Extrahieren des verschlüsselten Bildes aus der Registrierung kann jeder mit Zugriff auf Containerd einen Container aus dem Bild erstellen. Um zu bestätigen, dass der Benutzer Rechte zur Verwendung des Container-Images hat, empfehlen wir, die privaten Schlüssel bereitzustellen, die zum Entschlüsseln des Containers verwendet werden.Wir verwenden Schlüssel, um die Autorisierung des Benutzers zu überprüfen, ob sie zum Entschlüsseln des LEK jeder verschlüsselten Ebene verwendet werden können, und wenn dies bestätigt wird, lassen wir den Container starten.



Eine Schritt-für-Schritt-Anleitung zur Verschlüsselung mit Containerd



In diesem Abschnitt werden die Verschlüsselungsschritte demonstriert, die mit containerderd mithilfe von ctr in der Befehlszeile angewendet werden. Wir zeigen Ihnen, wie Sie ein Container-Image verschlüsseln und entschlüsseln.



Zunächst müssen Sie das Git- Containerd / Imgcrypt-Repository klonen , das ein Teilprojekt ist und das Container-Image verschlüsseln / entschlüsseln kann. Dann müssen Sie Containerd erstellen und ausführen. Um diese Schritte ausführen zu können, müssen Sie wissen, wie die Golang-Entwicklungsumgebung eingerichtet ist: Für



imgcrypt ist Containerd Version 1.3 oder höher erforderlich.



Erstellen und installieren Sie imgcrypt:



# make
# sudo make install


Führen Sie Containerd mit der im folgenden Beispiel gezeigten Konfigurationsdatei aus. Verwenden Sie das Verzeichnis / tmp für Verzeichnisse, um Konflikte in Containerd zu vermeiden. Erstellen Sie auch eine Containerd-Version 1.3 aus dem Quellcode, installieren Sie sie jedoch nicht.



# cat config.toml
disable_plugins = ["cri"]
root = "/tmp/var/lib/containerd"
state = "/tmp/run/containerd"
[grpc]
  address = "/tmp/run/containerd/containerd.sock"
  uid = 0
  gid = 0
[stream_processors]
    [stream_processors."io.containerd.ocicrypt.decoder.v1.tar.gzip"]
        accepts = ["application/vnd.oci.image.layer.v1.tar+gzip+encrypted"]
        returns = "application/vnd.oci.image.layer.v1.tar+gzip"
        path = "/usr/local/bin/ctd-decoder"
    [stream_processors."io.containerd.ocicrypt.decoder.v1.tar"]
        accepts = ["application/vnd.oci.image.layer.v1.tar+encrypted"]
        returns = "application/vnd.oci.image.layer.v1.tar"
        path = "/usr/local/bin/ctd-decoder"
# sudo ~/src/github.com/containerd/containerd/bin/containerd -c config.toml


Erstellen Sie ein RSA-Schlüsselpaar mit dem Befehlszeilentool openssl und verschlüsseln Sie das Bild:



# openssl genrsa --out mykey.pem
Generating RSA private key, 2048 bit long modulus (2 primes)
...............................................+++++
............................+++++
e is 65537 (0x010001)
# openssl rsa -in mykey.pem -pubout -out mypubkey.pem
writing RSA key
# sudo chmod 0666 /tmp/run/containerd/containerd.sock
# CTR="/usr/local/bin/ctr-enc -a /tmp/run/containerd/containerd.sock"
# $CTR images pull --all-platforms docker.io/library/bash:latest
[...]
# $CTR images layerinfo --platform linux/amd64 docker.io/library/bash:latest
   #                                                                    DIGEST      PLATFORM      SIZE   ENCRYPTION   RECIPIENTS
   0   sha256:9d48c3bd43c520dc2784e868a780e976b207cbf493eaff8c6596eb871cbd9609   linux/amd64   2789669                          
   1   sha256:7dd01fd971d4ec7058c5636a505327b24e5fc8bd7f62816a9d518472bd9b15c0   linux/amd64   3174665                          
   2   sha256:691cfbca522787898c8b37f063dd20e5524e7d103e1a3b298bd2e2b8da54faf5   linux/amd64       340                          
# $CTR images encrypt --recipient jwe:mypubkey.pem --platform linux/amd64 docker.io/library/bash:latest bash.enc:latest
Encrypting docker.io/library/bash:latest to bash.enc:latest
$ $CTR images layerinfo --platform linux/amd64 bash.enc:latest
   #                                                                    DIGEST      PLATFORM      SIZE   ENCRYPTION   RECIPIENTS
   0   sha256:360be141b01f69b25427a9085b36ba8ad7d7a335449013fa6b32c1ecb894ab5b   linux/amd64   2789669          jwe        [jwe]
   1   sha256:ac601e66cdd275ee0e10afead03a2722e153a60982122d2d369880ea54fe82f8   linux/amd64   3174665          jwe        [jwe]
   2   sha256:41e47064fd00424e328915ad2f7f716bd86ea2d0d8315edaf33ecaa6a2464530   linux/amd64       340          jwe        [jwe]


Starten Sie Ihre lokale Bildregistrierung, damit Sie das verschlüsselte Bild darauf hochladen können. Um verschlüsselte Container-Images zu empfangen, benötigen Sie die neuesten Registrierungsversionen.



# docker pull registry:latest
# docker run -d -p 5000:5000 --restart=always --name registry registry


Laden Sie das verschlüsselte Image in Ihre lokale Registrierung hoch, extrahieren Sie es mit ctr-enc und führen Sie das Image aus:



# $CTR images tag bash.enc:latest localhost:5000/bash.enc:latest
# $CTR images push localhost:5000/bash.enc:latest
# $CTR images rm localhost:5000/bash.enc:latest bash.enc:latest
# $CTR images pull localhost:5000/bash.enc:latest
# sudo $CTR run --rm localhost:5000/bash.enc:latest test echo 'Hello World!'
ctr: you are not authorized to use this image: missing private key needed for decryption
# sudo $CTR run --rm --key mykey.pem localhost:5000/bash.enc:latest test echo 'Hello World!'
Hello World!


Fazit



Das Verschlüsseln von Container-Images ist eine gute Ergänzung zu ihrer Sicherheit. Es gewährleistet die Vertraulichkeit von Daten und die Integrität von Container-Images am Speicherort. Die vorgeschlagene Technologie basiert auf den öffentlich verfügbaren RSA-, Ellipsenkurven- und AES-Verschlüsselungstechnologien. Es wendet Schlüssel auf übergeordnete Verschlüsselungsschemata wie OpenPGP, JWE und PKCS # 7 an. Wenn Sie wissen, wie man mit OpenPGP arbeitet, können Sie Container-Images für OpenPGP-Empfänger mit ihren E-Mail-Adressen verschlüsseln, während einfache RSA-Schlüssel und elliptische Kurven für die Verschlüsselung wie JWE verwendet werden.



All Articles