Auswahl eines sicheren Docker-Basis-Images

Wenn du nach einem sicheren Docker-Basis-Image suchst, wonach suchst du dann genau?

Bei Flownative führen wir viele Tools und Anwendungen in Docker-Containern aus - allen voran Flownative Beach. Schon früh haben wir beschlossen, uns nicht auf fertige Images für PHP, Redis und all die anderen Komponenten, die wir brauchen, zu verlassen, weil wir wissen wollten, was wir betreiben, und nicht nur hoffen wollten, dass die Image-Maintainer ein Auge auf Sicherheitsfragen haben.

Wenn du viele Images selbst entwickelst, brauchst du ein gutes Basis-Image, auf das du dich verlassen kannst. Das Basis-Image sollte klein, leistungsfähig und sicher sein. Und obwohl wir in der Vergangenheit ein Basis-Image hatten, das gut funktioniert hat, ist es jetzt Zeit für ein neues.

Warum ein neues Basis-Image?

Während einer teilweisen Sicherheitsüberprüfung unserer Plattform schlug ein Berater vor, dass wir dieses Thema noch einmal aufgreifen und nach Möglichkeiten suchen sollten, unser Docker-Setup zu härten. Konkret wurde Alpine als Ersatz für unser abgespecktes Ubuntu Base Image vorgeschlagen.

Dieser Vorschlag klang für mich logisch - schließlich ist Alpine Linux für seinen minimalistischen Ansatz und seine Sicherheit bekannt. Also bereitete ich mich darauf vor, alle unsere Beach -Images auf der Basis von Alpine neu zu erstellen.

Dabei stieß ich jedoch auf einige Probleme mit Inkompatibilitäten zu den von uns verwendeten C-Erweiterungen und Paketen, die nicht im Alpine-Paket verfügbar waren. Also ging ich noch einmal einen Schritt zurück und dachte genauer drüber nach: Warum ist Debian zum Beispiel eine so schlechte Wahl für ein Basis-Image? Weiß ich das wirklich?

Werfen wir einen Blick auf einige bewährte Sicherheitspraktiken für Basis-Images und vergleichen wir, wie Debian und Alpine in das Bild passen.

Alpine und Debian - einige Hintergründe

Alpine Linux hat seine Wurzeln im Embedded Linux. Es ist eigentlich ein Fork des LEAF-Projekts (Linux Embedded Appliance Framework), das wiederum als Fork der sogenannten "Linux-on-a-floppy"-Distribution (Linux Router Project) begann, einem Betriebssystem, das auf eine 1,44 MB große Diskette passte. Ich vermute, dass es in der Docker-Community ursprünglich vor allem wegen seiner geringen Größe beliebt war - schließlich willst du für ein einfaches PHP-Image keine Gigabytes an Daten herunterladen.

Debian GNU/Linux ist eines der ältesten Betriebssysteme, das auf dem Linux-Kernel basiert und seit den frühen 1990er Jahren existiert. Debian ist eine sehr funktionsreiche Distribution, da sie auf zehntausende von Paketen zurückgreifen kann und viele verschiedene Architekturen und Bedürfnisse unterstützt. Debian - und die davon abgeleitete Distribution Ubuntu - sind heute sicherlich die meistgenutzten Betriebssysteme für Internet-Server [Referenz].

Nach dieser Geschichtsstunde wollen wir uns nun den Best Practices für ein sicheres Docker-Basis-Image widmen:

Angriffsfläche minimieren

Was nicht enthalten ist, kann auch nicht kaputtgehen. Das ist eine der wichtigsten Regeln für ein Basis-Image: Achte darauf, dass nur die Software enthalten ist, die tatsächlich gebraucht wird. Und, als Konsequenz hieraus: Stelle sicher, dass du wirklich weißt, welche Software enthalten ist und wie sie funktioniert.

Abgesehen davon, dass du nur ein Minimum an Paketen einbindest, kannst du auch den Zugriff auf bestimmte Befehle auf den Benutzer beschränken, der die Prozesse tatsächlich ausführt.

Benutzer mit den geringsten Privilegien

Es gibt keinen guten Grund, Prozesse innerhalb eines Containers als root auszuführen. Deshalb führen wir in Beach Containern Prozesse als Benutzer mit der ID 1000 aus.

