Kubernetes-Sicherheit kann klingen, als wäre sie ein eigener Beruf neben dem Betrieb. Manchmal stimmt das. Trotzdem braucht man als Einsteiger eine praktische Grundlage, weil die ersten Fehler meist einfach sind: alles in default, jeder Pod nutzt den Default-ServiceAccount, breite Rechte aus einem Tutorial, Secrets wie harmlose YAML.

Namespaces und RBAC sind keine Themen für „später in Produktion“. Sie sind die Grammatik eines gemeinsam genutzten Clusters. Namespaces teilen den Cluster in verständliche Bereiche. RBAC entscheidet, was Benutzer und Workloads tun dürfen. Sicherheitsgrundlagen verhindern, dass ein Fehler an einer Stelle zum clusterweiten Problem wird.

Ziel ist nicht, in einem Blogpost Security Engineer zu werden. Ziel ist ein Modell, mit dem der nächste kubectl apply weniger naiv ausfällt.

Namespaces sind ein Geltungsbereich, keine Festung

Ein Namespace teilt viele Kubernetes-Ressourcen in benannte Bereiche. Pods, Services, Deployments, ConfigMaps, Secrets, Roles und RoleBindings liegen normalerweise in einem Namespace. Nodes, PersistentVolumes, StorageClasses, ClusterRoles und Namespaces selbst sind clusterweit.

Den Unterschied sieht man mit:

kubectl api-resources --namespaced=true
kubectl api-resources --namespaced=false

Namespaces sind nützlich, weil Teams und Umgebungen einen eigenen Arbeitsbereich bekommen:

kubectl create namespace payments-dev
kubectl get pods -n payments-dev
kubectl get deploy,svc,cm,secret -n payments-dev

Sie reduzieren auch Fehler. Ein Deployment in payments-dev zu löschen ist schlecht. Dasselbe in payments-prod ist schlimmer. Explizite Namespaces machen solche Risiken sichtbarer.

Ein Namespace ist allein keine harte Sicherheitsgrenze. Er verschlüsselt keinen Traffic automatisch, blockiert keinen Netzwerkzugriff, verhindert nicht jede namespace-übergreifende Lesemöglichkeit und stoppt keinen privilegierten Benutzer. Er ist ein Geltungsbereich, den andere Kontrollen nutzen können.

Dieser Unterschied zählt. Sagt jemand: „Wir sind sicher, weil jedes Team einen Namespace hat“, lautet die nächste Frage: Was erzwingt das? RBAC? NetworkPolicy? ResourceQuota? Admission Policies? Getrennte Cluster für stärkere Isolation? Der Namespace ist die Adresse. Die Leitplanken sind eigene Mechanismen.

Was in einen Namespace gehört

Für den Einstieg eignen sich Namespaces gut für:

  • Trennung von Umgebungen wie dev, staging und prod
  • Trennung von Teams oder Anwendungen
  • Quotas und Limits für eine Gruppe von Workloads
  • RBAC, damit ein Team eigene Objekte verwalten kann
  • NetworkPolicies in einem begrenzten Bereich

Ein kleiner Namespace-Aufbau kann so aussehen:

apiVersion: v1
kind: Namespace
metadata:
  name: demo
  labels:
    purpose: learning
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: demo-quota
  namespace: demo
spec:
  hard:
    requests.cpu: "2"
    requests.memory: 4Gi
    limits.cpu: "4"
    limits.memory: 8Gi
    pods: "20"

Die Quota ist keine Security im engen Sinn, aber Betriebssicherheit. Sie verhindert, dass ein Namespace aus Versehen unbegrenzt Ressourcen verbraucht.

Ein häufiger Irrtum: „Namespaces ersetzen Labels.“ Tun sie nicht. Services wählen Pods über Labels. Deployments verwalten Pods über Labels. NetworkPolicies wählen Pods über Labels. Namespaces ordnen den Raum; Labels identifizieren Dinge im Raum.

RBAC beantwortet eine einfache Frage

RBAC steht für Role-Based Access Control. Der Begriff klingt formell; die Kernfrage ist einfach:

Wer darf was mit welchen Ressourcen tun?

In Kubernetes kann „wer“ ein menschlicher Benutzer, eine Gruppe oder ein ServiceAccount sein. „Was“ ist ein Verb wie get, list, watch, create, update, patch oder delete. „Welche Ressourcen“ meint Pods, Deployments, Secrets, ConfigMaps und so weiter.

