Container Security Best Practices
Container Security Best Practices
Container-Sicherheit erfordert Maßnahmen auf mehreren Ebenen. Lernen Sie Best Practices für sichere Images, Kubernetes-Konfiguration und Runtime-Schutz.
Security Layers
┌─────────────────────────────────────────────────────────────┐ │ CONTAINER SECURITY LAYERS │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 1. IMAGE SECURITY │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ • Minimale Base Images │ │ │ │ • Keine Secrets in Images │ │ │ │ • Vulnerability Scanning │ │ │ │ • Signierte Images │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ 2. REGISTRY SECURITY │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ • Private Registry │ │ │ │ • Access Control │ │ │ │ • Image Scanning bei Push │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ 3. ORCHESTRATION SECURITY (Kubernetes) │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ • RBAC │ │ │ │ • Network Policies │ │ │ │ • Pod Security Standards │ │ │ │ • Secrets Management │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ 4. RUNTIME SECURITY │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ • Seccomp/AppArmor │ │ │ │ • Read-only Filesystem │ │ │ │ • Resource Limits │ │ │ │ • Runtime Monitoring │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘
Sichere Dockerfiles
# ❌ UNSICHER FROM ubuntu:latest RUN apt-get update && apt-get install -y nodejs npm COPY . /app RUN npm install USER root CMD ["node", "server.js"] # ✅ SICHER FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci --only=production FROM gcr.io/distroless/nodejs20-debian12 WORKDIR /app COPY --from=builder /app/node_modules ./node_modules COPY --chown=nonroot:nonroot . . USER nonroot EXPOSE 3000 CMD ["server.js"]
# Best Practices für Dockerfiles
# 1. Spezifische Tags statt :latest
FROM node:20.10.0-alpine3.19
# 2. Multi-Stage Builds
FROM node:20-alpine AS builder
# Build-Tools hier
RUN npm ci && npm run build
FROM node:20-alpine AS production
# Nur Production-Artefakte
COPY --from=builder /app/dist ./dist
# 3. Minimale Base Images
# Alpine, Distroless, oder Scratch
# 4. Keine Secrets im Image
# ❌ COPY .env /app/.env
# ✅ Secrets via Environment oder Mounted Volumes
# 5. .dockerignore nutzen
# .git, node_modules, .env, *.log
# 6. Non-root User
RUN addgroup -g 1001 appgroup && \
adduser -u 1001 -G appgroup -D appuser
USER appuser
# 7. HEALTHCHECK
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -q --spider http://localhost:3000/health || exit 1
Image Scanning
# Trivy (Open Source Scanner)
trivy image myapp:latest
# Scan im CI/CD
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest
# Als GitHub Action
- name: Trivy Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
format: 'sarif'
exit-code: '1'
severity: 'CRITICAL,HIGH'
# Docker Scout
docker scout cves myapp:latest
docker scout recommendations myapp:latest
# Grype (Anchore)
grype myapp:latest
Kubernetes Pod Security
# Sichere Pod-Konfiguration
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
# Service Account mit minimalen Rechten
serviceAccountName: myapp-sa
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:v1.0.0
imagePullPolicy: Always
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
privileged: false
resources:
limits:
cpu: "500m"
memory: "256Mi"
requests:
cpu: "100m"
memory: "128Mi"
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /app/cache
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
Pod Security Standards
# Namespace mit Pod Security Standard labeln
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
# Levels:
# - privileged: Keine Einschränkungen
# - baseline: Verhindert bekannte Privilege Escalations
# - restricted: Streng, beste Security
# Was "restricted" verhindert:
# ✗ Privileged containers
# ✗ Host namespaces (network, PID, IPC)
# ✗ Host ports
# ✗ Running as root
# ✗ Privilege escalation
# ✗ Non-default capabilities
Network Policies
# Default: Deny All Ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
---
# Erlaubt nur Traffic von Frontend zu Backend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-allow-frontend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
---
# Egress: Nur DNS und interne Services
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-egress
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
# DNS erlauben
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
# Nur zu Database
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
Secrets Management
# ❌ UNSICHER: Secrets als Plain Text
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
password: cGFzc3dvcmQxMjM= # Base64 ist KEINE Verschlüsselung!
# ✅ BESSER: External Secrets Operator
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: vault
target:
name: db-credentials
data:
- secretKey: password
remoteRef:
key: secret/data/production/database
property: password
# ✅ BESSER: Sealed Secrets
# Verschlüsselt mit Cluster-Key, sicher für Git
kubeseal --format yaml < secret.yaml > sealed-secret.yaml
# Secrets als Files, nicht Env Vars (sicherer)
spec:
containers:
- name: app
volumeMounts:
- name: secrets
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secrets
secret:
secretName: app-secrets
defaultMode: 0400 # Nur lesbar für Owner
# Im Code:
const dbPassword = fs.readFileSync('/etc/secrets/db-password', 'utf8').trim();
RBAC
# Service Account mit minimalen Rechten
apiVersion: v1
kind: ServiceAccount
metadata:
name: myapp-sa
namespace: production
---
# Role mit minimalen Permissions
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: myapp-role
namespace: production
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
resourceNames: ["myapp-config"] # Nur spezifische 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
Runtime Security
# Seccomp Profile (System Call Filter)
spec:
securityContext:
seccompProfile:
type: RuntimeDefault # Kubernetes Default Profile
# type: Localhost
# localhostProfile: profiles/custom.json
# AppArmor (Linux Security Module)
metadata:
annotations:
container.apparmor.security.beta.kubernetes.io/myapp: runtime/default
# Read-only Root Filesystem
securityContext:
readOnlyRootFilesystem: true
# Dann tmpfs für schreibbare Verzeichnisse:
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir:
medium: Memory
sizeLimit: 64Mi
Vulnerability Checklist
IMAGE SECURITY [ ] Minimales Base Image (Alpine, Distroless) [ ] Spezifische Image Tags, nicht :latest [ ] Multi-Stage Builds [ ] Keine Secrets im Image [ ] Image Scanning in CI/CD [ ] Image Signing (Cosign, Notary) POD SECURITY [ ] runAsNonRoot: true [ ] readOnlyRootFilesystem: true [ ] allowPrivilegeEscalation: false [ ] Drop ALL capabilities [ ] Resource Limits gesetzt [ ] Seccomp Profile aktiviert KUBERNETES SECURITY [ ] RBAC mit Least Privilege [ ] Network Policies (Default Deny) [ ] Pod Security Standards enforced [ ] Secrets verschlüsselt (External Secrets) [ ] Service Account Token nicht auto-mounted RUNTIME [ ] Logging und Monitoring [ ] Anomalie-Detection [ ] Regelmäßige Security Audits [ ] Incident Response Plan
💡 Best Practices:
1. Defense in Depth - mehrere Security-Layer
2. Least Privilege überall anwenden
3. Regelmäßig Images auf Vulnerabilities scannen
4. Immutable Infrastructure - keine SSH in Container
5. Security als Teil der CI/CD Pipeline
2. Least Privilege überall anwenden
3. Regelmäßig Images auf Vulnerabilities scannen
4. Immutable Infrastructure - keine SSH in Container
5. Security als Teil der CI/CD Pipeline