Es ist auch wichtig, dass du genau darauf achtest, wie du deine Container ausführst. In Kubernetes solltest du zum Beispiel auf jeden Fall vermeiden, einen Pod im privilegierten Modus auszuführen. Und wenn du die volle Kontrolle über deine Runtime und Images hast, kannst du bestimmte Aktionen durch Seccomp-Sicherheitsprofile einschränken .

Betrachte zum Beispiel die folgende Datei, die wir profile.json nennen:

{
  "defaultAction": "SCMP_ACT_ALLOW", 
  "syscalls": [
    {
    "name": "mkdir",
    "action": "SCMP_ACT_ERRNO"
    }
  ]
}

... einen Container mit dem oben genannten Profil wie folgt auszuführen, würde den Prozessen des Containers die Ausführung von mkdir verweigern:

docker run --rm -it \
  --security-opt seccomp=profile.json \
  hello-world

Schwachstellen finden und beheben

Sicherheit ist nicht statisch (oder wie wir sagen: "Sicherheit ist wie der Versuch, Pudding an die Wand zu nageln"). Und realistisch betrachtet kannst du deine Images nur sicher halten, wenn du Schwachstellen-Scanner einsetzt und den Prozess automatisierst.

Es gibt verschiedene Sicherheitsscanner für Docker-Container, sowohl kommerzielle als auch freie / Open Source Varianten. Das Problem bei vielen dieser Scanner ist, dass sie eine Menge Output produzieren, auf den du reagieren musst. Und was noch schlimmer ist: Sie produzieren oft viele falsch positive Ergebnisse.

Du musst auch wissen, dass jedes Team, das eine Linux-Distribution betreut, Sicherheitsprobleme unterschiedlich behandelt. Da Debian sehr offen mit allen Sicherheitsproblemen umgeht, kann die Liste länger sein als bei anderen Projekten.

Häufige Sicherheitsupdates

Das Betriebssystem deiner Wahl sollte einen gut etablierten Prozess für den Umgang mit Sicherheitslücken haben und regelmäßig Sicherheitsupdates veröffentlichen. Aber selbst die schnellste Veröffentlichung von Sicherheitsupdates nützt dir nichts, wenn sie nicht in deinem Image enthalten ist.

Das ist eine der wichtigsten Erkenntnisse aus einigen Projekten, die ich mir ansehen konnte: Sicherheitsupdates für Docker-Images wurden nie automatisiert und selten nach einem festen Zeitplan geplant. Um das richtig zu machen, musst du dein Basis-Image und alle anderen davon abgeleiteten Images automatisch neu erstellen. Durch einen solchen Rebuild erhältst du normalerweise automatisch alle aktuellen Sicherheitsupdates.

Die nächste Herausforderung besteht dann darin, diese Images auszurollen - am besten ebenfalls automatisch -, damit alle deine Anwendungen auf den aktualisierten Basis-Images laufen.

Weitere zu berücksichtigende Aspekte

Es gibt einige weiche Faktoren, die ich ebenfalls für sicherheitsrelevant halte.

Erstens: Wenn die Software, die du verwendest, schlecht dokumentiert ist und es keine ausreichend große Community gibt, die dir Antworten geben könnte, ist es wahrscheinlich, dass du nicht genug über das System weißt, um mögliche Sicherheitslücken zu erkennen.

Zweitens, und in gewisser Weise verwandt: Du musst dich mit dem System auskennen und es gut bedienen können. Wenn du jahrelange Erfahrung als Ubuntu-Benutzer hast, wirst du vielleicht Probleme mit Slackware haben. Aber im Ernst: Wenn du dich zwischen zwei Optionen entscheiden musst und unentschlossen bist, kann deine Erfahrung mit einem System den Ausschlag geben.

Drittens: Vergewissere dich, dass das Basis-Image die Funktionalität für die meisten deiner Anwendungsfälle bietet. Es nützt nichts, wenn das Basis-Image super sicher ist, aber du musst viele Inkompatibilitäten umgehen und dadurch können - möglicherweise sicherheitsrelevante - Fehler entstehen.

Die Vor- und Nachteile