Vier RBAC-Objekte sind besonders wichtig:

Role. Eine Menge von Berechtigungen in einem Namespace.

RoleBinding. Weist eine Role einem Subject in einem Namespace zu.

ClusterRole. Eine clusterweite Menge von Berechtigungen. Sie kann für clusterweite Ressourcen gelten oder in Namespaces wiederverwendet werden.

ClusterRoleBinding. Weist eine ClusterRole clusterweit zu.

Wenn möglich, mit Role und RoleBinding anfangen. ClusterRoleBinding nur, wenn wirklich clusterweiter Zugriff nötig ist.

Eine lesende Role für einen Namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: workload-reader
  namespace: demo
rules:
  - apiGroups: [""]
    resources: ["pods", "pods/log", "services", "configmaps"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["apps"]
    resources: ["deployments", "replicasets"]
    verbs: ["get", "list", "watch"]

Und ein Binding für einen ServiceAccount:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: dashboard
  namespace: demo
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: dashboard-reads-workloads
  namespace: demo
subjects:
  - kind: ServiceAccount
    name: dashboard
    namespace: demo
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: workload-reader

Damit darf der ServiceAccount keine Pods löschen, keine Secrets lesen und keine Deployments ändern. Genau das ist der Sinn.

Berechtigungen testen, nicht raten

kubectl auth can-i ist einer der nützlichsten RBAC-Befehle am Anfang:

kubectl auth can-i get pods -n demo
kubectl auth can-i delete pods -n demo
kubectl auth can-i get secrets -n demo

Admins können ein Subject impersonieren und dessen Rechte prüfen:

kubectl auth can-i list pods -n demo \
  --as=system:serviceaccount:demo:dashboard

kubectl auth can-i get secrets -n demo \
  --as=system:serviceaccount:demo:dashboard

Liefert der erste Befehl yes und der zweite no, tut die Role oben, was sie soll.

Das zählt, weil RBAC-Fehler wie Anwendungsfehler aussehen können. Ein Controller schreibt vielleicht forbidden: User "system:serviceaccount:demo:dashboard" cannot list resource "pods". Die Lösung ist nicht, ihn zum Cluster-Admin zu machen. Die Lösung ist zu verstehen, welche Rechte er wirklich braucht.

Der Default-ServiceAccount wird leicht vergessen

Jeder Namespace hat einen default ServiceAccount. Gibt ein Pod keinen eigenen an, nutzt er diesen.

kubectl get serviceaccount -n demo
kubectl get pod web-abc123 -n demo -o jsonpath='{.spec.serviceAccountName}'; echo

Für einfache Workloads ohne Kubernetes-API ist ein ServiceAccount-Token oft gar nicht nötig. In vielen Clustern werden projizierte Tokens nur bei Bedarf genutzt oder können pro Pod deaktiviert werden:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: web
  namespace: demo
---
apiVersion: v1
kind: Pod
metadata:
  name: web
  namespace: demo
spec:
  serviceAccountName: web
  automountServiceAccountToken: false
  containers:
    - name: app
      image: nginx:1.27

Das sollte man nicht blind in Controller oder Operatoren kopieren — die brauchen oft API-Zugriff. Bei einer Webanwendung, die nie mit der Kubernetes-API spricht, ist es aber eine gute Frage.

Die bessere Gewohnheit: Workloads mit API-Bedarf bekommen eigene benannte ServiceAccounts mit nur den nötigen Rechten. Breite Rechte auf default sind riskant, weil jeder vergessene Pod sie erbt.

Secrets sind kodiert, nicht automatisch sicher

Kubernetes-Secrets werden oft missverstanden. Die Werte in einem Secret-Manifest sind base64-kodiert — nicht durch das Manifestformat verschlüsselt.

echo -n 'not-a-real-password' | base64
echo 'bm90LWEtcmVhbC1wYXNzd29yZA==' | base64 --decode; echo

Der Cluster kann Secrets im Storage verschlüsseln, wenn das konfiguriert ist. Zugriff wird per RBAC kontrolliert. Wer in einem Namespace get secrets darf, kann die Inhalte meist lesen.

Das bedeutet:

  • Secret-Lesezugriff nicht beiläufig vergeben.
  • Echte Secret-Werte nicht in Git committen.
  • Externe Secret-Manager oder versiegelte Workflows nutzen, wenn vorhanden.
  • Zugangsdaten rotieren, wenn ein Leak möglich ist.

Eine einfache Prüfung:

kubectl auth can-i get secrets -n demo
kubectl get secrets -n demo

Dürfen viele Benutzer und Workloads Secrets „für alle Fälle“ lesen, ist der Namespace weniger isoliert, als er aussieht.

Netzwerkzugriff ist eine eigene Schicht

RBAC kontrolliert Zugriff auf die Kubernetes-API. Es kontrolliert nicht, ob ein Pod einen anderen Pod über das Netzwerk erreicht.

Ein Entwickler kann keine Berechtigung zum Lesen von Secrets haben, während ein kompromittierter Anwendungspod trotzdem eine Datenbank-Service-Adresse erreicht, wenn das Netzwerk es erlaubt. Andere Schicht, andere Kontrolle.

NetworkPolicy ist das übliche Kubernetes-native Werkzeug für Pod-Netzwerkregeln — sofern das CNI sie erzwingt. Eine einfache Ingress-Policy erlaubt zum Beispiel nur Pods mit app: api, Pods mit app: database auf Port 5432 zu erreichen:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-api-to-database
  namespace: demo
spec:
  podSelector:
    matchLabels:
      app: database
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: api
      ports:
        - protocol: TCP
          port: 5432

Diese Policy ist namespaced. Der podSelector unter from wählt Pods im selben Namespace aus, solange kein namespaceSelector dazukommt. In echten Clustern wird dieses Detail schnell wichtig.

NetworkPolicy ist nicht das Erste vor Pods und Services — gehört aber zu den Grundlagen, weil Namespace-Trennung ohne Netzwerkgedanken unvollständig bleibt.

Workload-Defaults gehören zur Sicherheit

Security ist nicht nur die Frage, wer kubectl benutzen darf. Es geht auch darum, was ein Container darf, wenn er läuft.

Ein einfacher Container-securityContext kann unnötige Rechte entfernen:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: app
          image: nginx:1.27
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]

