Chương 29: Kubernetes Security liên quan Web
Khái niệm
Kubernetes orchestrates containers nhưng cũng có attack surface riêng. Lỗi cấu hình K8s có thể expose internal services, allow privilege escalation, và amplify web security vulnerabilities.
Ingress Misconfiguration
Expose Internal Services
# SAI: Expose admin service ra internet
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: admin-ingress
spec:
rules:
- host: admin.example.com # ← Public DNS!
http:
paths:
- path: /
backend:
service:
name: admin-service
port:
number: 8080
# ĐÚNG: Admin chỉ accessible từ VPN IP
metadata:
annotations:
nginx.ingress.kubernetes.io/whitelist-source-range: "10.0.0.0/8,192.168.0.0/16"
SSRF đến Kubernetes API
Nếu app bị SSRF:
http://kubernetes.default.svc.cluster.local/api/v1/pods
http://10.96.0.1/api/v1/secrets ← Kubernetes API server
Nếu Pod không mount ServiceAccount token
→ Không có authentication → List semua pods, secrets!
# Disable ServiceAccount token auto-mount
spec:
automountServiceAccountToken: false # Nếu không cần K8s API access
# Hoặc dùng minimal permissions:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get"] # Chỉ đọc configmap cụ thể
Secrets trong Kubernetes
# SAI: Hardcode secret trong manifest
env:
- name: DB_PASSWORD
value: "SuperSecret123"
# ĐÚNG: Dùng K8s Secret
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
stringData:
password: "SuperSecret123"
# Reference trong Pod:
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
Vấn đề với K8s Secrets:
- Chỉ base64 encoded, không encrypted by default
- Accessible bởi bất kỳ ai có quyền read trong namespace
Giải pháp:
# 1. Encrypt etcd at rest
# /etc/kubernetes/encryption-config.yaml
# 2. Dùng External Secrets Operator
# Sync secrets từ AWS Secrets Manager/Vault vào K8s
# 3. Sealed Secrets (Bitnami)
kubeseal --format yaml < secret.yaml > sealed-secret.yaml
# Sealed secrets safe để commit vào git
Pod Security
apiVersion: v1
kind: Pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:latest
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE # Chỉ nếu cần port <1024
resources:
limits:
cpu: "500m"
memory: "256Mi"
requests:
cpu: "100m"
memory: "128Mi"
Network Policy
# Default deny all ingress/egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
# Allow only necessary traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: webapp-policy
spec:
podSelector:
matchLabels:
app: webapp
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: nginx-ingress
ports:
- port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- port: 5432
- to:
- namespaceSelector: {}
ports:
- port: 53 # DNS
Container Image Security
# Scan images cho vulnerabilities
trivy image myapp:latest --severity HIGH,CRITICAL
# Sign images với cosign
cosign sign --key cosign.key myapp:latest
# Verify trước khi deploy
cosign verify --key cosign.pub myapp:latest
# Admission controller: chỉ allow signed images
# Kyverno or OPA Gatekeeper
# Secure Dockerfile
FROM python:3.11-slim
# Non-root user
RUN groupadd -r app && useradd --no-create-home -r -g app app
WORKDIR /app
# Copy only necessary files
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY src/ .
# Drop to non-root
USER app
EXPOSE 8080
CMD ["python", "app.py"]
Tóm tắt
- Ingress misconfiguration → expose internal services → SSRF/direct access.
- Kubernetes API accessible từ pods nếu có ServiceAccount token.
- K8s Secrets chỉ base64 encoded — dùng External Secrets với real encryption.
- Pod security: non-root, read-only filesystem, drop capabilities.
- Network Policy: default deny, allow minimum necessary.
- Scan container images trong CI/CD.