
三大策略引擎聯防——從網路分段到 Admission Control 到合規基線。
完成本日實作後,你將能夠:
本篇整合 NetworkPolicy、Kyverno、CIS Benchmark 三大策略引擎。建議的最低閱讀量:
| 等級 | 天數 | 內容 |
|---|---|---|
| 必備 | Day 1 | ATT&CK 概念 + 靶場部署 |
| 必備 | Day 3、5 | Initial Access 防禦 + Execution 防禦(NetworkPolicy 和 Kyverno 基礎) |
| 建議 | Day 24 | Kill Chain(理解策略引擎在攻擊鏈中的阻斷效果) |
| 可跳過 | Day 25–26 | Falco / Tetragon(偵測層,與策略層獨立) |
快速路徑:如果時間有限,至少讀完 Day 1 → 3 → 5 → 本篇。
每個送到 API Server 的請求,在寫入 etcd 之前會經過一條完整的檢查鏈:

Admission Control 攔截在資源建立之前——危險的 Pod 根本不會被排程和執行。相比之下,Runtime 偵測(Falco)是在攻擊已經發生時才告警。兩者的關係:
| 特性 | Admission Control(預防) | Runtime Detection(偵測) |
|---|---|---|
| 作用時機 | Pod 建立前 | Pod 運行中 |
| 代表工具 | Kyverno、OPA、PodSecurity | Falco、Tetragon |
| 效果 | 阻擋危險配置進入叢集 | 發現已發生的惡意行為 |
| 覆蓋範圍 | 只能檢查 API 請求的靜態配置 | 可偵測動態行為(程式執行、網路連線) |
| 局限 | 無法偵測 0-day 或配置正常的攻擊 | 只能告警,不能阻止已發生的事 |
最佳實踐是兩者都部署——Admission Control 擋住 90% 的低垂果實,Runtime Detection 抓住剩下的 10%。
| 特性 | PodSecurity(內建) | Kyverno(外部 Webhook) | OPA/Gatekeeper(外部 Webhook) |
|---|---|---|---|
| 安裝方式 | K8s 內建,直接啟用 | Helm 安裝 | Helm 安裝 |
| 規則語言 | 三個固定等級(Privileged/Baseline/Restricted) | YAML(ClusterPolicy CRD) | Rego(自訂語言) |
| 靈活性 | 低(只能選等級) | 高(可自訂任意規則) | 高(但 Rego 學習曲線陡) |
| Mutate 能力 | 無 | 有(可注入/修改欄位) | 有限 |
| Generate 能力 | 無 | 有(可自動產生 NetworkPolicy 等) | 無 |
| KOAD 使用 | ✓(基線) | ✓(7 條策略) | ✗ |
CIS(Center for Internet Security)是一個非營利組織,發布各種 IT 系統的安全配置基準。CIS Kubernetes Benchmark 由業界專家共同維護,是 K8s 安全合規的事實標準。
CIS K8s Benchmark 的結構:
| 章節 | 檢查對象 | 範例 |
|---|---|---|
| 1. Control Plane | API Server、Controller Manager、Scheduler、etcd | anonymous-auth=false |
| 2. etcd | etcd 獨立章節 | peer-auto-tls=false |
| 3. Control Plane Configuration | Authentication、Admission | 啟用 PodSecurity |
| 4. Worker Nodes | kubelet、proxy | protect-kernel-defaults=true |
| 5. Policies | RBAC、Pod Security、NetworkPolicy、Secrets | 最小權限 SA |
評分機制:
| 標記 | 意義 | 處理方式 |
|---|---|---|
| Scored + PASS | 通過安全檢查 | 維持 |
| Scored + FAIL | 未通過,需修復 | 立即處理 |
| Not Scored + WARN | 建議項目(非強制) | 評估後決定 |
kube-bench 是最常用的自動化 CIS 掃描工具——它以 K8s Job 的方式運行,對照 CIS Benchmark 逐項檢查叢集配置,產出 PASS/FAIL/WARN 報告。

