Phần 7 — Security: RBAC, ServiceAccount, SecurityContext và Pod Security Standards
Ý kiến
0
Chưa có ý kiến nào. Hãy là người đầu tiên chia sẻ!
Chưa có ý kiến nào. Hãy là người đầu tiên chia sẻ!
Phần cuối series K8s: Cluster API (CAPI) quản lý cluster bằng K8s API, FinOps với OpenCost + Karpenter + right-sizing, AI/ML workloads (GPU, Kubeflow, KServe, vLLM, Argo Workflows, gang scheduling), Edge K8s và WebAssembly.
Multi-tenancy K8s (soft: namespace+RBAC+Quota, HNC, Capsule, vCluster; hard: cluster riêng) và multi-cluster: management cluster, GitOps ApplicationSet, Cluster API, Karmada, Crossplane, observability liên cluster.
Service Mesh chuyên sâu cho K8s: Istio (sidecar + ambient), Linkerd (proxy Rust nhẹ), Cilium Service Mesh (eBPF sidecarless) — mTLS, VirtualService, AuthorizationPolicy, canary, mirror, multi-cluster, đo overhead, khi không nên dùng mesh.
Series Kubernetes Toàn Tập — 13 phần:
Phần 7 — Security ← bạn đang đọc
K8s không tự an toàn — bạn phải config đúng. Phần này đi qua framework 4C của tài liệu chính thức (Cloud, Cluster, Container, Code) và đào sâu các cơ chế K8s cung cấp:
Authentication và Authorization (RBAC).
ServiceAccount cho workload identity.
Pod SecurityContext: user, capabilities, seccomp, AppArmor.
Pod Security Standards (replaced PodSecurityPolicy).
Admission control: Kyverno / OPA Gatekeeper.
Image security, secret encryption, audit log.
┌────────────────────────────────────┐
│ Cloud / Infra │ ← VPC, firewall, IAM, hardware
│ ┌──────────────────────────────┐ │
│ │ Cluster │ ← K8s: API auth, RBAC, etcd encryption
│ │ ┌────────────────────────┐ │ │
│ │ │ Container │ ← image, runtime, capabilities
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ Code │ ← OWASP, deps, secrets
│ │ │ └──────────────────┘ │ │ │
│ │ └────────────────────────┘ │ │
│ └──────────────────────────────┘ │
└────────────────────────────────────┘
Mỗi lớp ngoài bảo vệ lớp trong. K8s lo lớp Cluster và một phần Container; lớp Cloud và Code là việc của bạn.
API Server xác thực client qua nhiều cơ chế:
X.509 client cert — admin kubeadm dùng cert ký bởi CA cluster.
Bearer token — token cố định, ServiceAccount token, OIDC token.
OpenID Connect (OIDC) — tích hợp Keycloak, Okta, Auth0, Google… cho human user.
Webhook — gọi service ngoài để verify token.
K8s không tự quản User — không có resource User. User chỉ là string trong cert/token, dùng để khớp RBAC.
Sau auth, request đi qua chuỗi authorizer. Mặc định là RBAC. Nếu tất cả authorizer từ chối thì block, ngược lại allow.
RBAC gồm 4 object:
| Object | Scope | Mục đích |
|---|---|---|
Role | Namespace | Định nghĩa permissions trong 1 namespace |
ClusterRole | Cluster | Permissions cluster-wide hoặc cross-namespace |
RoleBinding | Namespace | Gán Role/ClusterRole cho subject trong namespace |
ClusterRoleBinding | Cluster | Gán ClusterRole cho subject cluster-wide |
Subject = User, Group, hoặc ServiceAccount.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: team-a
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch"]
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: alex-pod-reader
namespace: team-a
subjects:
- kind: User
name: alex
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-reader
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ops-node-reader
subjects:
- kind: Group
name: ops
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: node-reader
apiGroup: rbac.authorization.k8s.io
| Role | Permission |
|---|---|
cluster-admin | Toàn quyền — tránh gán |
admin | RW mọi resource trong namespace (qua RoleBinding) |
edit | RW resource cơ bản, không touch RBAC |
view | Read-only, không Secret |
Bind vào namespace dùng RoleBinding chứ không phải ClusterRoleBinding:
kubectl create rolebinding team-a-admins \
--clusterrole=admin --user=alex --user=bob -n team-a
# Tôi có làm được X không?
kubectl auth can-i create deployments -n team-a
kubectl auth can-i delete pods -n prod --as=alex
kubectl auth can-i '*' '*' --as=alex # admin?
# Liệt kê mọi verb tôi có trong namespace
kubectl auth can-i --list -n team-a
Không gán cluster-admin cho user/SA app.
Mỗi team có namespace riêng + RoleBinding admin trong namespace đó.
SA của app chỉ có quyền tối thiểu — nếu app không gọi API K8s thì không cần RoleBinding.
Audit định kỳ: kubectl get clusterrolebindings -o wide.
Pod luôn có identity. Nếu bạn không khai báo, K8s gán default SA của namespace. SA này thường có quyền rất ít — nhưng nếu app cần gọi API K8s (controller, operator, CI), bạn nên tạo SA riêng.
apiVersion: v1
kind: ServiceAccount
metadata:
name: alloy
namespace: monitoring
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: alloy-reader
rules:
- apiGroups: [""]
resources: ["nodes", "pods", "services", "endpoints"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: alloy-reader
subjects:
- kind: ServiceAccount
name: alloy
namespace: monitoring
roleRef:
kind: ClusterRole
name: alloy-reader
apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: alloy
namespace: monitoring
spec:
selector:
matchLabels: { app: alloy }
template:
metadata:
labels: { app: alloy }
spec:
serviceAccountName: alloy # ← gán SA
containers:
- name: alloy
image: grafana/alloy:v1.0
Trước 1.22, mỗi SA có 1 Secret chứa token vĩnh viễn. Từ 1.22+, K8s inject token short-lived qua projected volume tự động:
/var/run/secrets/kubernetes.io/serviceaccount/token # rotate ~1h, audience: api
App đọc file này, dùng làm Bearer token gọi API server. Không cần manage Secret SA token.
App không gọi API K8s thì nên tắt mount SA token để giảm bề mặt tấn công:
spec:
automountServiceAccountToken: false
SA K8s có thể map sang cloud IAM:
EKS IRSA — annotate SA với eks.amazonaws.com/role-arn, pod assume role IAM.
GKE Workload Identity — bind SA K8s ↔ GCP IAM SA.
Azure AD Workload Identity — tương tự.
Không cần access key/secret nằm trong Secret.
Container chạy mặc định là root, có nhiều Linux capability. SecurityContext giới hạn lại.
apiVersion: v1
kind: Pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:1.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
runAsUser: 10001
runAsNonRoot: true — container fail nếu image cố chạy UID 0.
runAsUser, runAsGroup, fsGroup — UID/GID khi process khởi động, GID supplementary cho mount volume.
allowPrivilegeEscalation: false — chặn setuid/setgid binary leo quyền.
readOnlyRootFilesystem: true — / chỉ đọc, app write phải vào volume riêng. Chặn ghi malware vào /tmp.
capabilities.drop: ["ALL"] — bỏ mọi Linux capability rồi add lại cái cần (vd. NET_BIND_SERVICE để bind port < 1024).
seccompProfile.type: RuntimeDefault — dùng seccomp filter mặc định của runtime, chặn syscall nguy hiểm.
privileged: true — KHÔNG dùng cho app thường. Chỉ cho CNI/CSI/DaemonSet hệ thống.
Nếu host bật AppArmor (Ubuntu) hoặc SELinux (RHEL), bạn có thể pin profile cho container:
metadata:
annotations:
container.apparmor.security.beta.kubernetes.io/app: runtime/default
PodSecurityPolicy (PSP) bị remove ở 1.25. Thay thế là Pod Security Admission — một admission controller built-in implement 3 mức của Pod Security Standards:
| Mức | Mục đích |
|---|---|
privileged | Cho phép tất cả — system workload |
baseline | Chặn các config nguy hiểm rõ ràng (hostPath, hostNetwork, privileged…) |
restricted | Best practice hardened: runAsNonRoot, seccomp, capabilities drop ALL, … |
Bật cho namespace bằng label:
kubectl label namespace prod \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/audit=restricted
3 mode:
enforce — reject pod vi phạm.
warn — apply OK nhưng kubectl in warning.
audit — log vào audit log, không block.
Workflow khuyến nghị: bật warn + audit trước, fix violations, sau đó switch enforce.
PSS chỉ làm policy cứng có sẵn. Cần policy custom (vd. image phải từ registry nội bộ, mọi Service LoadBalancer phải có annotation cost-tag, mọi Deployment phải có topologySpreadConstraints), bạn cần một policy engine:
Kyverno — YAML policy, không cần Rego. Phù hợp đa số team.
OPA Gatekeeper — Rego (ngôn ngữ riêng), mạnh và linh hoạt nhưng học curve cao.
Ví dụ Kyverno policy chặn image không từ registry nội bộ:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: trusted-registry
spec:
validationFailureAction: Enforce
rules:
- name: check-image
match:
any:
- resources:
kinds: [Pod]
validate:
message: "Image phải từ registry.example.com"
pattern:
spec:
containers:
- image: "registry.example.com/*"
Pin image bằng digest (image@sha256:...), không latest.
Scan image trong CI: Trivy, Grype, Snyk.
Signature: Cosign + sigstore. Verify bằng admission policy (Kyverno verifyImages).
Minimal base image: distroless, alpine, chainguard, scratch.
Image pull policy: Always cho tag mutable, IfNotPresent cho tag pinned/digest.
Private registry credentials qua Secret dockerconfigjson bind vào SA.
Mặc định etcd lưu Secret plaintext base64. Bật encryption:
# /etc/kubernetes/enc/enc.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
providers:
- aescbc:
keys:
- name: key1
secret: <BASE64_OF_32_BYTES>
- identity: {} # giải mã object cũ chưa encrypt
# kube-apiserver flag
--encryption-provider-config=/etc/kubernetes/enc/enc.yaml
Sau khi rotate, re-encrypt tất cả Secret cũ:
kubectl get secrets -A -o json | kubectl replace -f -
Tốt hơn nữa: dùng KMS provider (AWS KMS, GCP KMS, Azure Key Vault, Vault) — key không bao giờ ra khỏi KMS.
API server hỗ trợ audit log dạng JSON, cấu hình qua --audit-policy-file và --audit-log-path:
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
resources:
- group: ""
resources: ["secrets"]
- level: RequestResponse
verbs: ["create", "update", "patch", "delete"]
resources:
- group: "rbac.authorization.k8s.io"
Ship audit log sang Loki/Elastic và alert trên hành vi đáng ngờ: tạo ClusterRoleBinding với cluster-admin, xoá namespace prod, exec vào pod prod…
API server chỉ expose qua private network hoặc qua bastion + VPN.
Bật --anonymous-auth=false nếu không cần.
Worker node OS hardened: CIS benchmark, kernel update tự động (kured), không SSH thẳng từ Internet.
IAM cloud cho từng cluster, không gán AdministratorAccess.
Backup etcd encrypted, off-site.
Least privilege ở mọi tầng — User, SA, IAM cloud.
Pod Security Standards restricted cho namespace ứng dụng.
SecurityContext: runAsNonRoot, drop ALL capability, readOnlyRootFilesystem.
NetworkPolicy default-deny.
Encryption at rest cho etcd; ưu tiên KMS.
External secret store (Vault/cloud) + ESO/CSI Secret Store.
Image scan + signature, registry nội bộ, distroless base.
Audit log + alert hành vi nhạy cảm.
Định kỳ chạy kube-bench (CIS), kube-hunter, Trivy operator để báo cáo.
# Liệt kê pod chạy root
kubectl get pods -A -o jsonpath=\
'{range .items[*]}{.metadata.namespace}/{.metadata.name}: {.spec.containers[*].securityContext.runAsUser}{"\n"}{end}'
# CIS benchmark
kube-bench run --targets master,node
# Image vuln scan
trivy k8s --report summary cluster
# RBAC audit
kubectl get clusterrolebindings -o yaml | grep -A 5 cluster-admin
4C mental model: bảo vệ từ Cloud xuống Code.
RBAC cấp permissions tối thiểu, không ai ngoài cluster-admin thật.
SA + projected token cho workload identity; map sang IAM cloud.
SecurityContext + Pod Security Standards restricted mọi namespace app.
Kyverno/Gatekeeper cho policy custom.
Image signature, etcd encryption, audit log — đừng bỏ.
Phần 8: scheduling và autoscaling.