iT邦幫忙

2026 iThome 鐵人賽

DAY 27
0
Kubernetes

資安這條路:從攻擊者視角看 Kubernetes系列 第 27 篇

Day 27|NetworkPolicy + Kyverno + CIS Benchmark:策略引擎全覆蓋

  • 分享至 

  • xImage
  •  

資安這條路:從攻擊者視角看 Kubernetes

三大策略引擎聯防——從網路分段到 Admission Control 到合規基線。

學習目標

完成本日實作後,你將能夠:

  • 設計 NetworkPolicy 實作 zero-trust 網路分段
  • 部署 Kyverno 策略並切換 Audit / Enforce 模式
  • 使用 kube-bench 執行 CIS Benchmark 掃描並解讀結果
  • 驗證三層策略引擎的聯防效果
  • 將 CIS 控制項對應到 ATT&CK 技術

閱讀前置條件

本篇整合 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 → 本篇。


基礎知識:Admission Control 與合規框架

K8s Admission Control 流程

每個送到 API Server 的請求,在寫入 etcd 之前會經過一條完整的檢查鏈:

kubectl apply -f pod.yaml

為什麼 Admission Control 是最佳防禦點

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%。

內建 vs 外部 Admission Controller

特性 PodSecurity(內建) Kyverno(外部 Webhook) OPA/Gatekeeper(外部 Webhook)
安裝方式 K8s 內建,直接啟用 Helm 安裝 Helm 安裝
規則語言 三個固定等級(Privileged/Baseline/Restricted) YAML(ClusterPolicy CRD) Rego(自訂語言)
靈活性 低(只能選等級) 高(可自訂任意規則) 高(但 Rego 學習曲線陡)
Mutate 能力 無 有(可注入/修改欄位) 有限
Generate 能力 無 有(可自動產生 NetworkPolicy 等) 無
KOAD 使用 ✓(基線) ✓(7 條策略) ✗

CIS Benchmark 是什麼

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)」的完整防線。


NetworkPolicy 實戰

Default Deny 原則

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

DNS 白名單策略

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

跨 Namespace 隔離

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

CIDR 封鎖(阻擋 Metadata API)

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

NetworkPolicy 設計原則

原則 說明
Default Deny 每個 Namespace 都從 deny-all 開始
最小權限 只開放已知的、必要的通訊路徑
標籤選擇 用 label 選擇 Pod,而非 IP
DNS 白名單 永遠允許 DNS(port 53)
分 Namespace 不同環境(dev/staging/prod)用不同 Namespace + 不同策略

超越 NetworkPolicy:mTLS 與 Service Mesh

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。

驗證 NetworkPolicy

# 主機終端
# 確認策略已部署
$ 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 阻擋

Kyverno 全策略回顧

KOAD 的 7 條策略

策略 作用 阻擋的攻擊
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

Kyverno 規則詳解

以 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"

Mutating 規則:自動修補

除了 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: "?*"

Audit → Enforce 遷移策略

階段 模式 目的 建議時長
1. 觀察期 Audit 收集違規資料,不影響現有工作負載 2-4 週
2. 修復期 Audit 修復違規工作負載 1-2 週
3. 試行期 Enforce (staging) 在 staging 環境先啟用 1 週
4. 上線 Enforce (production) 生產環境啟用 持續

查看 Kyverno 違規報告

# 主機終端
# 查看 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 Kubernetes Benchmark

什麼是 CIS Benchmark

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 掃描

# 主機終端
# 在叢集內執行 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

自動化 CIS 合規掃描

# 定期掃描的 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

kube-bench 結果解讀

== 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 修復失敗項 → 減少整體攻擊面
三者聯防 多重阻擋點 → 攻擊者無法繞過所有層

實測:三層聯防 vs 攻擊鏈

# 主機終端
# 測試 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 已強化

CKS 考點

考點 本日內容 權重
NetworkPolicy Default deny + 白名單 Cluster Setup 15%
Admission Control Kyverno Enforce 策略 Minimize Microservice 20%
CIS Benchmark kube-bench 掃描和修復 Cluster Hardening 15%

CKS 實戰提示

  • NetworkPolicy 語法必背:podSelector、namespaceSelector、policyTypes、ingress、egress 的結構
  • 注意 AND vs OR:同一個 from 陣列元素內的條件是 AND,不同元素之間是 OR
  • Egress deny 會擋 DNS:記得額外允許 port 53(UDP + TCP)
  • kube-bench 常考:知道怎麼跑、怎麼讀結果、怎麼修復 FAIL 項目

常見問題排除

問題 現象 解法
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

本日小結

完成度自我檢查

  • [ ] NetworkPolicy default-deny-all 已部署且能阻擋跨 Pod 通訊
  • [ ] 能寫出允許特定流量的 NetworkPolicy(ingress + egress)
  • [ ] Kyverno 策略已部署且 PolicyReport 有輸出
  • [ ] 能切換 Kyverno 策略的 Audit / Enforce 模式
  • [ ] kube-bench 已執行且能解讀 PASS / FAIL / WARN 結果
  • [ ] 完成三層聯防測試,理解各層的防禦覆蓋範圍

今日重點回顧

  1. NetworkPolicy:網路層分段,即使 RBAC 失守也能阻擋通訊
  2. Kyverno:Admission 層控制,阻擋危險 Pod 配置進入叢集
  3. CIS Benchmark:持續合規檢查,確保叢集配置符合安全基線

三者各自獨立工作,但聯合起來形成立體防禦。KOAD 攻擊鏈的測試證明了:任何單一防禦層被繞過,其他層仍然有效。

明天 Day 28 進入全系列總結——KOAD 攻防矩陣:35 場景 × ATT&CK × D3FEND 完整對照。



上一篇
Day 26|Falco Talon + Tetragon + Audit Log:三層偵測堆疊
下一篇
Day 28|KOAD 攻防矩陣:35 場景 × ATT&CK × D3FEND 完整對照
系列文
資安這條路:從攻擊者視角看 Kubernetes 共 28 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言