Container-Sicherheit und Kubernetes Hardening: Der vollständige Guide
Container- und Kubernetes-Sicherheit von Grund auf: Das 4C-Modell, Docker-Image-Hardening (non-root, distroless, multi-stage), Container-Scanning mit Trivy und Grype, Kubernetes RBAC, Pod Security Standards (restricted), NetworkPolicy (deny-all + Allowlist), Secrets Management mit External Secrets Operator und Vault, Runtime-Security mit Falco und eBPF, Serverless Security, Supply Chain Security mit Cosign/SLSA, CI/CD-Pipeline und Cloud-Native Security Maturity Model.
Inhaltsverzeichnis (14 Abschnitte)
Container haben die Art und Weise, wie Anwendungen deployed werden, revolutioniert - und gleichzeitig neue Angriffsflächen geschaffen. Von unsicheren Images über überprivilegierte Pods bis hin zu Container-Escape: ein falsch konfigurierter Kubernetes-Cluster ermöglicht Ausbrüche aus Containern, laterale Bewegung zu anderen Pods und vollständige Cluster-Übernahme. Dieser Guide zeigt, wie Container-Sicherheit und Kubernetes wirklich gehärtet werden.
Das 4C-Modell der Cloud-Native Security
Cloud (Infrastruktur-Sicherheit)
└── Cluster (Kubernetes-Sicherheit)
└── Container (Image- und Laufzeit-Sicherheit)
└── Code (Anwendungssicherheit)
Jede Schicht schützt die darunter liegende.
Eine Schwachstelle in der Cloud-Schicht gefährdet alles.
Das 4C-Modell stammt aus dem CNCF Cloud Native Security Whitepaper und definiert vier Sicherheitsebenen:
- Cloud - Infrastruktur, Cloud Provider IAM, VPC-Design
- Cluster - Kubernetes selbst: RBAC, API-Server-Härtung, etcd-Verschlüsselung
- Container - Images, Runtime-Security, Capabilities
- Code - Anwendungssicherheit, Dependencies, Secrets im Code
Kubernetes Threat-Landschaft
OWASP Kubernetes Top 10 - häufigste Angriffsvektoren:
- Kompromittierter Container → Escape zum Host
- Schwache RBAC → unberechtigter API-Server-Zugriff
- Unverschlüsselte Secrets → Credentials im Klartext in etcd
- Fehlende Network Policies → laterale Bewegung zwischen Pods
- Unsichere Images → bekannte CVEs in Container-Images
- Privilegierte Container → Host-Zugriff aus Container
- Misconfigured ServiceAccounts → automatisch gemountete Tokens
- Unsicheres Kubernetes Dashboard ohne Authentifizierung
- Fehlende Audit Logs → keine Forensik möglich
- Überprivilegierte Cloud-IAM-Rollen für Worker Nodes (z.B. EC2)
Container-Image-Sicherheit
Sichere Dockerfiles
# Multi-stage build: Build-Tools bleiben aus dem Produktions-Image heraus
# Go-Beispiel: Minimalstes Image (FROM scratch)
FROM golang:1.22-alpine AS builder
WORKDIR /build
COPY go.sum go.mod ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app ./cmd/server
FROM scratch # LEER - nur Binary + CA-Certs
COPY --from=builder /app /app
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
USER 65534:65534 # nobody
ENTRYPOINT ["/app"]
# Python-Beispiel: Distroless Image (kein Shell, kein Paketmanager)
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
COPY . .
FROM gcr.io/distroless/python3-debian12
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY --from=builder /app .
USER nonroot:nonroot # Nicht root!
CMD ["app.py"]
# Java-Beispiel: JRE statt JDK, kein Maven in Production
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
FROM eclipse-temurin:21-jre-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
USER appuser
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
# Node-Beispiel: Non-Root User
FROM node:20-alpine
WORKDIR /app
COPY package*.json .
RUN npm ci --production && \
addgroup -S appgroup && \
adduser -S appuser -G appgroup
COPY . .
USER appuser
EXPOSE 3000
CMD ["node", "server.js"]
Was Distroless Images bieten:
- Kein Shell (
bash,sh) - Kein Paketmanager (
apt,apk) - Kein
curl,wget - Minimale Angriffsfläche - Angreifer kann nach Eindringen kaum etwas ausführen
Container-Image-Scanning
# Trivy (Aqua Security, kostenlos - empfohlen):
trivy image --severity HIGH,CRITICAL nginx:latest
trivy image --severity CRITICAL,HIGH python:3.12
# Dockerfile auf Fehlkonfigurationen prüfen:
trivy config --severity HIGH,CRITICAL Dockerfile
# Kubernetes-Manifeste prüfen:
trivy config --severity HIGH,CRITICAL ./k8s/
# CI/CD Integration (GitHub Actions):
# - name: Trivy Image Scan
# uses: aquasecurity/trivy-action@master
# with:
# image-ref: 'myapp:${{ github.sha }}'
# format: 'sarif'
# output: 'trivy-results.sarif'
# severity: 'CRITICAL,HIGH'
# exit-code: '1' # Pipeline schlägt fehl bei Findings!
# GitLab CI Integration:
# scan_image:
# stage: security
# image: aquasec/trivy:latest
# script:
# - trivy image --exit-code 1 --severity HIGH,CRITICAL
# $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
# allow_failure: false
# Grype (Anchore):
grype myapp:latest
grype myapp:latest --only-fixed # Nur patchbare Schwachstellen
# Snyk:
snyk container test myapp:v1.2.3
Image-Policy-Regeln:
- Keine Images mit HIGH/CRITICAL CVEs in Produktion
- Base-Images wöchentlich rebuilden (frische Patches)
- Private Registry (Harbor) statt direktes Docker Hub
- Digest-Pinning:
image: nginx:1.25-alpine@sha256:a8560b36...(Tag kann neu gebaut werden, Digest nicht)
Supply Chain Security
Image-Signierung mit Cosign (Sigstore)
# Cosign - Image-Signierung (SLSA Level 2+)
# 1. Keypair generieren
cosign generate-key-pair
# 2. Image nach dem Build signieren
cosign sign --key cosign.key \
registry.example.com/myapp:1.2.3
# 3. Verifizierung (in CI/CD oder Admission Controller)
cosign verify --key cosign.pub \
registry.example.com/myapp:1.2.3
# Keyless Signing mit OIDC (GitHub Actions)
cosign sign \
--oidc-issuer=https://token.actions.githubusercontent.com \
--identity-token="${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \
registry.example.com/myapp:${{ github.sha }}
# SBOM (Software Bill of Materials) generieren und attachen
syft registry.example.com/myapp:1.2.3 \
-o spdx-json > sbom.json
cosign attach sbom --sbom sbom.json \
registry.example.com/myapp:1.2.3
# SBOM auf Schwachstellen prüfen
grype sbom:sbom.json --fail-on high
Kubernetes Cluster Hardening
Das Kubernetes-Sicherheitsmodell
CIS Kubernetes Benchmark v1.9 (2024):
Über 100 Checks für Control Plane, Worker Nodes, Policies
kube-bench: automatisiertes Benchmark-Testing
kube-bench ausführen:
kubectl apply -f \
https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml
kubectl logs -l app=kube-bench
Wichtige CIS-Kontrollen:
1.2.1 --anonymous-auth=false (API Server)
1.2.6 --authorization-mode=Node,RBAC (nie AlwaysAllow!)
4.2.1 --anonymous-auth=false (Kubelet)
5.1.1 RBAC: nicht cluster-admin vergeben
Häufige kube-bench FAIL-Punkte beheben:
Anonymous Auth deaktivieren:
--anonymous-auth=false
RBAC aktivieren:
--authorization-mode=Node,RBAC
Audit Logging aktivieren:
--audit-log-path=/var/log/kubernetes/audit.log
--audit-log-maxage=30
--audit-log-maxbackup=10
--audit-log-maxsize=100
RBAC - Rollenbasierte Zugriffskontrolle
# Kubernetes RBAC-Konzepte:
# Role/ClusterRole: Was darf man tun? (Verbs auf Resources)
# RoleBinding: Wer bekommt eine Role? (in einem Namespace)
# ClusterRoleBinding: Wer bekommt eine ClusterRole? (clusterweit)
# GEFÄHRLICH - Anti-Pattern: Vollzugriff für alle Service Accounts
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: terrible-idea
subjects:
- kind: Group
name: system:serviceaccounts # ALLE Service Accounts!
roleRef:
kind: ClusterRole
name: cluster-admin # Vollständiger Cluster-Admin!
---
# KORREKT - Least Privilege: Dedizierter ServiceAccount pro App
apiVersion: v1
kind: ServiceAccount
metadata:
name: myapp-sa
namespace: production
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: myapp-role
namespace: production
rules:
# Nur was die App wirklich braucht:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
resourceNames: ["myapp-config"] # Nur eigene ConfigMap!
# Kein: secrets (braucht External Secrets Operator!)
# Kein: pods/exec (Container-Shell!)
# Kein: cluster-scoped resources
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: myapp-rolebinding
namespace: production
subjects:
- kind: ServiceAccount
name: myapp-sa
namespace: production
roleRef:
kind: Role
name: myapp-role
apiGroup: rbac.authorization.k8s.io
Gefährliche RBAC-Berechtigungen - niemals vergeben:
cluster-adminfür Anwendungs-ServiceAccounts- Wildcards:
verbs: ["*"],resources: ["*"] secrets: ["get", "list"](alle Secrets lesen!)pods/exec(Shell in beliebige Pods!)nodes: ["*"](Node-Übernahme!)
# RBAC-Audit:
kubectl auth can-i create pods --namespace=production
kubectl auth can-i '*' '*' --all-namespaces # Cluster-Admin-Check
# Alle Bindings für einen Service Account:
kubectl get rolebindings,clusterrolebindings -A \
-o json | jq '.items[] | select(.subjects[]?.name=="myapp-sa")'
# rbac-lookup (Visualization):
kubectl-rbac-lookup myapp-sa -k serviceaccount -n production
Pod Security Standards (PSS)
# Namespace mit "restricted" Policy labeln (seit Kubernetes 1.25):
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/audit: restricted
# Alternativ via kubectl:
# kubectl label namespace production \
# pod-security.kubernetes.io/enforce=restricted
Pod Security Profile:
privileged: Alles erlaubt (nur für System-Workloads!)
baseline: Grundlegende Sicherheit (verhindert Eskalation)
restricted: Maximal sicher (empfohlen für Anwendungen)
Restricted verhindert:
Privileged containers
hostNetwork, hostPID, hostIPC
hostPath volumes
PrivilegeEscalation
Root-User
Nicht-sichere Capabilities
# Restricted-konformer Pod (vollständiges Beispiel):
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: production
spec:
template:
spec:
serviceAccountName: myapp-sa # Dedizierter SA!
automountServiceAccountToken: false # Kein Token wenn nicht nötig!
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault # Syscall-Filterung aktiv!
containers:
- name: myapp
image: myregistry/myapp:v1.2.3@sha256:abc123 # Digest-pinning!
securityContext:
allowPrivilegeEscalation: false # Kein sudo/setuid!
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"] # Alle Linux-Capabilities entfernen!
# add: ["NET_BIND_SERVICE"] # Nur wenn Port < 1024 nötig
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m" # Kein unbegrenzter CPU!
volumeMounts:
- name: tmp
mountPath: /tmp # Schreibbares /tmp trotz readOnlyRootFilesystem
volumes:
- name: tmp
emptyDir: {}
NetworkPolicy und Microsegmentierung
Standard-Kubernetes: Alle Pods können alle Pods erreichen!
NetworkPolicy ist ein kritisches Härtungsmittel.
Voraussetzung: CNI mit NetworkPolicy-Support
(Calico, Cilium, Weave Net - NICHT: Flannel ohne Calico)
# 1. Default-Deny-Policy: erst alles blockieren, dann erlauben
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {} # Alle Pods im Namespace
policyTypes:
- Ingress
- Egress
---
# 2. Erlaubt: Ingress vom Ingress-Controller
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-controller
namespace: production
spec:
podSelector:
matchLabels:
app: myapp
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
podSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
ports:
- protocol: TCP
port: 3000
---
# 3. Erlaubt: Frontend zu Backend (nur spezifische Pods)
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
---
# 4. Erlaubt: Egress zur DB und DNS
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-db-egress
namespace: production
spec:
podSelector:
matchLabels:
app: myapp
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: postgresql
ports:
- protocol: TCP
port: 5432
# DNS immer erlauben!
- ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
Erweitert mit Cilium (DNS-basiertes Filtering):
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: restrict-egress-to-fqdn
spec:
endpointSelector:
matchLabels:
app: backend
egress:
- toFQDNs:
- matchName: "api.extern.de" # Nur diese Domain erlaubt!
toPorts:
- ports:
- port: "443"
protocol: TCP
Secrets Management in Kubernetes
Problem: Kubernetes Secrets sind standardmäßig nur Base64-kodiert
(= nicht verschlüsselt!) in etcd gespeichert.
Wer etcd lesen kann, kennt alle Secrets.
Secrets in Git (häufigster Fehler): niemals!
etcd-Verschlüsselung aktivieren
# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key> # openssl rand -base64 32
- identity: {} # Fallback für unverschlüsselte Secrets (Migration)
# kube-apiserver starten mit:
# --encryption-provider-config=/etc/kubernetes/encryption-config.yaml
# Bestehende Secrets re-encrypten:
# kubectl get secrets -A -o json | kubectl replace -f -
# Überprüfung: Ist etcd verschlüsselt?
etcdctl get /registry/secrets/default/my-secret --print-value-only \
| strings | head -5
# Wenn Plaintext → nicht verschlüsselt!
# Mit Encryption: Ausgabe beginnt mit "k8s:enc:..."
External Secrets Operator (empfohlen)
# Secrets in Vault/AWS SSM - nie direkt in K8s
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: myapp-secrets
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: myapp-secrets # Erstellt K8s Secret automatisch!
creationPolicy: Owner
data:
- secretKey: db-password # K8s Secret Key
remoteRef:
key: production/myapp/database # Vault Pfad
property: password
---
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "https://vault.company.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "external-secrets" # Vault Role mit Least Privilege
Sealed Secrets (Bitnami) - GitOps-freundlich: Secrets werden asymmetrisch verschlüsselt in Git gespeichert. Der Controller im Cluster entschlüsselt. Schlüssel-Rotation möglich.
Admission Controller und Policy Enforcement
# Kyverno Policy: Kein latest-Tag erlaubt
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-tag
spec:
validationFailureAction: enforce
rules:
- name: require-image-tag
match:
resources:
kinds: ["Pod"]
validate:
message: "Image tag ':latest' ist verboten. Nutze einen spezifischen Tag."
pattern:
spec:
containers:
- image: "!*:latest"
---
# Kyverno: Root-User verboten
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-root-user
spec:
validationFailureAction: enforce
rules:
- name: check-runAsNonRoot
match:
resources:
kinds: ["Pod"]
validate:
message: "Container dürfen nicht als root laufen."
pattern:
spec:
containers:
- (securityContext):
runAsNonRoot: true
---
# OPA Gatekeeper: Nur Registry-interne Images erlaubt
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
name: allowed-repos
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
repos:
- "registry.intern.firma.de/"
- "registry.k8s.io/"
# Docker Hub Images (docker.io/) werden abgelehnt!
Runtime Security mit Falco und eBPF
Falco - eBPF-basierte Laufzeit-Sicherheit
# Installation via Helm:
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm install falco falcosecurity/falco \
--namespace falco \
--set driver.kind=ebpf \
--set falcosidekick.enabled=true \
--set falcosidekick.config.slack.webhookurl=<WEBHOOK>
# Was Falco erkennt:
# Shell in Container geöffnet (pod exec)
# Privileg-Eskalation versucht
# Sensitive Dateien gelesen (/etc/shadow, /etc/passwd)
# Ungewöhnliche Netzwerkverbindungen von Containers
# Malware-ähnliches Verhalten (Crypto-Mining, Reverse Shell)
# Wichtige Default-Rules (bereits in Falco eingebaut):
- rule: "Terminal shell in container"
desc: "A shell was used as entrypoint/exec in a container"
condition: >
evt.type = execve and
container and
proc.name in (shell_binaries)
- rule: "Write below binary dir"
desc: "An attempt to write to any file below a set of binary directories"
condition: >
bin_dir and evt.dir = < and
not user_known_write_below_binary_dir_activities
# Custom Rule: Crypto-Mining erkennen
- rule: "Crypto Mining Activity"
desc: "Detected potential crypto mining"
condition: >
evt.type = connect and
fd.sport in (3333, 4444, 8888, 14444, 45700) and
container
output: >
Crypto mining (connection=%fd.name pid=%proc.pid container=%container.id)
priority: CRITICAL
# Falco-Alerts nach Slack/PagerDuty (falcosidekick Helm values):
# falcosidekick:
# config:
# slack:
# webhookurl: "https://hooks.slack.com/services/..."
# minimumpriority: "warning"
# pagerduty:
# routingkey: "<ROUTING-KEY>"
# minimumpriority: "critical"
Tetragon und Cilium (eBPF)
Moderne Laufzeit-Sicherheitstools nutzen eBPF:
Tetragon (Cilium):
Process, File, Network Events
Real-time Enforcement (nicht nur Alerting)
K8s-native (aware von Namespace, Pod, ServiceAccount)
Alert: wenn /etc/shadow gelesen wird
Alert: wenn kubectl im Container ausgeführt wird
Kill: wenn Crypto-Miner-Verhalten erkannt wird
Cilium (Network Policy mit eBPF):
L7-Awareness: HTTP-Method, DNS-basiertes Filtering
Wireguard-Verschlüsselung zwischen Nodes
Hubble: Network Observability
Service Mesh für Zero Trust
# Istio PeerAuthentication: mTLS Pflicht (alle Pods müssen sich authentifizieren)
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICT
Serverless Security
Serverless (AWS Lambda, Azure Functions, Google Cloud Functions)
hat eigene Risiken:
Angriffsvektoren:
Event-Injection: Angreifer kontrolliert Input-Event (S3, SQS, etc.)
Dependency Vulnerabilities: npm/pip-Pakete ohne EDR-Schutz
Overly Permissive IAM Roles
Secrets in Environment Variables
Denial-of-Wallet (kostspielige Loops oder rekursive Aufrufe)
Best Practices:
Lambda Execution Role: nur minimal permissions
Secrets: AWS Secrets Manager / Parameter Store (nicht Env Vars)
Function Timeout setzen (verhindert Denial-of-Wallet)
Dead Letter Queue für Fehler-Handling
VPC-Placement wenn interne Ressourcen (RDS, ElastiCache)
Input Validation: event['key'] niemals direkt in SQL/OS-Command
# AWS Lambda - sichere Struktur
import re, boto3
def handler(event, context):
# Validierung ZUERST
bucket = event.get('bucket', '')
if not re.match(r'^[a-z0-9-]+$', bucket):
raise ValueError("Invalid bucket name")
# Secrets aus SSM, nicht aus Env Vars
ssm = boto3.client('ssm')
api_key = ssm.get_parameter(
Name='/myapp/api_key', WithDecryption=True
)['Parameter']['Value']
# ...
Security im CI/CD-Pipeline
# Vollständige Sicherheits-Pipeline (GitHub Actions)
name: Secure Build Pipeline
on: [push]
jobs:
sast:
name: Static Analysis
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Semgrep SAST
uses: returntocorp/semgrep-action@v1
with:
config: p/owasp-top-ten
sca:
name: Dependency Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: pip-audit
run: pip-audit -r requirements.txt
container-scan:
name: Container Image Scan
needs: [sast, sca]
runs-on: ubuntu-latest
steps:
- name: Build Image
run: docker build -t myapp:${{ github.sha }} .
- name: Trivy Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
severity: CRITICAL,HIGH
exit-code: 1
- name: Sign Image
run: |
cosign sign --key env://COSIGN_KEY \
registry.example.com/myapp:${{ github.sha }}
env:
COSIGN_KEY: ${{ secrets.COSIGN_KEY }}
dast:
name: Dynamic Analysis (Staging)
needs: container-scan
runs-on: ubuntu-latest
steps:
- name: Deploy to Staging
run: helm upgrade --install myapp ./chart \
--set tag=${{ github.sha }}
- name: OWASP ZAP Scan
uses: zaproxy/action-full-scan@v0.10.0
with:
target: https://staging.example.com
Cloud-Native Security Maturity Model
Level 0 - Blind:
Keine Container-Scans
Default Kubernetes-Config
Root-Container überall
Level 1 - Basic:
Image Scanning (Trivy/Snyk)
Non-root Container
RBAC aktiviert (aber broad)
Level 2 - Managed:
Supply Chain Signing (Cosign)
Pod Security Standards (restricted)
NetworkPolicy für alle Namespaces
Falco Laufzeit-Monitoring
Level 3 - Advanced:
SLSA Level 3 (Build-Herkunft verifiziert)
eBPF-basiertes Laufzeit-Enforcement (Tetragon)
Service Mesh mit mTLS (Istio/Linkerd)
GitOps mit Policy-as-Code (OPA, Kyverno)
Automatisches Remediation
Level 4 - Optimized:
Kontinuierliche Compliance (CSPM + Policy)
Chaos Engineering für Security
Red Team Exercises in Cloud-Umgebung
Security-Checkliste für Kubernetes
API-Server:
--anonymous-auth=false
--authorization-mode=Node,RBAC (nie AlwaysAllow!)
--audit-log-path konfiguriert
--encryption-provider-config für Secrets
--tls-cipher-suites auf sichere Ciphers gesetzt
Worker Nodes:
kubelet --anonymous-auth=false
kubelet --authorization-mode=Webhook
kubelet --read-only-port=0 (schließt Port 10255!)
Kernel-Updates aktuell
RBAC:
Kein cluster-admin für normale Service Accounts
RBAC-Audit mit rbac-lookup durchgeführt
automountServiceAccountToken: false (per Namespace-Default)
Network:
Default-Deny NetworkPolicy in allen Namespaces
Ingress Controller mit TLS
Egress-Policies für externe Kommunikation
Images:
Image-Scanning in CI/CD (Trivy/Grype)
Nur Registry-interne Images erlaubt (OPA Gatekeeper/Kyverno)
Digest-Pinning für alle produktiven Images
Non-Root-User in allen Containern
Distroless oder Scratch-Images bevorzugen
Secrets:
etcd-Verschlüsselung aktiviert
Keine Secrets in Environment-Variablen oder Configmaps
External Secrets Operator oder Sealed Secrets für sensitive Daten
Monitoring:
Falco für Runtime-Anomalie-Erkennung
Kubernetes Audit-Logs in SIEM weitergeleitet
kube-bench regelmäßig ausführen
IaC / GitOps:
Kubernetes-Manifeste via Checkov/tfsec geprüft
Kyverno oder OPA Gatekeeper als Admission Controller
Image-Signierung mit Cosign erzwungen
Fazit
Container und Kubernetes bieten enorme Vorteile für Deployment und Skalierung - aber ohne konsequentes Hardening entstehen massive Sicherheitsrisiken. Das 4C-Modell gibt den Rahmen: Cloud, Cluster, Container, Code - jede Schicht muss gesichert sein. Die wichtigsten Maßnahmen sind: Distroless-Images mit Digest-Pinning, RBAC nach Least-Privilege, Pod Security Standards auf “restricted”, NetworkPolicy Default-Deny, External Secrets statt etcd-Secrets und Falco für Runtime-Anomalie-Erkennung.
Weiterführend: Cloud Security Hub | Cloud Detection Engineering
Quellen & Referenzen
- [1] NIST SP 800-204 Security Strategies for Microservices-based Application Systems - NIST
- [2] CNCF Cloud Native Security Whitepaper - CNCF
- [3] CIS Kubernetes Benchmark - CIS
- [4] OWASP Kubernetes Top 10 - OWASP
Fragen zu diesem Thema?
Unsere Experten beraten Sie kostenlos und unverbindlich.
Über den Autor
Geschäftsführender Gesellschafter der AWARE7 GmbH mit langjähriger Expertise in Informationssicherheit, Penetrationstesting und IT-Risikomanagement. Absolvent des Masterstudiengangs Internet-Sicherheit an der Westfälischen Hochschule (if(is), Prof. Norbert Pohlmann). Bestseller-Autor im Wiley-VCH Verlag und Lehrbeauftragter der ASW-Akademie. Einschätzungen zu Cybersecurity und digitaler Souveränität erschienen u.a. in Welt am Sonntag, WDR, Deutschlandfunk und Handelsblatt.
10 Publikationen
- Einsatz von elektronischer Verschlüsselung - Hemmnisse für die Wirtschaft (2018)
- Kompass IT-Verschlüsselung - Orientierungshilfen für KMU (2018)
- IT Security Day 2025 - Live Hacking: KI in der Cybersicherheit (2025)
- Live Hacking - Credential Stuffing: Finanzrisiken jenseits Ransomware (2025)
- Keynote: Live Hacking Show - Ein Blick in die Welt der Cyberkriminalität (2025)
- Analyse von Angriffsflächen bei Shared-Hosting-Anbietern (2024)
- Gänsehaut garantiert: Die schaurigsten Funde aus dem Leben eines Pentesters (2022)
- IT Security Zertifizierungen — CISSP, T.I.S.P. & Co (Live-Webinar) (2023)
- Sicherheitsforum Online-Banking — Live Hacking (2021)
- Nipster im Netz und das Ende der Kreidezeit (2017)