| 層級 | 作用時機 | 防禦能力 | 限制 |
|---|---|---|---|
| Kyverno | Pod 建立時(Admission) | 阻擋危險配置進入叢集 | 已存在的 Pod 不受影響 |
| NetworkPolicy | Pod 運行時(Network) | 限制 Pod 網路通訊 | 不能阻擋 Pod 內部行為 |
| CIS Benchmark | 定期掃描(Compliance) | 發現配置偏差 | 不能即時阻擋,只能報告 |
三者結合才能形成「進不來(Kyverno)→ 出不去(NetworkPolicy)→ 藏不住(CIS)」的完整防線。
KOAD 攻擊鏈測試驗證了一個關鍵事實:default-deny-all 阻斷了整條攻擊鏈。 即使 attacker pod 擁有 cluster-admin 權限,NetworkPolicy 仍然在網路層阻擋了所有通訊。
# 預設拒絕所有進出流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
在 default deny 的基礎上,逐一開放必要的通訊:
# 允許 frontend → backend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080
---
# 允許 backend → database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-backend-to-db
namespace: production
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: backend
ports:
- port: 5432
default-deny Egress 會連 DNS 都擋住,所以必須明確允許 DNS:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
S30(Lateral Movement)場景展示了跨 Namespace 攻擊的風險。用 namespaceSelector 精確控制跨 NS 通訊:
# 只允許 monitoring namespace 的 Prometheus 抓取 metrics
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-prometheus-scrape
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- port: 9090
S01 場景的 SSRF 攻擊目標是雲端 Metadata API(169.254.169.254)。用 Egress CIDR 阻擋:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: block-metadata-api
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32 # 雲端 Metadata API
| 原則 | 說明 |
|---|---|
| Default Deny | 每個 Namespace 都從 deny-all 開始 |
| 最小權限 | 只開放已知的、必要的通訊路徑 |
| 標籤選擇 | 用 label 選擇 Pod,而非 IP |
| DNS 白名單 | 永遠允許 DNS(port 53) |
| 分 Namespace | 不同環境(dev/staging/prod)用不同 Namespace + 不同策略 |
NetworkPolicy 運作在 L3/L4(IP + Port),能控制哪些 Pod 可以互連,但無法驗證對方的身份——攻擊者偽造 label 就能繞過。
Service Mesh(Istio / Linkerd)在此之上加入 mTLS(mutual TLS),提供 L7 層級的雙向身份驗證:
| 層級 | NetworkPolicy | Service Mesh mTLS |
|---|---|---|
| 運作層 | L3/L4(IP/Port) | L7(HTTP/gRPC) |
| 身份驗證 | 無(信任 label) | 有(X.509 憑證) |
| 加密 | 無 | TLS 加密所有流量 |
| 細粒度策略 | Pod/Namespace | 可依 HTTP path/method 授權 |
選型建議: 先部署 NetworkPolicy(成本低、效果明顯),再評估是否需要 Service Mesh。對大多數環境,NetworkPolicy + RBAC 已足夠;多團隊、零信任要求高的環境才需要 mTLS。不想上 Service Mesh 也可以用 SPIFFE/SPIRE 單獨實作 mTLS。
# 主機終端
# 確認策略已部署
$ kubectl get networkpolicies -n production
NAME POD-SELECTOR AGE
default-deny-all <none> 5m
allow-frontend-to-backend app=backend 5m
allow-backend-to-db app=database 5m
allow-dns <none> 5m
# 測試連通性——frontend → backend(應允許)
$ kubectl exec -n production frontend-pod -- \
wget -q -O- http://backend:8080/health
OK
# 測試連通性——frontend → database(應拒絕)
$ kubectl exec -n production frontend-pod -- \
wget -q -O- --timeout=3 http://database:5432
wget: can't connect to remote host: Connection timed out
# ✓ frontend 不能直連 database
# 測試 Metadata API 封鎖
$ kubectl exec -n production frontend-pod -- \
curl -s --max-time 3 http://169.254.169.254/latest/meta-data/
curl: (28) Connection timed out
# ✓ SSRF 被 NetworkPolicy 阻擋
| 策略 | 作用 | 阻擋的攻擊 |
|---|---|---|
| koad-block-privileged | 禁止特權容器 | S13, S14, S16, S18 |
| koad-block-hostpath | 禁止 hostPath | S15, S16 |
| koad-block-host-namespaces | 禁止 hostPID/Net/IPC | S17 |
| koad-require-trusted-registry | 映像白名單 | S12, S35 |
| koad-require-run-as-nonroot | 要求非 root | 所有場景 |
| koad-disable-automount-sa | 關閉 SA 自動掛載 | S05, S26 |
| koad-drop-capabilities | 要求 drop ALL | S17, S18 |
以 koad-block-privileged 為例,拆解 Kyverno 規則的四個組成部分:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: koad-block-privileged
annotations:
policies.kyverno.io/title: Block Privileged Containers
policies.kyverno.io/category: Pod Security
policies.kyverno.io/severity: high
policies.kyverno.io/description: >-
Privileged containers can access host resources and escape the
container boundary. This policy blocks privileged: true.
spec:
validationFailureAction: Audit # Audit 模式:記錄但不阻擋
background: true # 掃描已存在的資源
rules:
- name: block-privileged
match:
any:
- resources:
kinds: ["Pod"]
validate:
message: >-
Privileged containers are not allowed.
Set securityContext.privileged to false.
pattern:
spec:
=(initContainers):
- =(securityContext):
=(privileged): "false"
containers:
- =(securityContext):
=(privileged): "false"
除了 Validate(驗證),Kyverno 還支援 Mutate(自動修改):
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: koad-auto-add-security-defaults
spec:
rules:
- name: add-security-context
match:
any:
- resources:
kinds: ["Pod"]
mutate:
patchStrategicMerge:
spec:
containers:
- (name): "*"
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
capabilities:
drop: ["ALL"]
這條 Mutate 規則會自動為所有 Pod 加上安全的 securityContext 預設值。
除了 KOAD 的 7 條,生產環境還應加入:
# 禁止 :latest tag
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-tag
spec:
validationFailureAction: Enforce
rules:
- name: disallow-latest
match:
any:
- resources:
kinds: ["Pod"]
validate:
message: "Images with ':latest' tag are not allowed"
pattern:
spec:
containers:
- image: "!*:latest"
# 要求設定 resource limits
- name: require-limits
validate:
message: "Resource limits are required"
pattern:
spec:
containers:
- resources:
limits:
memory: "?*"
cpu: "?*"
| 階段 | 模式 | 目的 | 建議時長 |
|---|---|---|---|
| 1. 觀察期 | Audit | 收集違規資料,不影響現有工作負載 | 2-4 週 |
| 2. 修復期 | Audit | 修復違規工作負載 | 1-2 週 |
| 3. 試行期 | Enforce (staging) | 在 staging 環境先啟用 | 1 週 |
| 4. 上線 | Enforce (production) | 生產環境啟用 | 持續 |
# 主機終端
# 查看 PolicyReport(Audit 模式下的違規記錄)
$ kubectl get policyreport -A
NAMESPACE NAME PASS FAIL WARN ERROR SKIP AGE
koad polr-ns-koad 12 7 0 0 0 1h
default polr-ns-default 8 0 0 0 0 1h
# 查看違規詳情
$ kubectl get policyreport polr-ns-koad -n koad -o yaml | \
yq '.results[] | select(.result == "fail") | .message'
Privileged containers are not allowed.
HostPath volumes are not allowed.
Host namespaces (hostPID, hostIPC, hostNetwork) are not allowed.
# 查看 ClusterPolicyReport
$ kubectl get clusterpolicyreport
NAME PASS FAIL WARN ERROR SKIP AGE
clusterpol-report 45 12 3 0 0 1h
CIS(Center for Internet Security)Kubernetes Benchmark 是業界標準的 K8s 安全配置基線,包含 200+ 項檢查:
| 領域 | 項目數 | 說明 |
|---|---|---|
| Control Plane | ~40 | API Server、Controller Manager、Scheduler |
| etcd | ~10 | 加密、認證、peer 通訊 |
| Control Plane Config | ~15 | 認證、加密、Audit Log |
| Worker Nodes | ~30 | kubelet、proxy 配置 |
| Policies | ~30 | RBAC、PSP/PSS、NetworkPolicy |
# 主機終端
# 在叢集內執行 kube-bench
$ kubectl apply -f - <<'EOF'
apiVersion: batch/v1
kind: Job
metadata:
name: kube-bench
namespace: default
spec:
template:
spec:
hostPID: true
containers:
- name: kube-bench
image: aquasec/kube-bench:latest
command: ["kube-bench"]
volumeMounts:
- name: var-lib-kubelet
mountPath: /var/lib/kubelet
readOnly: true
- name: etc-kubernetes
mountPath: /etc/kubernetes
readOnly: true
restartPolicy: Never
volumes:
- name: var-lib-kubelet
hostPath:
path: /var/lib/kubelet
- name: etc-kubernetes
hostPath:
path: /etc/kubernetes
EOF
# 查看結果
$ kubectl logs job/kube-bench
[INFO] 1 Control Plane Security Configuration
[INFO] 1.1 Control Plane Node Configuration Files
[PASS] 1.1.1 Ensure API server pod spec permissions are set to 644
[PASS] 1.1.2 Ensure API server pod spec ownership is set to root:root
[FAIL] 1.1.3 Ensure controller-manager pod spec permissions are set to 644
...
== Summary total ==
45 checks PASS
12 checks FAIL
23 checks WARN
| 項目 | 失敗原因 | 修復方法 | 涉及 KOAD 場景 |
|---|---|---|---|
| 1.2.16 | Audit Log 未啟用 | --audit-log-path + --audit-policy-file |
Day 26 Audit Log |
| 1.2.18 | 匿名認證未關閉 | --anonymous-auth=false |
S02 kubelet 暴露 |
| 4.2.1 | kubelet 匿名認證 | kubelet config: anonymous.enabled: false |
S02 kubelet 暴露 |
| 5.2.2 | 未設定 PSS | Namespace label pod-security.kubernetes.io/enforce |
Day 12 逃逸防禦 |
| 5.4.1 | 未設定 NetworkPolicy | 部署 default-deny-all | 本日 NetworkPolicy |
# 定期掃描的 CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: kube-bench-daily
namespace: kube-system
spec:
schedule: "0 6 * * *" # 每天早上 6 點
jobTemplate:
spec:
template:
spec:
hostPID: true
containers:
- name: kube-bench
image: aquasec/kube-bench:latest
command:
- /bin/sh
- -c
- |
kube-bench --json | \
curl -X POST -H "Content-Type: application/json" \
-d @- https://siem.company.com/cis-report
restartPolicy: Never
== Summary ==
45 checks PASS → 符合 CIS 標準
12 checks FAIL → 必須修復的安全問題
23 checks WARN → 需要人工檢查(通常是可選的強化項)
FAIL 類型:
- [FAIL] 1.2.18 Ensure --anonymous-auth argument is set to false
Remediation: Edit API server pod spec and set --anonymous-auth=false
→ 這項直接對應 KOAD S02(kubelet 暴露),不修復就有匿名存取風險。
用 KOAD 攻擊鏈的結果驗證:
| 防禦層 | 獨立阻擋效果 |
|---|---|
| Kyverno Enforce | 阻擋 S13–S18 特權/hostPath Pod 建立 → 攻擊在 TA0004 停止 |
| NetworkPolicy | 阻擋 attacker pod 網路通訊 → 攻擊在 TA0001 停止 |
| CIS Benchmark | 修復失敗項 → 減少整體攻擊面 |
| 三者聯防 | 多重阻擋點 → 攻擊者無法繞過所有層 |
# 主機終端
# 測試 1:無任何防禦
$ kubectl exec -n koad attacker -- bash /attack/attack-chain.sh
[S01] SSRF → Metadata API ✓ SUCCESS
[S05] kubectl → cluster-admin ✓ SUCCESS
[S13] Privileged container escape ✓ SUCCESS
[S35] Deploy cryptominer ✓ SUCCESS
攻擊鏈完成:4/4 步驟成功
# 測試 2:只啟用 NetworkPolicy
$ kubectl apply -f defense/network-policies/
$ kubectl exec -n koad attacker -- bash /attack/attack-chain.sh
[S01] SSRF → Metadata API ✗ BLOCKED (NetworkPolicy)
攻擊在第 1 步就被阻擋
# 測試 3:只啟用 Kyverno Enforce
$ kubectl exec -n koad attacker -- bash /attack/attack-chain.sh
[S01] SSRF → Metadata API ✓ SUCCESS
[S05] kubectl → cluster-admin ✓ SUCCESS
[S13] Privileged container escape ✗ BLOCKED (Kyverno)
攻擊在第 3 步被阻擋
# 測試 4:三層全部啟用
攻擊在第 1 步就被 NetworkPolicy 阻擋
即使繞過 NetworkPolicy,第 3 步被 Kyverno 阻擋
即使繞過 Kyverno,CIS 修復後 kubelet 和 API Server 已強化
| 考點 | 本日內容 | 權重 |
|---|---|---|
| NetworkPolicy | Default deny + 白名單 | Cluster Setup 15% |
| Admission Control | Kyverno Enforce 策略 | Minimize Microservice 20% |
| CIS Benchmark | kube-bench 掃描和修復 | Cluster Hardening 15% |
podSelector、namespaceSelector、policyTypes、ingress、egress 的結構from 陣列元素內的條件是 AND,不同元素之間是 OR| 問題 | 現象 | 解法 |
|---|---|---|
| kube-bench Job 卡在 Pending | kubectl get pods 顯示 Pending |
確認 Node 有足夠資源;minikube 環境下確認 hostPID: true 被允許:kubectl describe pod kube-bench 查看 Events |
| kube-bench 結果全部 WARN | 所有項目都是 WARN 而非 PASS/FAIL | 可能未正確掛載 hostPath(/var/lib/kubelet 和 /etc/kubernetes),確認 volumeMounts 路徑正確 |
| NetworkPolicy 不生效 | 套用 deny-all 後 Pod 仍可通訊 | CNI 不支援 NetworkPolicy(例如 flannel 原生不支援)。minikube 請用 minikube start --cni=calico |
| Kyverno PolicyReport 為空 | kubectl get policyreport 無結果 |
確認 Kyverno 版本 >= 1.7,且策略設定 background: true;等待 1-2 分鐘讓背景掃描完成 |
| Kyverno Enforce 擋住系統 Pod | kube-system Pod 建立失敗 | 策略的 exclude 應排除系統 Namespace:加入 exclude: resources: namespaces: ["kube-system", "falco"] |
| DNS 解析失敗 | 套用 Egress deny-all 後 Pod 內 DNS 查詢逾時 | 必須額外部署 allow-dns NetworkPolicy 允許 port 53 到 kube-system Namespace |
default-deny-all 已部署且能阻擋跨 Pod 通訊三者各自獨立工作,但聯合起來形成立體防禦。KOAD 攻擊鏈的測試證明了:任何單一防禦層被繞過,其他層仍然有效。
明天 Day 28 進入全系列總結——KOAD 攻防矩陣:35 場景 × ATT&CK × D3FEND 完整對照。