Das kann Images brechen, die ins Root-Dateisystem schreiben oder Linux Capabilities brauchen. Kein Grund, es zu ignorieren — ein Grund, das Image zu testen und zu verstehen.

Clusterweite Admission Controls können solche Muster erzwingen. In neueren Clustern kann Pod Security Admission über Namespace-Labels eingeschränkte Standards anwenden. Managed Platforms und OpenShift bringen oft eigene Policies. Am ersten Tag muss man nicht jedes Detail kennen. Wichtig: „Der Pod startet“ heißt nicht automatisch „der Pod läuft mit einem sicheren Profil“.

Eine praktische Anfangs-Checkliste

Für ein kleines Team oder einen Lerncluster würde ich mit diesen Gewohnheiten starten:

  1. Namespaces bewusst anlegen — nicht jedes Tutorial und jede App in default.
  2. Labels konsistent für Apps, Komponenten und Umgebungen nutzen.
  3. Jedem Workload mit API-Bedarf einen eigenen ServiceAccount geben.
  4. Role und RoleBinding nutzen, bevor ClusterRoleBinding ins Spiel kommt.
  5. Berechtigungen mit kubectl auth can-i testen.
  6. get secrets nur vergeben, wenn es einen klaren Grund gibt.
  7. ResourceQuotas und LimitRanges, wo geteilte Umgebungen erschöpft werden können.
  8. Prüfen, ob NetworkPolicy im eigenen Cluster wirklich erzwungen wird.
  9. Container-securityContext-Defaults vor Produktion ansehen.

Nichts davon macht einen Cluster perfekt sicher. Perfekt ist nicht das Ziel am Anfang. Das Ziel ist, den Cluster nicht wie einen flachen Raum zu betreiben, in dem jeder Workload, jeder Benutzer und jedes Secret näher beieinander liegen als gedacht.

Namespaces geben Struktur. RBAC gibt Berechtigungen. Netzwerk- und Workload-Policies verringern den Blast Radius. Zusammen wird aus „alles kann wahrscheinlich alles erreichen“ ein System, in dem Zugriff benannt, geprüft und getestet werden muss.