cloud-init als Sicherheitsproblem
Grrrr!
Oh, was’n Scheiß.
Ich bin vor ungefähr zwei Jahren und vor zwei Monaten schon mal auf zwei Linux-Probleme gestoßen, die sich als zusammenhängend herausgestellt haben.
Vor so ungefähr zwei Jahren habe ich auf einem Server-PC eine Ubuntu-Server-Version installiert, danach aber den Hostnamen geändert. Immer nach dem Reboot hatte die Maschine aber den alten Namen wieder. Und zwar selbst dann, wenn sie nicht am Netzwerk hing und den nicht per DHCP oder DNS bekommen haben kann, und der alte Name nirgends in /etc mehr auftauchte. Woher und warum zum Teufel kommt die Kiste immer auf ihren alten Namen, den sie doch eigentlich nicht mehr kennen dürfte, eigentlich nicht kennen kann?
Ursache war cloud-init.
cloud-init ist ein Paket, das auf den ersten Blick sehr nützlich, auf den zweiten eher gefährlich ist.
Denn eigentlich und nach der (damals kaum existierenden, inzwischen umfangreichen aber irreführend unvollständigen Dokumentation) ist das eine feine Sache. Man gibt (ähnlich wie Kickstarter usw.) in einer Konfigurationsdatei (YAML-Syntax) so ein paar einfache Grundeinstellungen für die Maschine an, etwa welche ssh-Schlüssel eingetragen werden sollen, welche Accounts existieren sollen, oder auch einen puppet-client starten. Eigentlich ist das wunderbar, denn wenn man das einmal geschrieben hat, kann man bei einem Cloud-hoster wunderbar schnell und einfach so eine Maschine anwerfen. Einfach so „starte mir mal eine Maschine, hier ist die Konfigurationsdatei” und hinten kommt die laufende Maschine raus oder zumindest so eingestellt, dass Zugang, Zeitzone, Sprache und so weiter schon alles stimmen. Sehr nützlich.
Aber ach.
Was nämlich in der Doku nicht drinsteht (jedenfalls vor zwei Jahren und zwei Monaten nicht drinstand, ich habe es angemeckert) ist, dass das Ding nicht, wie man denken würde, die Maschine nur beim ersten Start initialisiert, und dann nichts mehr tut, sondern das auch fürderhin bei jedem Neustart.
Und je nachdem, in welcher Cloud-Umgebung das läuft, so ganz 100%ig bin ich mangels Dokumentation noch nicht durchgestiegen, holt sich das Ding beim Boot per http die Konfigurationsdatei jedesmal neu und wendet sie neu an.
Das war bei mir damals, als ich vor ca. 2 Jahren mal eine Servermaschine installiert habe, nicht der Fall, weil keine Cloud-Maschine und kein Cloud-Image, sondern vom DVD/USB-Stick-Image installiert, aber fieserweise hatte Ubuntu den Installer hinter den Kulissen so geändert, dass der die Maschine nicht mehr direkt installiert, sondern so eine cloud-init-Datei erzeugt und auf der Maschine ablegt und die Maschine sich dann selbst konfiguriert (Accounts anlegen usw.)
Das tat sie aber bei jedem Booten. Und weil die Datei nicht in /etc sondern irgendwo unter /var lag (pfad weiß ich nicht mehr auswendig) hat die bei jedem Boot ihren Hostnamen wieder auf den alten Namen zurückgestellt, der in dieser Datei stand.
Da frage ich schon „Seid Ihr wahnsinnig?”
Was ist denn, wenn ich ein kompromittiertes Passwort ändere oder einen ssh-Schlüssel entferne, und das Ding installiert die beim nächsten Boot undokumentiert wieder zurück?
Ja, aber das brauche man doch, weil cloud heute so wichtig sei.
Nun, fragte ich, das würde ja noch gut erklären, warum man es beim ersten Boot macht, aber doch nicht jedesmal neu.
Doch, doch, hieß es, weil man ja in der cloud auch den Fall haben kann, dass jemand eine bereits installierte Maschine clont. Und dann muss natürlich jede Kopie in der Lage sein, sich mit neuen IP-Adressen und so weiter neu zu installieren.
Ich hatte mich damals sehr deutlich dahingehend geäußert, dass das ein brachiales Sicherheitsloch sein kann und in die Kategorie „geht gar nicht” fällt.
Irgendwie haben sie das womöglich gefixt, ich habe auf der Kiste irgendwas gelöscht, und das Problem seither nicht mehr, irgendwie auch wieder vergessen.
2 Jahre später.
Ich hatte neulich, vor einigen Monaten, mal das Unterfangen betrieben, auf Cloud-Maschinen Kubernetes zu installieren und diese Installation per Ansible zu automatisieren, also nicht per Hand zu pfriemeln, um einen sauber dokumentierten und reproduzierbaren Weg zu haben, bei Bedarf ruck-zuck und automatisiert ein Kubernetes laufen zu haben. Sowas kann man ja immer mal spontan brauchen. Und wenn man es jederzeit sofort neu erzeugen kann, muss man es auch nicht aufheben, sondern kann es einfach löschen, wenn man es nicht braucht, damit es kein Geld kostet.
Also habe ich neulich, vor zwei Monaten, an genau daran gebaut, bin aber an einem seltsamen Problem hängen geblieben: Ab und zu passierte es, dass ich mich nicht ohne weiteres auf den Maschinen einloggen konnte, weil der ssh-client beklagte, dass sich die Host-Keys der Server geändert hatten, der client also eine Man-in-the-middle-Attacke witterte und das Einloggen verweigerte.
Tatsächlich ist mir das einige Male (aber nicht immer) passiert, dass die Maschinen bei einem Reboot die ssh-schlüssel neu erzeugten. Die Dateien in /etc/ssh waren da jeweils ganz neu.
Das kann nur mit cloud-init zu tun haben, hatte auch einen Bug-Report aufgemacht, falls da jemand was weiß, aber jeder hat nur auf den anderen gezeigt. Der Provider müsse schuld sein.
Dann hatte ich 2 Monate lang keine Zeit, mich darum zu kümmern.
Am Wochenende habe ich nochmal frisch angefangen, bin aber sofort wieder in dieses Problem gelaufen. Ab und zu einfach neue ssh-Schlüssel.
Und habe mir das mal näher angesehen.
Es sieht nach einem Timing-Problem mit dem vermaledeiten systemd aus.
Wenn die Kiste bootet, kommt sie in der Hardware mit einer virtuellen Ethernetkarte im virtuellen PCI-Slot 3 hoch. Und heißt so altmodisch eth0.
Dann kommt aber Ubuntu 20.04/systemd und meint nach neuer Praxis, dass die Interfaces nicht mehr wie früher in der Reihenfolge ihres Auftauchens eth0, eth1, eth2 usw. numeriert werden dürfen, sondern „predictable” sein müssen, also unabhängig von Zahl und Reinfolge, und benennt das Interface nach ens3 um. Ethnernet Network Slot 3.
So weit, so gut.
Oder schlecht.
Das Ding holt sich nämlich bei jedem Booten wieder die cloud-init-Datei per http vom Provider, der da drin sagt, welche IP-Adresse das Interface haben soll, welche ssh-Schlüssel zu installieren sind und so weiter.
Steht da das gleiche drin wie vorher, ist alles gut.
Und da steht vom Provider das Interface nun als eth0 drin, identifiziert über die MAC-Adresse.
Was macht Linux nun damit? Erkennt es und benennt das Interface erneut um, wieder zurück nach eth0. Es heißt also nach dem Booten eth0, wird dann nach dem predictable-Schema auf ens3 und dann durch cloud-init (genauer gesagt: die vom letzten cloud-init-Lauf erzeugte netplan-Datei) wieder nach eth0 zurückumbenannt.
Mittendrin aber versucht cloud-init, das Interface nach einem Universalschema zu konfigurieren und per http die cloud-Konfiguration zu holen. Und das geht nur meistens, aber nicht immer gut, weil manchmal anscheinend (tiefer bin ich noch nicht drin) netplan und cloud-init so gleichzeitig laufen, dass cloud-init gerade ens3 als Interfacenamen sieht und dann nicht drauf zugreifen kann, weil netplan ihn genau zwischendrin wieder nach eth0 umbenannt hat.
Der cloud-init-Code geht dann baden und wirft einen großen Fehler, und kommt dann zu dem Schluss, dass es keine Config gibt, man also ein Clon sei und neue Schlüssel erzeugen müsse.
Was für ein Mist.
Was aber irgendwo auch heißt, dass die Maschine bei einem Reboot mit gewisser Wahrscheinlichkeit (ich weiß nicht, wie die dort ihr Netzwerk genau konfiguriert und abgesichert haben) angreifbar wäre, wenn nämlich der Provider (der aber auch andere Möglichkeiten hat) oder jemand mit einer Maschine im selben Netz eine falsche cloud-init-Datei unterjubeln und damit zusätzliche ssh-Schlüssel als authorized unterjubeln kann.
Ich bin gerade alles andere als begeistert. Und anscheinend der erste, der das bei Ubuntu eingeworfen hat.
Es hat zumindest so einen gewissen Odor von Backdoor.
Jedenfalls Murks.
So ganz im Detail bin ich aber noch nicht durchgestiegen, weil gerade diese wichtigen Sachen nicht in der Doku stehen (oder zumindest vor 2 Monaten noch nicht drin standen), und man das im Normalfall nicht merkt, dass das Ding bei jedem Boot die Maschine neu überbügelt.
Nicht gut.