Warum ist “Sicherheit” überhaupt so wichtig? Was habe ich als Software-Entwickler damit zu tun? Und: wo kann ich mal ungestört herumspielen und Dinge kaputtmachen? Fragen über Fragen …
Wir haben Antworten für dich!
In unserer kommenden Blogserie “Über Ziegen und andere Kuriositäten” nehmen wir das Thema „Kubernetes-Sicherheit“ ernst – und dich an die Hand.
Was erwartet dich?
- Eine Anleitung zum Spielen, Angreifen & Zerstören
- Rollentausch: nimm abwechselnd die Rolle des “Angreifers”, des “Verteidigers” und des “Entwicklers” an und realisiere die unterschiedlichen Perspektiven
- Praktische Szenarien
- Tools & deren Einsatz
Vorbereitung
Am besten schaffst du dir eine günstige, isolierte “Hacker-Umgebung”. Wie das geht, erfährst du gleich am Beispiel eines automatisierten Hetzner-Cloud-Setups. Natürlich kannst du das Setup auch lokal umsetzen oder eine Alternative zu Hetzner wählen; wir haben uns der Einfachheit halber für diese Variante entschieden.
Anleitung für einen “Goat Shed”
Was hat das alles mit Ziegen zu tun? Nein, du hast dich nicht verlesen – wir bauen uns eine kleine Hetzner Cloud VM für unsere Experimente auf Basis von Kubernetes Goat und dessen praktischen Szenarien. Die Vorteile dieses Setups sind wie folgt:
Ubuntu VM provisionieren
Hetzner Cloud & das passende cloud-init Skript
Wenn du noch keinen Hetzner-Cloud-Account hast, besorge dir hier einen. Für unsere Zwecke reicht ein CX21 (der ist mit 2 vCPUs und 4 GB RAM zwar nicht üppig bestückt, dafür aber ausreichend und günstig). Wähle “Ubuntu 22” als OS Image aus setze einen Hostnamen, z. B. “k8s-goat”.
Damit Docker bereits vorinstalliert ist und fail2ban grundlegende SSH Auth Attacks abwährt, nutzt bitte unser kompaktes “Cloud Config”-Skript und füge es im Setup Wizard ein, ebenso wie deinen SSH Public Key. Übrigens enthält das Skript auch auskommentierte Zeilen für weitere (Admin-)User und deren sudo-Berechtigungen:
Sobald der neue Server bereitsteht, loggst du dich per SSH und deinem Public Key ein:
ssh -l root <PUBLIC-IPV4> sudo su -l
Workshop vorbeiten
Führe dann folgende manuelle Schritte (siehe Gist) aus, um neben kubectl, helm und kind auch kubernetes-goats initialisiert zu haben.
Schau’ dich um!
Nun sollte ein Single Node Cluster auf Basis von kind
laufen:
kubectl get nodes kubectl cluster-info kubectl get namespaces # kubernetes goat sollte erkennbar sein (Beispielausgabe!) kubectl get pods NAME READY STATUS RESTARTS AGE batch-check-job-d8lw5 0/1 Completed 0 4h15m build-code-deployment-7b558b489f-gqjlf 1/1 Running 0 4h15m hacker-container 1/1 Running 5 (96m ago) 4h8m health-check-deployment-ff6f9f76f-2z25p 1/1 Running 0 4h15m hidden-in-layers-x2j22 1/1 Running 0 4h15m internal-proxy-deployment-7955c45559-9q57x 2/2 Running 0 4h15m kubernetes-goat-home-deployment-578759495-dnlvj 1/1 Running 0 4h15m metadata-db-7b78ff9dd9-l4nfq 1/1 Running 0 4h15m poor-registry-deployment-c96986875-n4zp8 1/1 Running 0 4h15m system-monitor-deployment-7d665b6fdf-zsswv 1/1 Running 0 4h15m
Szenario #1: Namespaces überwinden
Jetzt geht’s endlich los! Namespaces in Kubernetes sind aus Sicherheitsperspektive – ähnlich wie Secrets – ein eher spärliches Konzept. Sie isolieren von alleine keine Workloads und mit etwas Gespür und Erfahrung können wir auch unser “Clusternachbarn” leicht ausspionieren.
Challenge: Secrets aus einem beliebigen Redis Server auslesen
Versetze dich bitte zunächst in die Rolle des “Hackers”. Dieser hat bereits Zugriff auf unser Cluster und schaut sich emsig um. Wenige Augenblicke wird es dauern, bis er zu freigiebige Redis-Services entdeckt und diese abfragt.
Kubernetes nutzt einige Default-Netzwerk-CIDRs, z. B. für Pods oder Services.
Als Cluster-Admin kannst du z. B. folgendes tun, um das konfigurierte Service CIDR zu verifizieren:
kubectl get cm kubeadm-config -n kube-system -o yaml | grep serviceSubnet serviceSubnet: 10.96.0.0/16
Unser “Attacker” startet nun eine Shell in einem präparierten Pod und beginnt die Suche nach Redis: kubectl run -it hacker-container --image=madhuakula/hacker-container -- sh
Er scannt nun das gesamte Services-Subnet und findet eine IP-Adresse:
zmap -p 6379 10.96.0.0/16 -o results.csv # output dedacted cat results.csv 10.96.90.123 redis-cli -h 10.96.90.123 10.96.90.123:6379> KEYS * 1) "SECRETSTUFF" 10.96.90.123:6379> GET SECRETSTUFF "k8s-goat-a5a3e446faafa9d0514b3ff396ab8a40" 10.96.90.123:
Bäm! Obwohl in unserem Beispiel Redis nicht im Default-Namespace erreichbar ist, kommen wir ohne Weiteres an die Daten heran!
Die Antwort des “Defenders”
Wechseln wir – wie angedeutet – jetzt die Perspektive. Der zuständige Cluster-Administrator kann nun Vorkehrungen treffen. So könnte er z. B. über das Kubernetes-Konzept der Network Policies nachdenken und diese etablieren.
Die Antwort des “Entwicklers”
Nach einiger Recherche entschließt sich das Team, eine Authentifizierung ihres Redis In-Memory Stores zu erzwingen. Das läßt sich zudem sehr gut über GitOps-Prinzipien automatisieren und kontinuierlich überwachen.
Recap: das haben wir gemacht & gelernt
- Isoliertes Single-Node Kubernetes-Cluster auf Basis von kind
- kubernetes-goat Workshop Repository ausgecheckt und Beispielworkloads installiert
- Mit zmap nach offenen Redis-Services geforscht
- Redis Keys aus einem beliebigen Kubernetes-Namespace ausgelesen
- Erfahren, was “Defenders” und “Entwickler” hiernach tun können
Wie geht es weiter?
Unser “Spiel-Cluster” können wir für unsere nächsten Explorationen und Szenarien weiterverwenden. Teil 2 dieser Reihe befasst sich dann mit “Secrets in Sourcecode und Docker Images”.
Schreibe einen Kommentar