Pod Security Standards và Admission
Bài 52 cho thấy RBAC quyết được ai tạo pod, nhưng RBAC không nhìn vào nội dung pod. Một người có quyền tạo pod vẫn có thể tạo pod chạy privileged, mượn hostNetwork, hay mount hostPath / — những đường thoát từ container ra node. Chặn việc đó là chặng admission, chặng thứ ba sau authentication và authorization. Kubernetes có sẵn một admission controller cho việc này: Pod Security Admission, bật mặc định từ 1.25.
Ba mức và ba chế độ
Pod Security Admission đo pod theo Pod Security Standards — ba mức tích lũy dần:
privileged ── không ràng buộc gì (cho workload hạ tầng: CNI, agent)
│
baseline ── chặn leo thang đã biết: hostNetwork/PID/IPC, privileged,
│ hostPath, capabilities nguy hiểm
▼
restricted ── baseline + hardening: runAsNonRoot, allowPrivilegeEscalation=false,
drop ALL capabilities, seccompProfile RuntimeDefault
Áp mức nào, ở chế độ nào là do nhãn trên namespace, theo khuôn pod-security.kubernetes.io/<MODE>=<LEVEL>. MODE có ba giá trị khác nhau ở hậu quả:
| MODE | khi pod vi phạm |
|---|---|
enforce |
từ chối — pod không được tạo |
warn |
vẫn tạo, nhưng trả cảnh báo cho người gửi |
audit |
vẫn tạo, ghi một annotation vào audit log |
Một namespace có thể đặt mức khác nhau cho từng chế độ. Cách dùng phổ biến là enforce ở mức vừa phải, warn/audit ở mức cao hơn để thấy trước cái gì sẽ vỡ khi siết.
enforce=restricted: pod thường bị đá
Gắn nhãn enforce restricted lên một namespace:
kubectl create namespace psa-demo
kubectl label namespace psa-demo \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest
Rồi thử tạo một pod bình thường, không khai securityContext gì:
kubectl -n psa-demo run plain --image=busybox:1.36 --command -- sleep 100000
Error from server (Forbidden): pods "plain" is forbidden: violates PodSecurity "restricted:latest":
allowPrivilegeEscalation != false (container "plain" must set securityContext.allowPrivilegeEscalation=false),
unrestricted capabilities (container "plain" must set securityContext.capabilities.drop=["ALL"]),
runAsNonRoot != true (pod or container "plain" must set securityContext.runAsNonRoot=true),
seccompProfile (pod or container "plain" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
Pod bị từ chối ngay ở admission, kèm đúng bốn điểm restricted đòi mà pod thiếu. Thông báo này cũng là hướng dẫn sửa: muốn pod sống trong namespace này, phải khai đủ bốn trường đó.
Pod tuân thủ thì chạy
Viết lại pod cho khớp restricted:
apiVersion: v1
kind: Pod
metadata: {name: compliant}
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile: {type: RuntimeDefault}
containers:
- name: app
image: busybox:1.36
command: ["sleep", "100000"]
securityContext:
allowPrivilegeEscalation: false
capabilities: {drop: ["ALL"]}
kubectl -n psa-demo apply -f compliant.yaml
kubectl -n psa-demo get pod compliant
NAME READY STATUS RESTARTS AGE
compliant 1/1 Running 0 9s
Pod chạy. runAsNonRoot: true buộc container không chạy bằng root (cần runAsUser khác 0), allowPrivilegeEscalation: false chặn tiến trình con giành thêm quyền, drop: ["ALL"] bỏ mọi Linux capability, seccompProfile RuntimeDefault bật bộ lọc syscall mặc định của runtime. Bốn trường này là nội dung Bài 55 mổ kỹ; ở đây chúng là điều kiện vào cửa.
warn: cảnh báo mà không chặn
Chế độ enforce từ chối thẳng, đôi khi quá gắt cho một namespace đang chạy. warn cho thấy pod nào sẽ vi phạm mà chưa chặn — hợp để dò trước khi siết. Một namespace khác, đặt warn=baseline, rồi tạo một pod vi phạm baseline:
kubectl create namespace psa-warn
kubectl label namespace psa-warn pod-security.kubernetes.io/warn=baseline
# pod hostNetwork + privileged
kubectl -n psa-warn apply -f priv-pod.yaml
Warning: would violate PodSecurity "baseline:latest": host namespaces (hostNetwork=true),
privileged (container "c" must not set securityContext.privileged=true)
pod/priv created
Có Warning: nêu rõ hai vi phạm baseline (hostNetwork, privileged), nhưng dòng cuối pod/priv created — pod vẫn được tạo. Đó là khác biệt giữa warn và enforce: cùng phát hiện vi phạm, nhưng warn chỉ báo còn enforce chặn. Một quy trình siết bảo mật thường bắt đầu bằng warn/audit để gom danh sách pod sẽ vỡ, sửa dần, rồi mới bật enforce.
PSA gác ở tầng pod, không phải workload
PSA là một validating admission plugin dựng sẵn trong API server (tên PodSecurity), nằm ở chặng admission của Bài 51 và chạy mỗi khi có request CREATE/UPDATE một Pod. Chi tiết này quan trọng và dễ sai: nó gác ở tầng Pod, không phải Deployment hay Job. Tạo một Deployment với pod template vi phạm trong namespace enforce=restricted:
kubectl -n psa2 create deployment bad --image=busybox:1.36 -- sleep 100000
kubectl -n psa2 get deploy bad
kubectl -n psa2 get pods
Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false ...
deployment.apps/bad created # Deployment TẠO ĐƯỢC
NAME READY UP-TO-DATE AVAILABLE
bad 0/1 0 0 # nhưng 0 pod
No resources found in psa2 namespace.
Deployment được tạo (chỉ có một Warning từ PSA chạy thử template), nhưng không pod nào ra đời. Vì PSA gác ở pod CREATE, mà Deployment thì không trực tiếp tạo pod — ReplicaSet của nó mới tạo (Bài 24), và đó là chỗ enforce chặn. Lỗi hiện ở event của ReplicaSet, không phải lúc tạo Deployment:
kubectl -n psa2 get events | grep FailedCreate
Warning FailedCreate replicaset/bad-df59c78fb Error creating: pods "bad-..." is forbidden:
violates PodSecurity "restricted:latest": allowPrivilegeEscalation != false ...
Đây là cơ chế cần nắm: enforce áp lên object pod lúc nó được tạo, nên với workload qua controller, vi phạm không làm hỏng việc tạo Deployment mà làm ReplicaSet kẹt vòng lặp FailedCreate. warn/audit thì khác — chúng chạy ngay trên object workload (đánh giá template) để báo sớm, đó là lý do Warning xuất hiện ngay lúc create deployment. Hệ quả thực tế: một Deployment "xanh" trong kubectl get deploy mà READY 0/1 mãi, trong namespace có PSA, gần như chắc là pod bị enforce chặn — phải xem event của ReplicaSet mới thấy.
🧹 Dọn dẹp
kubectl delete namespace psa-demo psa-warn psa2
Pod Security Admission là tính năng sẵn trong API server, không cài gì thêm; xóa các namespace là sạch. Manifest ở github.com/nghiadaulau/kubernetes-from-scratch, thư mục 54-pod-security.
Tổng kết
Pod Security Admission chặn pod nguy hiểm ngay ở chặng admission, không cần cài thêm (built-in từ 1.25). Nó đo pod theo ba mức tích lũy — privileged (không ràng buộc), baseline (chặn leo thang đã biết: host namespaces, privileged, hostPath), restricted (thêm hardening: runAsNonRoot, allowPrivilegeEscalation=false, drop ALL, seccomp RuntimeDefault) — và áp qua nhãn namespace pod-security.kubernetes.io/<mode>=<level>. Ba chế độ khác nhau ở hậu quả: enforce từ chối, warn cảnh báo, audit ghi log. Ta đã thấy enforce=restricted đá một pod thường ra với bốn vi phạm cụ thể, một pod khai đủ bốn trường thì chạy, và warn=baseline chỉ cảnh báo khi gặp pod hostNetwork+privileged. Về cơ chế, PSA là validating admission plugin gác ở tầng Pod lúc CREATE/UPDATE, nên với workload qua controller, vi phạm không chặn việc tạo Deployment mà làm ReplicaSet kẹt FailedCreate — một Deployment READY 0/1 mãi trong namespace có PSA thường là dấu hiệu đó. Cách triển khai thực tế: bật warn/audit trước để dò, sửa, rồi mới enforce.
Bốn trường mà restricted đòi — runAsNonRoot, drop capabilities, seccomp, allowPrivilegeEscalation — là chính sách ở mức Kubernetes. Bài 55 đi xuống tầng kernel xem chúng làm gì thật: capability nào cho phép gì, seccomp lọc syscall ra sao, AppArmor trên Ubuntu của node ràng buộc tiến trình đến đâu.