Natürlich ist diese Liste nicht erschöpfend. Es ist eher eine Auflistung von Punkten, die mir bei meiner Suche wichtig waren.

Alpine

Pro:

  • sehr kleine Bilder: die Community achtet sehr darauf, die Bildgrößen zu minimieren
  • minimale Funktionalität: nur absolut notwendige Pakete enthalten
  • leichtgewichtiges Init-System: wie Gentoo verwendet Alpine OpenRC, eine leichtgewichtige Alternative zu systemd
  • musl-Leistung: In einigen Fällen kann die musl libc leistungsfähiger sein als die glibc

Nachteile:

  • eher schlecht dokumentation
  • kleines Team: Derzeit sind 3 Entwickler als Alpine Linux Team gelistet
  • mögliche Inkompatibilitäten: musl libc kann Probleme mit einigen C-basierten Plugins verursachen und es können Anpassungen notwendig sein, wenn du Software selbst kompilierst

Debian

Pro:

  • kleine Images: Die Größe der abgespeckten Debian-Images (wie z.B. minideb von Bitnami) ist fast gleichauf mit Alpine (z.B. ist minideb + Python nur 7 MB größer als Alpine + Python)
  • viele Pakete: Es gibt kaum eine Software für Linux, die nicht auch für Debian gepackt wurde
  • gut getestet: Aufgrund seiner Beliebtheit wird Debian häufig verwendet und Probleme werden eher gefunden
  • umfassende Dokumentation; außerdem hat die Gemeinschaft eine große Menge an zusätzlichen Dokumentationen und Anleitungen erstellt
  • mehr Sicherheitsüberprüfungen: Auch hier gilt, dass Debian aufgrund der größeren Gemeinschaft mehr Aufmerksamkeit erhält und es wahrscheinlicher ist, dass Sicherheitslücken entdeckt werden, z. B. in der glibc im Vergleich zur musl libc (Annahme). Debian hat außerdem ein Sicherheitsprüfungsteam, das proaktiv nach Sicherheitsproblemen sucht.
  • herkunft: Die Authentizität von Paketen kann überprüft werden, z.B. mit debsigs / dpkgsig

Nachteile:

  • etwas größere Angriffsfläche: minideb besteht aus etwa 35 Paketen (wie bash, grep, hostname, mount ...), da apt davon abhängt
  • mehr Fehlalarme: Scanner können mehr Fehlalarme melden, auf die du achten musst

Fazit

Meiner Meinung nach gibt es kein Basis-Image, das für alle Zwecke geeignet ist - du musst immer auch die spezifischen Anwendungsfälle berücksichtigen. Für unser Szenario sind wir zu folgendem Schluss gekommen:

Alpine ist eine gute Wahl für ein Basis-Image, auch wenn es unter Experten unterschiedliche Meinungen darüber gibt, ob Alpine eine gute Wahl in Bezug auf Sicherheit und Stabilität ist. Es kann einige Kompatibilitätsprobleme oder Bugs im Zusammenhang mit dem Netzwerk geben, aber im Allgemeinen funktioniert es sehr gut. Für manche Software gibt es keine fertigen Pakete, also musst du sie selbst kompilieren und paketieren.

Ich habe jedoch gelernt, dass Debian (inzwischen) auch eine gute Wahl für ein Basis-Image ist! Der Fußabdruck ist vergleichbar mit dem von Alpine. Und ein abgespecktes Basis-Image enthält nur ein Minimum an Paketen, die gut gewartet werden und jeweils schon seit vielen Jahren existieren.

Die richtigen Werkzeuge (Scanner, Build-Automatisierung, automatisches Roll-Out) und die richtige Konfiguration (Least Privilege, Sicherheitsprofile, Drop-Container-Funktionen, Netzwerkrichtlinien) erweisen sich als ebenso wichtig wie ein solides Basis-Image.

Beide - Alpine und ein abgespecktes Debian - sind eine gute Wahl. Letztendlich entscheiden wir uns aber für Debian, weil wir mehr Vertrauen in das Ökosystem haben, erwarten, dass es besser zu der Software passt, die wir darauf laufen lassen wollen, und weil wir auf unsere langjährige Erfahrung als Debian-/GNU-Linux-Nutzer aufbauen können.