Zum Inhalt springen

Services, Wiki-Artikel, Blog-Beiträge und Glossar-Einträge durchsuchen

↑↓NavigierenEnterÖffnenESCSchließen
Kubernetes Security Audit: RBAC, PodSecurity, NetworkPolicy und Secrets - Container-Sicherheit und Orchestrierung
Cloud Security

Kubernetes Security Audit: RBAC, PodSecurity, NetworkPolicy und Secrets

Kubernetes-Cluster sind komplex und bieten viele Angriffsflächen: überprivilegierte ServiceAccounts, fehlende PodSecurity-Standards, offene NetworkPolicies, unverschlüsselte Secrets. Dieser Guide erklärt den Ablauf eines K8s-Security-Audits: RBAC-Analyse mit kubectl-who-can, PodSecurity Admission Controller, NetworkPolicy-Gaps, etcd-Verschlüsselung, CIS Kubernetes Benchmark und Audit-Logging.

Vincent Heinen Vincent Heinen Abteilungsleiter Offensive Services
12 Min. Lesezeit
OSCP+ OSCP OSWP OSWA

TL;DR

Ein Kubernetes-Security-Audit identifiziert systematisch Konfigurationsfehler, die Angriffsflächen in K8s-Clustern schaffen. Der Auditprozess umfasst die Analyse von RBAC-Berechtigungen, die Überprüfung von PodSecurity-Standards, die Bewertung von NetworkPolicies und die Sicherstellung der Secret-Verschlüsselung. Ein zentrales Werkzeug ist der CIS Kubernetes Benchmark, der 246 Prüfpunkte für die Sicherheit des Clusters definiert. Beispielsweise deckt die RBAC-Analyse mit Tools wie `kubectl-who-can` überprivilegierte ServiceAccounts auf, die Wildcards in ihren Berechtigungen nutzen oder als `cluster-admin` gebunden sind. Seit Kubernetes 1.25 stabilisiert, erzwingt der PodSecurity Admission Controller auf Namespace-Ebene Sicherheitsstandards wie `runAsNonRoot: true` oder das Verbot von `hostPath` Volumes, um Pods vor Privilege Escalation zu schützen.

Diese Zusammenfassung wurde KI-gestützt erstellt (EU AI Act Art. 52).

Inhaltsverzeichnis (6 Abschnitte)

Kubernetes ist das Schweizer Taschenmesser der Container-Orchestrierung - und sein Sicherheitsmodell ist entsprechend komplex. Standardmäßig sind viele Kubernetes-Cluster zu permissiv konfiguriert: ServiceAccounts können Cluster-Resourcen lesen, Pods laufen als root, NetworkPolicies fehlen und Secrets sind Base64-kodiert aber nicht verschlüsselt. Ein Kubernetes-Security-Audit folgt einem strukturierten Prozess und deckt systematisch diese Konfigurationsfehler auf.

Kubernetes Security Audit - Scope und Methodik

CIS Kubernetes Benchmark (CIS-K8s):
  → Center for Internet Security: 246 Prüfpunkte für Kubernetes
  → Kategorien: API Server, Controller Manager, Scheduler, etcd, Nodes, Policies
  → Level 1: Grundsicherheit (ohne Breaking Changes)
  → Level 2: Erhöhte Sicherheit (einige Einschränkungen)

Automatisierter CIS-Scan:
  # kube-bench (Aqua Security):
  kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml
  kubectl logs job/kube-bench

  Ausgabe:
  [INFO] 1 Master Node Security Configuration
  [PASS] 1.2.1 Ensure that the --anonymous-auth argument is set to false
  [FAIL] 1.2.9 Ensure that the --profiling argument is set to false
  [WARN] 1.2.16 Ensure that the admission control plugin AlwaysPullImages is set

  # Einzelner Node:
  kube-bench run --targets master
  kube-bench run --targets node

