Bảo mật & Cường hóa
Tư thế bảo mật cấp production cho cụm Kubernetes BANA — bao phủ các hạn chế cấp pod, cô lập namespace, RBAC, chuỗi cung ứng image và ưu tiên scheduling.
Pod Security Standards (PSS)
Kubernetes Pod Security Standards định nghĩa ba profile: privileged, baseline, và restricted. BANA enforce chúng ở cấp namespace qua label.
Label Namespace
Production namespaces — restricted profile
# Production namespaces — restricted profile
apiVersion: v1
kind: Namespace
metadata:
name: nx-backend
labels:
app.kubernetes.io/part-of: bana
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
---
apiVersion: v1
kind: Namespace
metadata:
name: nx-app
labels:
app.kubernetes.io/part-of: bana
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restrictedStaging namespaces — also use restricted:latest enforce mode
# Staging namespaces — also use restricted:latest enforce mode
# (matches production — all namespaces including nx-broker use restricted)
apiVersion: v1
kind: Namespace
metadata:
name: nx-backend
labels:
app.kubernetes.io/part-of: bana
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/warn: restrictedINFO
Staging dùng cùng enforce mode restricted như production trên tất cả namespace (bao gồm nx-broker). Điều này đảm bảo đồng đẳng bảo mật và phát hiện vi phạm PSS sớm.
Tóm tắt Profile
| Profile | Namespaces | Sử dụng |
|---|---|---|
restricted | nx-backend, nx-app, nx-persistent, nx-broker, nx-search, nx-internal, nx-watcher (cả staging và production) | Tất cả namespace enforce profile restricted |
Template SecurityContext
Mọi container trong BANA phải chạy với SecurityContext đã được cường hóa. Template này là baseline cho tất cả Deployment backend và frontend.
Dockerfile dùng user bun (UID 1000) built-in từ base image oven/bun:*-alpine qua USER bun. SecurityContext của K8s khớp với điều này bằng runAsUser: 1000 / runAsGroup: 1000.
SecurityContext Cấp Pod
### Pod-Level SecurityContext
spec:
template:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000 # matches 'bun' user in Dockerfile
runAsGroup: 1000 # matches 'bun' group in Dockerfile
fsGroup: 1000
seccompProfile:
type: RuntimeDefaultSecurityContext Cấp Container
### Container-Level SecurityContext
containers:
- name: <service>
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALLThư mục Có thể Ghi
Vì readOnlyRootFilesystem: true ngăn ghi vào filesystem container, các service cần thư mục có thể ghi dùng volume emptyDir:
Since readOnlyRootFilesystem: true prevents writes to the ...
containers:
- name: <service>
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir:
sizeLimit: 64MiTIP
Bun và Node.js có thể ghi vào /tmp để cache. Luôn mount emptyDir tại /tmp khi dùng readOnlyRootFilesystem: true.
Ví dụ Deployment Đã Cường hóa Đầy đủ
Deployment: nx-commerce
apiVersion: apps/v1
kind: Deployment
metadata:
name: nx-commerce
namespace: nx-backend
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: commerce
template:
metadata:
labels:
app.kubernetes.io/name: commerce
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: commerce
image: bcr.bana.com.vn/nx-commerce:0.0.1
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
ports:
- containerPort: 3000
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir:
sizeLimit: 64MiCường hóa ServiceAccount
Mặc định, Kubernetes mount token ServiceAccount vào mọi pod. Hầu hết pod ứng dụng không cần gọi Kubernetes API, nên token này là bề mặt tấn công không cần thiết.
Tắt Auto-Mount
spec:
template:
spec:
automountServiceAccountToken: falseÁp dụng điều này cho tất cả Deployment backend và frontend.
ServiceAccount Chuyên dụng
Service cần truy cập Kubernetes API có ServiceAccount riêng với quyền tối thiểu:
ServiceAccount: nx-traefik
apiVersion: v1
kind: ServiceAccount
metadata:
name: nx-traefik
namespace: nx-internal
labels:
app.kubernetes.io/name: traefik
app.kubernetes.io/part-of: bana
automountServiceAccountToken: false # Traefik uses file provider, no K8s API access neededTIP
Traefik trong BANA dùng cấu hình file provider, không phải Kubernetes CRD discovery. Điều này có nghĩa nó không cần watch CRD traefik.io và yêu cầu truy cập Kubernetes API tối thiểu hoặc không có, giữ dấu chân RBAC nhỏ gọn.
| ServiceAccount | Namespace | Mục đích | Truy cập API |
|---|---|---|---|
default | Tất cả | App pods | Tắt (automountServiceAccountToken: false) |
nx-traefik | nx-internal | Traefik file provider | Tối thiểu (không cần watch CRD) |
nx-cert-manager | cert-manager | cert-manager | Secrets, Ingress, CRDs |
nx-prometheus | nx-watcher | Prometheus scraping | Read-only pods, services, endpoints |
RBAC
Service Account GitLab CI/CD
Pipeline CI/CD cần quyền kubectl apply phạm vi cho các namespace cụ thể — không bao giờ cluster-admin.
ServiceAccount: gitlab-deployer
apiVersion: v1
kind: ServiceAccount
metadata:
name: gitlab-deployer
namespace: nx-backend
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: gitlab-deployer
namespace: nx-backend
rules:
- apiGroups: ["", "apps", "batch", "autoscaling", "policy"]
resources:
- deployments
- services
- configmaps
- secrets
- jobs
- horizontalpodautoscalers
- poddisruptionbudgets
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["traefik.io"]
resources: ["ingressroutes", "middlewares"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: gitlab-deployer
namespace: nx-backend
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: gitlab-deployer
subjects:
- kind: ServiceAccount
name: gitlab-deployer
namespace: nx-backendLặp lại cho mỗi namespace mà pipeline triển khai đến (nx-app, nx-backend).
Role Read-Only cho Portainer
ClusterRole: nx-portainer-readonly
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nx-portainer-readonly
rules:
- apiGroups: ["", "apps", "batch", "autoscaling", "policy", "networking.k8s.io"]
resources: ["*"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: nx-portainer-readonly
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: nx-portainer-readonly
subjects:
- kind: ServiceAccount
name: portainer-readonly
namespace: portainerTóm tắt RBAC
| Principal | Phạm vi | Quyền | Mục đích |
|---|---|---|---|
gitlab-deployer | Theo namespace | Full CRUD trên deployments, services, configs, jobs | Pipeline CI/CD |
nx-portainer-readonly | Toàn cluster | Chỉ đọc tất cả resource + pod logs | Dashboard Portainer cho PM/QA |
nx-traefik | nx-internal | Tối thiểu (file provider, không watch CRD) | Routing API gateway |
nx-prometheus | Toàn cluster | Chỉ đọc pods, services, endpoints | Scraping metrics |
Chuỗi Cung ứng Image
Trivy Scanning trong CI
Mọi image được scan lỗ hổng trước khi được push lên registry. Xem Operations để biết tích hợp .gitlab-ci.yml đầy đủ.
Added to .gitlab-ci.yml after docker build, before push
# Added to .gitlab-ci.yml after docker build, before push
scan-image:
stage: scan
image:
name: aquasec/trivy:latest
entrypoint: [""]
script:
- trivy image --exit-code 1 --severity CRITICAL,HIGH --no-progress $REGISTRY/$SERVICE:$CI_COMMIT_SHORT_SHA
allow_failure: falseKý Image (cosign)
Ký image sau khi push để đảm bảo tính toàn vẹn:
# In CI after push
cosign sign --key env://COSIGN_PRIVATE_KEY $REGISTRY/$SERVICE:$CI_COMMIT_SHORT_SHAAdmission Control
Dùng admission controller (ví dụ Kyverno) để chặn image chưa scan hoặc chưa ký trong production:
ClusterPolicy: require-image-scan
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-image-scan
spec:
validationFailureAction: Enforce
background: true
rules:
- name: check-image-registry
match:
any:
- resources:
kinds:
- Pod
namespaces:
- nx-backend
- nx-app
validate:
message: "Images must come from the bcr.bana.com.vn registry"
pattern:
spec:
containers:
- image: "bcr.bana.com.vn/nx-*"
- name: block-latest-tag
match:
any:
- resources:
kinds:
- Pod
namespaces:
- nx-backend
validate:
message: "Production images must use a specific tag, not :latest"
pattern:
spec:
containers:
- image: "!*:latest"Image Pull Policy
| Môi trường | Policy | Lý do |
|---|---|---|
| Staging | IfNotPresent | Restart nhanh hơn, chấp nhận được việc cũ |
| Production | Always | Đảm bảo nội dung của tag chính xác khớp với registry |
PriorityClass
PriorityClass đảm bảo các service quan trọng tồn tại trước áp lực node và sự kiện OOM. Pod priority thấp hơn bị preempt trước.
Định nghĩa
PriorityClass: nx-system-critical
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: nx-system-critical
value: 1000000
globalDefault: false
description: "Data layer, ingress controller"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: nx-high
value: 500000
globalDefault: false
description: "Identity (JWKS), API gateway"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: nx-default
value: 100000
globalDefault: true
description: "Backend services & frontends"
---
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: nx-low
value: 10000
globalDefault: false
description: "Background workers, batch jobs"Phân phối
| PriorityClass | Value | Services |
|---|---|---|
nx-system-critical | 1,000,000 | nginx-ingress, cert-manager, StatefulSet tầng dữ liệu |
nx-high | 500,000 | identity, Traefik |
nx-default | 100,000 | commerce, finance, inventory, ledger, pricing, sale, signal, payment-api, client, bo, overture, sale-renderer |
nx-low | 10,000 | payment-worker, wiki |
Sử dụng trong Deployment
spec:
template:
spec:
priorityClassName: nx-high # identity, TraefikWARNING
Không bao giờ đặt preemptionPolicy: Never trên các class priority cao. Toàn bộ ý nghĩa của PriorityClass là service quan trọng có thể preempt service không quan trọng khi tranh chấp tài nguyên.
LimitRange
Mỗi namespace có LimitRange enforce resource request tối thiểu mỗi container, ngăn pod bị scheduled với tài nguyên quá thấp nguy hiểm.
| Namespace | Min CPU | Min Memory | Default CPU | Default Memory |
|---|---|---|---|---|
nx-backend | 50m | 64Mi | 500m | 512Mi |
nx-app | 25m | 32Mi | 200m | 256Mi |
nx-broker | 100m | 128Mi | 500m | 768Mi |
nx-persistent | 100m | 256Mi | 500m | 1Gi |
nx-search | 50m | 128Mi | 200m | 512Mi |
nx-internal | 50m | 64Mi | 500m | 512Mi |
nx-watcher | 25m | 32Mi | 200m | 256Mi |
TIP
Namespace nx-broker enforce tối thiểu cpu 100m và memory 128Mi mỗi container. Tất cả namespace đều có giới hạn tối thiểu và tối đa để ngăn lạm dụng tài nguyên.
Checklist
Dùng checklist này khi triển khai service mới hoặc audit service hiện có:
- [ ] SecurityContext Pod:
runAsNonRoot,runAsUser: 1000,seccompProfile: RuntimeDefault - [ ] SecurityContext Container:
allowPrivilegeEscalation: false,readOnlyRootFilesystem: true,capabilities.drop: [ALL] - [ ]
automountServiceAccountToken: false(trừ khi cần truy cập API) - [ ] ServiceAccount riêng nếu cần truy cập API
- [ ] Image chỉ từ
bcr.bana.com.vn/nx-* - [ ] Trivy scan pass (không lỗ hổng CRITICAL/HIGH)
- [ ] Đã gán PriorityClass
- [ ] Namespace có label PSS đúng
- [ ] Không dùng tag
:latesttrong manifest production