Chuyển đến nội dung chính

BÀI 27: THỰC HÀNH — KUBERNETES SECURITY

Bài thực hành Module 6: Tạo ServiceAccounts và RBAC least privilege, viết ValidatingAdmissionPolicy bằng CEL, ký container image với Cosign, scan cluster với kube-bench, cấu hình PSA Restricted mode.

🔒 DevSecOps — Bài 27 BÀI 27: THỰC HÀNH — KUBERNETES SECURITY

KUBERNETES: TỪ CƠ BẢN ĐẾN NÂNG CAO

Module 6: Security

xdev.asia

🎯 Mục tiêu bài thực hành

  • Tạo RBAC với least privilege cho CI/CD ServiceAccount
  • Viết ValidatingAdmissionPolicy bằng CEL
  • Enforce Pod Security Admission Restricted mode
  • Scan images và cluster với Trivy
  • Deploy Falco và test runtime detection

Lab 1: RBAC — Read-only ServiceAccount

kubectl create namespace lab6

Tạo ServiceAccount cho monitoring tool (chỉ cần đọc)

cat <<EOF | kubectl apply -f - apiVersion: v1 kind: ServiceAccount metadata: name: monitoring-sa namespace: lab6

ClusterRole: chỉ đọc pods, services, nodes

apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: monitoring-reader rules:

  • apiGroups: [""] resources: ["pods", "services", "endpoints", "nodes"] verbs: ["get", "list", "watch"]
  • apiGroups: ["apps"] resources: ["deployments", "statefulsets", "daemonsets"] verbs: ["get", "list", "watch"]
  • apiGroups: ["metrics.k8s.io"] resources: ["pods", "nodes"] verbs: ["get", "list"]

apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: monitoring-binding subjects:

  • kind: ServiceAccount name: monitoring-sa namespace: lab6 roleRef: kind: ClusterRole name: monitoring-reader apiGroup: rbac.authorization.k8s.io EOF

Test permissions

kubectl auth can-i get pods --as=system:serviceaccount:lab6:monitoring-sa kubectl auth can-i delete deployments --as=system:serviceaccount:lab6:monitoring-sa

Expected: no

kubectl auth can-i create secrets --as=system:serviceaccount:lab6:monitoring-sa

Expected: no

Lab 2: RBAC cho CI/CD Deployer

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: cicd-deployer
  namespace: lab6
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: deployer
  namespace: lab6
rules:
# Chỉ có thể deploy (không đọc secrets)
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "create", "update", "patch"]
- apiGroups: [""]
  resources: ["services", "configmaps"]
  verbs: ["get", "list", "create", "update", "patch"]
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: cicd-binding
  namespace: lab6
subjects:
- kind: ServiceAccount
  name: cicd-deployer
  namespace: lab6
roleRef:
  kind: Role
  name: deployer
  apiGroup: rbac.authorization.k8s.io
EOF

Verify: có thể deploy nhưng không đọc secrets

kubectl auth can-i create deployments -n lab6 --as=system:serviceaccount:lab6:cicd-deployer kubectl auth can-i get secrets -n lab6 --as=system:serviceaccount:lab6:cicd-deployer

Lab 3: ValidatingAdmissionPolicy

# Policy 1: Chặn :latest tag
cat <<EOF | kubectl apply -f -
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: no-latest-image
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups: ["apps"]
      apiVersions: ["v1"]
      operations: ["CREATE", "UPDATE"]
      resources: ["deployments"]
  validations:
  - expression: |
      object.spec.template.spec.containers.all(c,
        !c.image.endsWith(":latest") && c.image.contains(":")
      )
    message: "Tất cả container images phải có specific tag, không dùng :latest"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: no-latest-image-binding
spec:
  policyName: no-latest-image
  validationActions: [Deny]
  matchResources:
    namespaceSelector:
      matchLabels:
        environment: production
EOF

Label namespace lab6 là production

kubectl label namespace lab6 environment=production

Test: deploy với :latest → bị reject

cat <<EOF | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: bad-deploy namespace: lab6 spec: replicas: 1 selector: matchLabels: app: bad template: metadata: labels: app: bad spec: containers: - name: app image: nginx:latest # vi phạm policy! EOF

Error: Tất cả container images phải có specific tag

Deploy với tag hợp lệ → OK

cat <<EOF | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: good-deploy namespace: lab6 spec: replicas: 1 selector: matchLabels: app: good template: metadata: labels: app: good spec: containers: - name: app image: nginx:1.27 # tag hợp lệ EOF

Lab 4: Pod Security Admission — Restricted Mode

# Tạo namespace với PSA Restricted
kubectl create namespace secure-ns
kubectl label namespace secure-ns \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/warn=restricted

Deploy non-compliant pod (sẽ bị reject)

cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: bad-pod namespace: secure-ns spec: containers:

  • name: app image: nginx:1.27

    Không có securityContext → vi phạm Restricted

EOF

Error: violates PodSecurity "restricted:latest"

Deploy compliant pod

cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: good-pod namespace: secure-ns spec: securityContext: runAsNonRoot: true runAsUser: 1000 seccompProfile: type: RuntimeDefault containers:

  • name: app image: nginx:1.27 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: ["ALL"] volumeMounts:
    • name: cache mountPath: /var/cache/nginx
    • name: run mountPath: /var/run
    • name: tmp mountPath: /tmp volumes:
  • name: cache emptyDir: {}
  • name: run emptyDir: {}
  • name: tmp emptyDir: {} EOF

Lab 5: Trivy Scan

# Cài Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin

Scan image

trivy image --severity HIGH,CRITICAL nginx:1.27

Scan Kubernetes manifests trong thư mục

mkdir -p /tmp/manifests kubectl get deployment good-deploy -n lab6 -o yaml > /tmp/manifests/deployment.yaml trivy config /tmp/manifests/

Scan running cluster (cần kubectl access)

trivy k8s --namespace lab6 cluster

Lab 6: Falco Runtime Detection

# Cài Falco
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm install falco falcosecurity/falco \
  --namespace falco \
  --create-namespace \
  --set driver.kind=modern_ebpf   # cho kernel mới (5.8+)

kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=falco -n falco --timeout=120s

Theo dõi Falco logs

kubectl logs -n falco -l app.kubernetes.io/name=falco -f &

Trigger alert: spawn shell trong container

POD=$(kubectl get pods -n lab6 -l app=good -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || kubectl run test-pod --image=nginx:1.27 -n lab6 --dry-run=client -o name) kubectl exec -n lab6 deploy/good-deploy -- sh -c "id"

Xem Falco alert:

Notice A shell was spawned in a container with an attached terminal

(user=root k8s.ns=lab6 k8s.pod=good-deploy-xxx container=app)

Test: đọc sensitive file

kubectl exec -n lab6 deploy/good-deploy -- cat /etc/passwd 2>/dev/null || true

Falco sẽ detect access to /etc/passwd

Cleanup

kubectl delete namespace lab6 secure-ns
kubectl delete validatingadmissionpolicy no-latest-image
kubectl delete validatingadmissionpolicybinding no-latest-image-binding
kubectl delete clusterrole monitoring-reader
kubectl delete clusterrolebinding monitoring-binding
helm uninstall falco -n falco

Tổng kết

  • ✅ RBAC least privilege cho monitoring và CI/CD roles
  • ✅ ValidatingAdmissionPolicy: chặn :latest images với CEL
  • ✅ PSA Restricted mode: enforce hardened security
  • ✅ Trivy: scan images và manifests
  • ✅ Falco: runtime threat detection với eBPF