RBAC Analyse - wer darf was?:
  # Alle ClusterRoleBindings anzeigen:
  kubectl get clusterrolebindings -o wide | grep -v "system:"

  # Wer hat cluster-admin?
  kubectl get clusterrolebindings -o json | \
    jq '.items[] | select(.roleRef.name=="cluster-admin") | .subjects'

  # kubectl-who-can (Plugin):
  kubectl who-can create pods -n default
  kubectl who-can delete secrets --all-namespaces
  kubectl who-can get secrets -n kube-system

  # Rogue ServiceAccount finden (hat zu viele Rechte):
  kubectl auth can-i --list --as=system:serviceaccount:default:default
  # → Liste aller erlaubten Aktionen für default ServiceAccount

  # RBAC-Audit Tool:
  kubectl-rbac-tool who-can get secrets
  # npm install -g krane  → grafische RBAC-Analyse

RBAC-Fehlkonfigurationen

Häufig gefundene RBAC-Probleme:

1. Default ServiceAccount hat zu viele Rechte:
  # Prüfen:
  kubectl auth can-i --list --as=system:serviceaccount:default:default

  # Problem: Default SA kann Secrets lesen:
  # apiVersion: rbac.authorization.k8s.io/v1
  # kind: ClusterRoleBinding
  # subjects:
  # - kind: ServiceAccount
  #   name: default
  #   namespace: default

  # Fix: Automounting deaktivieren wenn SA nicht benötigt:
  apiVersion: v1
  kind: ServiceAccount
  metadata:
    name: my-app
  automountServiceAccountToken: false  # Kein Token im Pod!

  # Oder: Pod-Level:
  spec:
    automountServiceAccountToken: false

2. Wildcards in RBAC (gefährlich!):
  # FALSCH - zu weit:
  rules:
  - apiGroups: ["*"]      # Alle API Groups
    resources: ["*"]       # Alle Resources
    verbs: ["*"]           # Alle Verben

  # RICHTIG - minimal:
  rules:
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "watch"]

3. Privileged ServiceAccount für Anwendungen:
  # Prüfen: welche SAs haben privilegierte Bindings?
  kubectl get rolebindings,clusterrolebindings --all-namespaces -o json | \
    jq '.items[] | select(.roleRef.name | test("admin|cluster-admin|edit")) |
    {name: .metadata.name, namespace: .metadata.namespace, roleRef: .roleRef.name}'

4. Escape-Path: Pod kann ServiceAccount-Token stehlen:
  # Wenn Pod mit high-privileged SA läuft:
  # Im Pod:
  cat /var/run/secrets/kubernetes.io/serviceaccount/token
  # → Token kann für kubectl-Befehle genutzt werden!

  # Test: welche Aktionen sind mit SA-Token möglich?
  TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
  kubectl --token="$TOKEN" auth can-i --list

Audit-Checkliste RBAC:
  □ Kein Wildcards in Production RBAC
  □ Default ServiceAccount: automountServiceAccountToken=false
  □ Keine cluster-admin Bindings für Anwendungen
  □ Minimale Berechtigungen per Namespace getrennt
  □ Regelmäßige RBAC-Review (quartalsweise)
  □ Externe SA-Berechtigungen (CI/CD, Monitoring) minimiert

PodSecurity Admission Controller

PodSecurity Admission (PSA) - seit K8s 1.25 stable, OPA-Policies deprecated:

3 Security-Level:
  privileged:  Keine Einschränkungen (für System-Pods)
  baseline:    Verhindert kritische Privilege-Escalation (empfohlen als Start)
  restricted:  Strengste Sicherheit (für Produktions-Workloads)

PSA Labels auf Namespace:
  # Namespace-Level Enforcement:
  kubectl label namespace production \
    pod-security.kubernetes.io/enforce=restricted \
    pod-security.kubernetes.io/enforce-version=v1.29

  # Warn + Audit ohne Enforcement (zum Testen):
  kubectl label namespace staging \
    pod-security.kubernetes.io/warn=restricted \
    pod-security.kubernetes.io/audit=restricted

