🎯 Mục tiêu bài thực hành
- Tạo RBAC với least privilege cho CI/CD ServiceAccount
- Viết ValidatingAdmissionPolicy bằng CEL
- Enforce Pod Security Admission Restricted mode
- Scan images và cluster với Trivy
- Deploy Falco và test runtime detection
Lab 1: RBAC — Read-only ServiceAccount
kubectl create namespace lab6Tạo ServiceAccount cho monitoring tool (chỉ cần đọc)
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: ServiceAccount metadata: name: monitoring-sa namespace: lab6
ClusterRole: chỉ đọc pods, services, nodes
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: monitoring-reader rules:
- apiGroups: [""] resources: ["pods", "services", "endpoints", "nodes"] verbs: ["get", "list", "watch"]
- apiGroups: ["apps"] resources: ["deployments", "statefulsets", "daemonsets"] verbs: ["get", "list", "watch"]
- apiGroups: ["metrics.k8s.io"] resources: ["pods", "nodes"] verbs: ["get", "list"]
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: monitoring-binding subjects:
- kind: ServiceAccount name: monitoring-sa namespace: lab6 roleRef: kind: ClusterRole name: monitoring-reader apiGroup: rbac.authorization.k8s.io EOF
Test permissions
kubectl auth can-i get pods --as=system:serviceaccount:lab6:monitoring-sa kubectl auth can-i delete deployments --as=system:serviceaccount:lab6:monitoring-sa
Expected: no
kubectl auth can-i create secrets --as=system:serviceaccount:lab6:monitoring-sa
Expected: no
Lab 2: RBAC cho CI/CD Deployer
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: ServiceAccount metadata: name: cicd-deployer namespace: lab6 --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: deployer namespace: lab6 rules: # Chỉ có thể deploy (không đọc secrets) - apiGroups: ["apps"] resources: ["deployments"] verbs: ["get", "list", "create", "update", "patch"] - apiGroups: [""] resources: ["services", "configmaps"] verbs: ["get", "list", "create", "update", "patch"] - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: cicd-binding namespace: lab6 subjects: - kind: ServiceAccount name: cicd-deployer namespace: lab6 roleRef: kind: Role name: deployer apiGroup: rbac.authorization.k8s.io EOFVerify: có thể deploy nhưng không đọc secrets
kubectl auth can-i create deployments -n lab6 --as=system:serviceaccount:lab6:cicd-deployer kubectl auth can-i get secrets -n lab6 --as=system:serviceaccount:lab6:cicd-deployer
Lab 3: ValidatingAdmissionPolicy
# Policy 1: Chặn :latest tag cat <<EOF | kubectl apply -f - apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicy metadata: name: no-latest-image spec: failurePolicy: Fail matchConstraints: resourceRules: - apiGroups: ["apps"] apiVersions: ["v1"] operations: ["CREATE", "UPDATE"] resources: ["deployments"] validations: - expression: | object.spec.template.spec.containers.all(c, !c.image.endsWith(":latest") && c.image.contains(":") ) message: "Tất cả container images phải có specific tag, không dùng :latest" --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicyBinding metadata: name: no-latest-image-binding spec: policyName: no-latest-image validationActions: [Deny] matchResources: namespaceSelector: matchLabels: environment: production EOFLabel namespace lab6 là production
kubectl label namespace lab6 environment=production
Test: deploy với :latest → bị reject
cat <<EOF | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: bad-deploy namespace: lab6 spec: replicas: 1 selector: matchLabels: app: bad template: metadata: labels: app: bad spec: containers: - name: app image: nginx:latest # vi phạm policy! EOF
Error: Tất cả container images phải có specific tag
Deploy với tag hợp lệ → OK
cat <<EOF | kubectl apply -f - apiVersion: apps/v1 kind: Deployment metadata: name: good-deploy namespace: lab6 spec: replicas: 1 selector: matchLabels: app: good template: metadata: labels: app: good spec: containers: - name: app image: nginx:1.27 # tag hợp lệ EOF
Lab 4: Pod Security Admission — Restricted Mode
# Tạo namespace với PSA Restricted
kubectl create namespace secure-ns
kubectl label namespace secure-ns \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/warn=restricted
Deploy non-compliant pod (sẽ bị reject)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: bad-pod
namespace: secure-ns
spec:
containers:
- name: app
image: nginx:1.27
Không có securityContext → vi phạm Restricted
EOF
Error: violates PodSecurity "restricted:latest"
Deploy compliant pod
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: good-pod
namespace: secure-ns
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: nginx:1.27
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
volumeMounts:
- name: cache mountPath: /var/cache/nginx
- name: run mountPath: /var/run
- name: tmp mountPath: /tmp volumes:
- name: cache emptyDir: {}
- name: run emptyDir: {}
name: tmp emptyDir: {} EOF
Lab 5: Trivy Scan
# Cài Trivy curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/binScan image
trivy image --severity HIGH,CRITICAL nginx:1.27
Scan Kubernetes manifests trong thư mục
mkdir -p /tmp/manifests kubectl get deployment good-deploy -n lab6 -o yaml > /tmp/manifests/deployment.yaml trivy config /tmp/manifests/
Scan running cluster (cần kubectl access)
trivy k8s --namespace lab6 cluster
Lab 6: Falco Runtime Detection
# Cài Falco helm repo add falcosecurity https://falcosecurity.github.io/charts helm install falco falcosecurity/falco \ --namespace falco \ --create-namespace \ --set driver.kind=modern_ebpf # cho kernel mới (5.8+)kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=falco -n falco --timeout=120s
Theo dõi Falco logs
kubectl logs -n falco -l app.kubernetes.io/name=falco -f &
Trigger alert: spawn shell trong container
POD=$(kubectl get pods -n lab6 -l app=good -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || kubectl run test-pod --image=nginx:1.27 -n lab6 --dry-run=client -o name) kubectl exec -n lab6 deploy/good-deploy -- sh -c "id"
Xem Falco alert:
Notice A shell was spawned in a container with an attached terminal
(user=root k8s.ns=lab6 k8s.pod=good-deploy-xxx container=app)
Test: đọc sensitive file
kubectl exec -n lab6 deploy/good-deploy -- cat /etc/passwd 2>/dev/null || true
Falco sẽ detect access to /etc/passwd
Cleanup
kubectl delete namespace lab6 secure-ns
kubectl delete validatingadmissionpolicy no-latest-image
kubectl delete validatingadmissionpolicybinding no-latest-image-binding
kubectl delete clusterrole monitoring-reader
kubectl delete clusterrolebinding monitoring-binding
helm uninstall falco -n falco
Tổng kết
- ✅ RBAC least privilege cho monitoring và CI/CD roles
- ✅ ValidatingAdmissionPolicy: chặn :latest images với CEL
- ✅ PSA Restricted mode: enforce hardened security
- ✅ Trivy: scan images và manifests
- ✅ Falco: runtime threat detection với eBPF