PodSecurity Admission (PSA) — Modes & Profiles
PSA là gì và vì sao nó thay thế PodSecurityPolicy
PodSecurity Admission (PSA) là built-in validating admission controller (PodSecurity, file 1) thực thi Pod Security Standards — một bộ ba profile bảo mật chuẩn hóa cho Pod. Nó là cơ chế thay thế chính thức cho PodSecurityPolicy (PSP), thứ đã bị deprecate ở Kubernetes 1.21 và gỡ bỏ hoàn toàn ở 1.25 (Pod Security Admission). Nếu bạn còn runbook nhắc tới PSP, nó đã lỗi thời — trên GKE phiên bản hiện đại, PSP không tồn tại.
Khác biệt thiết kế cốt lõi giữa PSA và PSP, và là lý do PSA thắng:
| Khía cạnh | PodSecurityPolicy (đã gỡ) | PodSecurity Admission |
|---|---|---|
| Cấu hình | Object PSP + RBAC binding phức tạp | Label trên namespace |
| Phạm vi | Cluster-wide, gán qua RBAC | Per-namespace |
| Chế độ | Allow/Deny | enforce / audit / warn |
| Versioning | Không | Có (pin version) |
| Profile | Tự định nghĩa (dễ sai) | Chuẩn hóa (3 profile cố định) |
PSP thất bại vì cơ chế "Pod nào dùng PSP nào" phụ thuộc RBAC theo ServiceAccount tạo Pod — cực kỳ khó suy luận và dễ cấu hình sai theo hướng quá lỏng. PSA đơn giản hóa triệt để: gán một profile cho một namespace bằng label, mọi Pod trong namespace đó bị kiểm theo profile đó. Dễ đọc, dễ audit, dễ suy luận.
Điểm mạnh và cũng là giới hạn: PSA chỉ làm đúng một việc — kiểm Pod theo ba profile có sẵn. Nó không tùy biến được ("cho phép capability X nhưng không Y"), không kiểm tài nguyên ngoài Pod (image, label, ingress). Khi cần những thứ đó, dùng Gatekeeper hoặc CEL policy (file 6, 8). PSA là lớp nền chi phí thấp; policy engine là lớp tùy biến trên nó.
Ba mode: enforce / audit / warn
PSA áp policy theo ba mode, có thể bật đồng thời với các level khác nhau (PSA modes):
| Mode | Hành vi khi Pod vi phạm |
|---|---|
enforce | Từ chối Pod (request bị block) |
audit | Cho qua, nhưng ghi annotation vào audit log |
warn | Cho qua, nhưng trả cảnh báo cho người dùng (hiện trên kubectl) |
Cấu hình hoàn toàn bằng label namespace:
apiVersion: v1
kind: Namespace
metadata:
name: payments
labels:
# enforce: chặn cứng ở mức baseline
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/enforce-version: v1.31
# audit + warn: đo trước ở mức restricted (nghiêm hơn) mà chưa chặn
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: v1.31
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: v1.31Cấu hình trên là mẫu rollout chuẩn: enforce ở mức bạn chắc chắn workload thỏa (baseline), trong khi audit+warn ở mức nghiêm hơn (restricted) để đo trước xem nếu siết lên restricted thì bao nhiêu Pod vi phạm — mà chưa thực sự chặn. Khi audit cho thấy không còn vi phạm restricted, bạn nâng enforce lên restricted.
Version pinning — vì sao bắt buộc
Label pod-security.kubernetes.io/<MODE>-version cố định phiên bản ngữ nghĩa của profile, ví dụ v1.31 hoặc latest (version labels). Đây là một trong những chi tiết quan trọng nhất mà nhiều người bỏ qua.
Lý do: định nghĩa của profile baseline/restricted tiến hóa theo phiên bản Kubernetes. Một control mới có thể được thêm vào restricted ở một bản nâng cấp. Nếu bạn để version: latest (mặc định khi không pin), thì khi GKE nâng control plane lên minor version mới, profile có thể âm thầm siết thêm — và Pod đang chạy tốt bỗng vi phạm sau upgrade, gây outage không lường trước.
Pin version (enforce-version: v1.31) đảm bảo ngữ nghĩa không đổi qua các bản nâng cấp control plane, cho tới khi bạn chủ động nâng version label. Quy trình đúng: nâng version label một cách có kiểm soát (audit ở version mới trước, sửa vi phạm, rồi enforce). Đây là nguyên tắc cùng tinh thần với pin version ở các nơi khác trong GKE — không để hành vi đổi ngầm theo upgrade.
Ba profile và từng control chi tiết
Ba profile từ lỏng tới chặt (Pod Security Standards):
privileged — không giới hạn
Chấp nhận mọi Pod, không kiểm gì. Dành cho workload hạ tầng đặc quyền (CNI agent, CSI driver, monitoring cấp node) cần hostNetwork, privileged, hostPath. Trên GKE, các DaemonSet hệ thống chạy ở namespace với mức này. Không bao giờ dùng privileged cho namespace ứng dụng thường.
baseline — chặn các privilege escalation đã biết
baseline cho phép cấu hình Pod mặc định nhưng chặn các đường nâng quyền rõ ràng. Các control chính (baseline controls):
| Control | Ràng buộc |
|---|---|
| HostProcess | securityContext.windowsOptions.hostProcess phải là false/nil |
| Host namespaces | hostNetwork, hostPID, hostIPC phải false/nil |
| Privileged containers | securityContext.privileged phải false/nil |
| Capabilities | Chỉ được add các capability "an toàn" (NET_BIND_SERVICE...); không add capability nguy hiểm ngoài danh sách cho phép |
| HostPath volumes | volumes[*].hostPath bị cấm |
| HostPorts | containers[*].ports[*].hostPort phải nil/0 (hoặc từ danh sách biết trước) |
| AppArmor | Profile phải là RuntimeDefault/Localhost/nil |
| SELinux | type giới hạn (container_t...); không tự đặt user/role |
| /proc mount | Phải Default/nil (không Unmasked) |
| Seccomp | Không được Unconfined |
| Sysctls | Chỉ các sysctl namespaced an toàn (kernel.shm_rmid_forced, net.ipv4.ip_local_port_range...) |
baseline là mức tối thiểu hợp lý cho mọi namespace ứng dụng đa mục đích — nó chặn được phần lớn cách một container thoát ra node, mà không quá khắt khe đến mức vỡ workload thông thường.
restricted — hardening theo best practice
restricted gồm toàn bộ control của baseline, cộng thêm các yêu cầu hardening nghiêm ngặt (restricted controls):
| Control bổ sung | Yêu cầu |
|---|---|
| Volume types | Chỉ cho phép tập an toàn: configMap, csi, downwardAPI, emptyDir, ephemeral, persistentVolumeClaim, projected, secret |
| Privilege escalation | allowPrivilegeEscalation: false (bắt buộc, Linux) |
| Run as non-root | runAsNonRoot: true (bắt buộc) |
| runAsUser | Nếu đặt, phải khác 0 (không chạy root) |
| Seccomp | Phải đặt rõ RuntimeDefault hoặc Localhost (không được để nil) |
| Capabilities | Phải drop: ["ALL"], chỉ được add lại NET_BIND_SERVICE nếu cần |
restricted là mục tiêu cho workload nhạy cảm (fintech, xử lý dữ liệu khách hàng). Nhưng nó dễ vỡ workload cũ vì hầu hết image mặc định không đặt runAsNonRoot, không drop ALL capabilities, không khai seccomp. Đây là lý do rollout restricted bắt buộc đi qua audit/warn trước (bên dưới).
Một Pod thỏa restricted điển hình có securityContext như sau:
spec:
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:1.0
securityContext:
allowPrivilegeEscalation: false
runAsUser: 1000
capabilities:
drop: ["ALL"]Khác biệt then chốt: enforce áp lên Pod, audit/warn áp lên workload
Đây là một đặc tính tinh tế nhưng cực kỳ quan trọng cho rollout an toàn (PSA workload resources):
enforcechỉ áp lên Pod object cuối cùng, không áp trực tiếp lên workload resource (Deployment, StatefulSet, Job). Nghĩa là khi bạnkubectl applymột Deployment vi phạm, Deployment được tạo thành công — nhưng ReplicaSet controller không tạo được Pod, và bạn chỉ phát hiện qua event/status của ReplicaSet, không qua lỗiapply.auditvàwarnáp lên cả workload resource. Nghĩa là khi bạnapplyDeployment vi phạm, bạn thấy ngay cảnh báo (warn) tại thời điểm apply, dù enforce chỉ chặn ở mức Pod.
Hệ quả thực chiến: chỉ bật enforce mà không bật warn là cấu hình tồi — developer apply Deployment "thành công", rồi bối rối vì Pod không bao giờ xuất hiện, phải đi đào event ReplicaSet mới hiểu. Luôn bật warn (và audit) cùng level với enforce để feedback hiện ngay lúc apply.
Production architecture patterns
Rollout restricted có kỷ luật (mô hình ba bước)
Đây là quy trình chuẩn để nâng một cluster đang chạy lên restricted mà không gây outage:
- Audit + warn ở restricted, chưa enforce (hoặc enforce ở baseline). Đo qua audit log: namespace nào, workload nào vi phạm gì.
- Sửa workload: thêm
runAsNonRoot,drop: ["ALL"],seccompProfile,allowPrivilegeEscalation: false. Lặp tới khi audit sạch. - Nâng enforce lên restricted, giữ version pin. Theo dõi
pod_security_evaluations_totalđể chắc không còn vi phạm.
Mặc định toàn cluster + override theo namespace
GKE/Kubernetes cho phép đặt cấu hình PSA mặc định ở cấp cluster (qua AdmissionConfiguration) để mọi namespace không-gán-label rơi vào một mức an toàn (ví dụ enforce: baseline), thay vì privileged (mặc định khi không có label). Trên cluster tự dựng đây là cờ API server; trên GKE, cấu hình mặc định cấp cluster được quản lý khác — thực hành phổ biến trên GKE là dùng một policy bắt buộc label PSA (qua Gatekeeper, file 6) để không namespace nào "lọt" ra mức privileged vì quên gán label.
Phân tầng namespace theo độ nhạy cảm
Mô hình multi-tenant trưởng thành gán profile theo tầng: namespace hệ thống/hạ tầng → privileged (chỉ cho DaemonSet được duyệt); namespace ứng dụng chung → baseline; namespace xử lý dữ liệu nhạy cảm → restricted. Việc gán này nên là policy bắt buộc, không phải tùy chọn của từng team.
Exemptions — và cái bẫy của chúng
PSA hỗ trợ exemptions cấu hình tĩnh ở cấp controller (qua AdmissionConfiguration), bỏ qua mọi kiểm tra cho (PSA exemptions):
- Usernames — danh tính người gọi cụ thể.
- RuntimeClassNames — Pod dùng runtime class nhất định.
- Namespaces — namespace cụ thể.
Cái bẫy quan trọng: hầu hết Pod được tạo bởi controller, không phải người dùng trực tiếp. Exempt một username chỉ exempt Pod do chính người đó tạo trực tiếp — không exempt Deployment/Job họ tạo (vì Pod thực sự do ReplicaSet/Job controller tạo, với danh tính ServiceAccount của controller). Để exempt workload resource, phải exempt ServiceAccount của controller (ví dụ system:serviceaccount:kube-system:replicaset-controller) — nhưng làm vậy là exempt mọi workload, quá rộng. Vì lý do này, exemption theo namespace thường là cách kiểm soát được duy nhất hợp lý; exemption theo username gần như luôn không làm điều người ta tưởng.
Common mistakes / anti-patterns
Bật restricted enforce thẳng trên cluster đang chạy
Như đã nhấn mạnh: workload cũ gần như chắc chắn vi phạm. Hậu quả ở scale: hàng loạt Pod không tạo được, dịch vụ down sau khi "chỉ thêm một label". Luôn audit/warn trước.
Không pin version
Để latest nghĩa là profile có thể siết thêm âm thầm khi GKE nâng control plane. Một bản upgrade vô hại bỗng làm Pod vi phạm. Luôn pin và nâng version có chủ đích.
Chỉ enforce mà không warn
Developer apply Deployment "thành công" nhưng Pod không bao giờ chạy, không hiểu vì sao. Luôn bật warn cùng level enforce.
Tưởng PSA đủ cho mọi nhu cầu security Pod
PSA chỉ có 3 profile cố định. "Cho phép hostPath cho một volume cụ thể nhưng cấm phần còn lại" — PSA không làm được, cần Gatekeeper/CEL (file 6, 8). PSA là nền, không phải toàn bộ.
Dùng exemption username và tưởng đã exempt workload
Như phân tích: exempt username không exempt Deployment. Hiểu rõ ranh giới này trước khi dựa vào exemption.
GCP-native implementation guidance
Gán PSA cho namespace bằng label (cách phổ biến nhất, declarative):
kubectl label namespace payments \
pod-security.kubernetes.io/enforce=baseline \
pod-security.kubernetes.io/enforce-version=v1.31 \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/audit=restrictedKiểm tra trước một workload có thỏa restricted không mà chưa apply (dry-run đi qua PSA):
kubectl apply -f deployment.yaml --dry-run=server
# cảnh báo warn (nếu có) hiện ngay tại đâyTrên GKE, PSA luôn bật (built-in). Để có cấu hình mặc định cấp cluster và quản lý PSA tập trung qua fleet, GKE Enterprise/Policy Controller cung cấp Pod Security policy bundle — một cách enforce Pod Security Standards qua Gatekeeper (file 6) khi cần linh hoạt hơn PSA thuần.