Restricted Profile - was wird erzwungen?:
  □ runAsNonRoot: true (kein root!)
  □ runAsUser: > 0 (nicht UID 0)
  □ allowPrivilegeEscalation: false
  □ readOnlyRootFilesystem: true (empfohlen)
  □ seccompProfile: RuntimeDefault oder localhost
  □ capabilities: DROP ALL
  □ hostProcess: false
  □ hostPath Volumes: verboten

Konformer Pod (restricted):
  apiVersion: v1
  kind: Pod
  spec:
    securityContext:
      runAsNonRoot: true
      runAsUser: 1000
      seccompProfile:
        type: RuntimeDefault
    containers:
    - name: app
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop: ["ALL"]
      # Temp-Verzeichnis wenn readOnly:
      volumeMounts:
      - name: tmp
        mountPath: /tmp
    volumes:
    - name: tmp
      emptyDir: {}

Cluster-weites Default-Policy (AdmissionConfiguration):
  # /etc/kubernetes/admission-config.yaml
  apiVersion: apiserver.config.k8s.io/v1
  kind: AdmissionConfiguration
  plugins:
  - name: PodSecurity
    configuration:
      apiVersion: pod-security.admission.config.k8s.io/v1
      kind: PodSecurityConfiguration
      defaults:
        enforce: "restricted"
        enforce-version: "latest"
        audit: "restricted"
        audit-version: "latest"
        warn: "restricted"
        warn-version: "latest"
      exemptions:
        namespaces: ["kube-system", "monitoring"]  # System-Namespaces ausgenommen

Audit: existierende Pods prüfen:
  # Wer würde restricted-Policy verletzen?
  kubectl get pods --all-namespaces -o json | \
    jq '.items[] | select(.spec.containers[].securityContext.privileged == true) |
    {name: .metadata.name, namespace: .metadata.namespace}'

  # Privileged Pods:
  kubectl get pods --all-namespaces -o jsonpath=\
    '{range .items[?(@.spec.containers[*].securityContext.privileged)]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}'

NetworkPolicy - Netzwerksegmentierung

NetworkPolicy - Default Deny ist Pflicht!

Problem ohne NetworkPolicy:
  → Alle Pods können mit allen Pods in allen Namespaces kommunizieren!
  → Lateral Movement: kompromittierter Pod erreicht Datenbank, andere Services

Default Deny AllIngress + AllEgress:
  # Erst: alles blockieren
  apiVersion: networking.k8s.io/v1
  kind: NetworkPolicy
  metadata:
    name: default-deny-all
    namespace: production
  spec:
    podSelector: {}  # Gilt für ALLE Pods in Namespace
    policyTypes:
    - Ingress
    - Egress

  # Dann: erlaubte Verbindungen explizit freischalten

Erlauben: Frontend → Backend:
  apiVersion: networking.k8s.io/v1
  kind: NetworkPolicy
  metadata:
    name: allow-frontend-to-backend
    namespace: production
  spec:
    podSelector:
      matchLabels:
        app: backend
    policyTypes:
    - Ingress
    ingress:
    - from:
      - podSelector:
          matchLabels:
            app: frontend
      ports:
      - protocol: TCP
        port: 8080

Erlauben: Backend → Datenbank (anderer Namespace):
  apiVersion: networking.k8s.io/v1
  kind: NetworkPolicy
  metadata:
    name: allow-backend-to-db
    namespace: database
  spec:
    podSelector:
      matchLabels:
        app: postgres
    ingress:
    - from:
      - namespaceSelector:
          matchLabels:
            name: production
        podSelector:
          matchLabels:
            app: backend
      ports:
      - port: 5432

Erlauben: DNS (Pflicht!):
  apiVersion: networking.k8s.io/v1
  kind: NetworkPolicy
  metadata:
    name: allow-dns
    namespace: production
  spec:
    podSelector: {}
    egress:
    - ports:
      - protocol: UDP
        port: 53

