RBAC quyết định ai được tạo pod. Pod Security Standards quyết định pod đó được phép làm gì với node. Hai lớp khác nhau, và lớp thứ hai bật bằng một dòng nhãn.

Ba mức, kết quả đo trên cùng một pod, và bốn dòng phải khai

Bật bằng nhãn namespace

kubectl create ns pss-restricted
kubectl label ns pss-restricted \
  pod-security.kubernetes.io/enforce=restricted

Không cài gì thêm. Từ Kubernetes 1.25, bộ kiểm này nằm sẵn trong API server, thay cho PodSecurityPolicy đã bị gỡ.

Ba mức: privileged (không chặn gì), baseline (chặn leo thang rõ ràng), restricted (siết chặt).

Đo thật: cùng một pod, ba namespace

Tôi tạo ba namespace gắn ba mức, rồi thả cùng một bộ pod vào.

pod privileged baseline restricted
nginx bình thường tạo được tạo được từ chối
privileged: true tạo được từ chối từ chối
hostNetwork: true tạo được từ chối từ chối

Ba dòng sau đúng như tên gọi. Dòng đầu mới là điều đáng dừng lại.

Pod nginx bình thường bị từ chối

Đây là toàn văn lời từ chối:

violates PodSecurity "restricted:latest":
  allowPrivilegeEscalation != false
    (container "c" must set securityContext.allowPrivilegeEscalation=false)
  unrestricted capabilities
    (container "c" must set securityContext.capabilities.drop=["ALL"])
  runAsNonRoot != true
    (pod or container "c" must set securityContext.runAsNonRoot=true)
  seccompProfile
    (pod or container "c" must set securityContext.seccompProfile.type
     to "RuntimeDefault" or "Localhost")

Pod này không làm gì đặc biệt cả. Nó chỉ chạy nginx.

Điểm cần hiểu: restricted không hỏi "pod này có nguy hiểm không" mà hỏi "pod này có khai báo là nó không nguy hiểm không". Không khai là từ chối.

Cách đó nghe khó chịu nhưng đúng. Kubernetes không biết container bên trong làm gì; nó chỉ đọc được phần khai báo. Mặc định của securityContext là "chạy root, giữ đủ capability, cho leo thang quyền" — nên im lặng nghĩa là mở, và restricted không chấp nhận im lặng.

Pod hợp lệ trông thế nào

apiVersion: v1
kind: Pod
metadata: {name: pok}
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    seccompProfile: {type: RuntimeDefault}
  containers:
    - name: c
      image: nginx:alpine
      securityContext:
        allowPrivilegeEscalation: false
        capabilities: {drop: ["ALL"]}

Kết quả: pod/pok created trong pss-restricted.

Năm dòng. Không dòng nào khó, nhưng dòng nào cũng phải có — và runAsUser: 1000 không thừa: runAsNonRoot: true chỉ kiểm tra image không chạy root, còn nếu image không khai USER thì pod sẽ dừng ở CreateContainerConfigError sau khi đã tạo xong. Bộ kiểm cho qua, kubelet mới chặn — hai chỗ khác nhau.

Với nginx thật, image chính thức chạy root nên phải dùng bản nginxinc/nginx-unprivileged hoặc tự dựng. Đó mới là chi phí thật của restricted: không phải năm dòng YAML, mà là việc phải kiểm lại từng image trong hệ thống.

Ba nhãn, ba hành vi

Nhãn Làm gì
enforce Từ chối tạo pod
warn Cảnh báo cho người gõ lệnh, pod vẫn được tạo
audit Ghi vào audit log

Dùng chung được, và đó là cách siết chặt an toàn:

kubectl label ns san-xuat \
  pod-security.kubernetes.io/enforce=baseline \
  pod-security.kubernetes.io/warn=restricted \
  pod-security.kubernetes.io/audit=restricted

Namespace này đang chặn ở mức baseline, nhưng mọi pod chưa đạt restricted sẽ hiện cảnh báo và vào audit log. Bạn đo được siết chặt sẽ gây vỡ bao nhiêu, mà chưa vỡ gì cả.

Đặt enforce=restricted thẳng vào một namespace đang chạy là cách nhanh nhất để dừng mọi lần triển khai tiếp theo — và nó không dừng pod đang chạy, nên bạn chỉ biết khi lần deploy sau thất bại.

enforce chỉ xét lúc tạo pod

Gắn nhãn enforce không đụng tới pod đang chạy. Pod vi phạm vẫn sống cho tới khi có ai xoá nó.

Hệ quả thực tế: một Deployment vi phạm vẫn phục vụ bình thường, nhưng nếu node chết hoặc bạn scale lên, ReplicaSet không tạo được pod thay thế. Sự cố nằm im chờ đúng lúc tệ nhất mới nổ.

Nên sau khi gắn nhãn, đừng dừng ở "không có lỗi". Đá thử một pod:

kubectl delete pod -n san-xuat <mot-pod> && \
  kubectl get pods -n san-xuat -w

Thử ba mươi giây

Xem namespace nào đang không có lớp bảo vệ nào:

kubectl get ns -o json | python3 -c '
import sys,json
for n in json.load(sys.stdin)["items"]:
    l=n["metadata"].get("labels",{})
    m=l.get("pod-security.kubernetes.io/enforce","(khong co)")
    print(f"{m:>12}  " + n["metadata"]["name"])
'

Rồi thử warn trước khi thử enforce — không mất gì mà biết ngay có bao nhiêu thứ sẽ vỡ:

kubectl label --overwrite ns <ten> \
  pod-security.kubernetes.io/warn=restricted

# tao lai chinh nhung pod dang chay, nhung chi chay thu
kubectl get pods -n <ten> -o yaml | kubectl create -f - --dry-run=server

Mỗi dòng Warning: would violate PodSecurity là một pod sẽ không tạo lại được sau khi siết. Lỗi AlreadyExists ở cuối là bình thường — pod đang chạy thật, cảnh báo đã in ra trước đó rồi.

Lệnh đầu tôi viết là kubectl delete pod --all --dry-run=server, và nó không in cảnh báo nào. Suýt kết luận là namespace sạch. Lý do rõ ràng khi nghĩ lại: bộ kiểm chỉ chạy lúc tạo pod, mà xoá thì không tạo gì. Muốn thấy cảnh báo thì phải làm động tác tạo.

Phần sau đo kubectl từ phía người dùng: cache ở đâu, và vì sao lệnh đầu tiên sau khi đổi cụm luôn chậm hơn hẳn.