NetworkPolicy Audit:
  # Namespaces ohne NetworkPolicy:
  kubectl get namespaces | while read ns; do
    count=$(kubectl get networkpolicy -n "$ns" 2>/dev/null | wc -l)
    if [ "$count" -le "1" ]; then
      echo "KEIN NetworkPolicy: $ns"
    fi
  done

  # Tool: network-policy-viewer
  kubectl neat | npx @lbogdan/kube-network-policies-visualizer
  # → Grafische Darstellung der NetworkPolicy-Topologie

Secrets Management in Kubernetes

K8s Secrets - Base64 ist KEINE Verschlüsselung!

Standardproblem:
  kubectl get secret db-password -o jsonpath='{.data.password}' | base64 -d
  # → Passwort im Klartext! Base64 ist nur Encoding, kein Schutz!

etcd-Verschlüsselung aktivieren (EncryptionConfiguration):
  # /etc/kubernetes/encryption-config.yaml
  apiVersion: apiserver.config.k8s.io/v1
  kind: EncryptionConfiguration
  resources:
  - resources: ["secrets"]
    providers:
    - aescbc:       # AES-CBC mit 256-bit Key
        keys:
        - name: key1
          secret: <base64-encoded-32-byte-key>
    - identity: {}  # Fallback für unverschlüsselte (Migration!)

  # kube-apiserver Flag:
  --encryption-provider-config=/etc/kubernetes/encryption-config.yaml

  # Prüfen: Secret im etcd verschlüsselt?
  ETCDCTL_API=3 etcdctl get /registry/secrets/default/db-password \
    --endpoints=https://127.0.0.1:2379 \
    --cacert=/etc/kubernetes/pki/etcd/ca.crt \
    --cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt \
    --key=/etc/kubernetes/pki/etcd/healthcheck-client.key | strings
  # Wenn verschlüsselt: sollte unlesbares Binary zeigen

Externe Secret Manager (Best Practice):
  Option 1: HashiCorp Vault Agent Injector:
    annotations:
      vault.hashicorp.com/agent-inject: "true"
      vault.hashicorp.com/role: "my-role"
      vault.hashicorp.com/agent-inject-secret-db-pass: "secret/data/db"
    # Vault Agent Sidecar liest Secret + schreibt als Datei in Pod

  Option 2: External Secrets Operator (ESO):
    # ExternalSecret: AWS Secrets Manager → K8s Secret
    apiVersion: external-secrets.io/v1beta1
    kind: ExternalSecret
    metadata:
      name: db-secret
    spec:
      refreshInterval: 1h
      secretStoreRef:
        name: aws-secrets-manager
        kind: SecretStore
      target:
        name: db-password
      data:
      - secretKey: password
        remoteRef:
          key: prod/db/password
          property: password
    # Secret wird automatisch im K8s erstellt und täglich aktualisiert!

  Option 3: CSI Secrets Store:
    # Secrets als Volume in Pod mounten (direkt aus Vault/AWS/Azure)
    volumes:
    - name: secrets-store
      csi:
        driver: secrets-store.csi.k8s.io
        readOnly: true
        volumeAttributes:
          secretProviderClass: aws-secrets
    # Vorteil: Secret nie als K8s Secret gespeichert!

Secret-Audit Checkliste:
  □ etcd-Verschlüsselung: EncryptionConfiguration aktiviert
  □ Kein Klartext in Deployments/ConfigMaps (Secret-Scanning im Repo!)
  □ RBAC: Secrets nur für notwendige ServiceAccounts lesbar
  □ Rotation: Secrets regelmäßig rotiert (Vault Dynamic Secrets ideal)
  □ Audit Logging: Secret-Zugriffe geloggt (K8s Audit Log)

Kubernetes Audit Logging

Kubernetes Audit Log - alle API-Anfragen protokolliert:

Audit Policy (Minimalempfehlung):
  # /etc/kubernetes/audit-policy.yaml
  apiVersion: audit.k8s.io/v1
  kind: Policy
  rules:
  # Keine Logs für Read-Only Requests auf non-sensitive Resources:
  - level: None
    verbs: ["get", "list", "watch"]
    resources:
    - group: ""
      resources: ["events"]

  # Secrets: Metadata + Request Body loggen:
  - level: RequestResponse
    resources:
    - group: ""
      resources: ["secrets", "configmaps"]

  # Privileged Operations: vollständig loggen:
  - level: RequestResponse
    verbs: ["create", "update", "patch", "delete"]
    resources:
    - group: "rbac.authorization.k8s.io"
      resources: ["clusterroles", "clusterrolebindings"]

  # Alles andere: Metadata (ohne Body):
  - level: Metadata

  kube-apiserver Flags:
    --audit-log-path=/var/log/kubernetes/audit.log
    --audit-policy-file=/etc/kubernetes/audit-policy.yaml
    --audit-log-maxage=30     # Tage aufbewahren
    --audit-log-maxbackup=3   # Backup-Dateien
    --audit-log-maxsize=100   # MB pro Datei

Audit Log analysieren (Sentinel KQL äquivalent):
  # Verdächtige: Secrets aus anderen Namespaces gelesen:
  cat /var/log/kubernetes/audit.log | jq '
    select(.verb == "get" and
           .objectRef.resource == "secrets" and
           .user.username | test("system:serviceaccount") | not)
    | {time: .stageTimestamp, user: .user.username,
       namespace: .objectRef.namespace, name: .objectRef.name}'

  # Neue ClusterRoleBinding:
  cat /var/log/kubernetes/audit.log | jq '
    select(.verb == "create" and
           .objectRef.resource == "clusterrolebindings")
    | {time: .stageTimestamp, user: .user.username, name: .objectRef.name}'

Falco - Echtzeit Runtime Security:
  # Falco erkennt verdächtiges Verhalten zur Laufzeit:
  - Container spawnt Shell: kubectl exec → Bash → ALERT
  - Datei in /etc/ geschrieben: ALERT
  - Privileged Container gestartet: ALERT
  - Netzwerkverbindung aus Container nach extern: ALERT

  Installation:
    helm repo add falcosecurity https://falcosecurity.github.io/charts
    helm install falco falcosecurity/falco \
      --namespace falco-system \
      --set falco.grpc.enabled=true \
      --set falcosidekick.enabled=true  # Alerts zu Slack/PagerDuty

Kubernetes-Cluster sind hochkomplex und bieten viele Angriffsflächen, die ohne strukturiertes Audit oft übersehen werden. AWARE7 prüft Kubernetes-Deployments nach CIS Kubernetes Benchmark und OWASP Kubernetes Security Testing Guide - von RBAC-Analyse über PodSecurity bis zu etcd-Verschlüsselung.

Cloud-Security Pentest anfragen | Penetrationstest Cloud-Security

Nächster Schritt

Unsere zertifizierten Sicherheitsexperten beraten Sie zu den Themen aus diesem Artikel — unverbindlich und kostenlos.

Kostenlos · 30 Minuten · Unverbindlich

Artikel teilen

Über den Autor

Vincent Heinen
Vincent Heinen

Abteilungsleiter Offensive Services

M.Sc. IT-Sicherheit mit über 5 Jahren Erfahrung in offensiver Sicherheitsanalyse. Leitet die Durchführung von Penetrationstests mit Spezialisierung auf Web-Applikationen, Netzwerk-Infrastruktur, Reverse Engineering und Hardware-Sicherheit. Verantwortlich für mehrere Responsible Disclosures.

OSCP+ OSCP OSWP OSWA
Zertifiziert ISO 27001ISO 9001AZAVBSI

Cookielose Analyse via Matomo (selbst gehostet, kein Tracking-Cookie). Datenschutzerklärung