在 Kubernetes 叢集的運作過程中,Secret 資源承載著應用程式最敏感的資訊,包含資料庫連線字串、API 金鑰、TLS 憑證等關鍵憑證。這些資訊一旦外洩或被不當存取,將對整個系統安全造成嚴重威脅。然而,Kubernetes 預設的 Secret 管理機制存在多個潛在的安全風險點。Secret 資料雖然經過 Base64 編碼,但這並非真正的加密,任何能夠讀取 Secret 的使用者都能輕易解碼取得原始內容。在 etcd 儲存層面,若未啟用加密機制,Secret 資料將以明文形式儲存,讓具有 etcd 存取權限的人員能夠直接讀取所有敏感資訊。本文將深入探討 Kubernetes Secret 的安全管理策略,從底層的加密機制到應用層的存取控制,從開發流程的整合到營運階段的監控稽核,提供完整的企業級敏感資料保護方案。透過系統化的安全實務應用,我們將建構出多層防禦體系,確保敏感資訊在整個生命週期中都能獲得適當的保護。

Kubernetes Secret 的安全風險與防護策略

在深入探討具體的防護機制之前,我們需要全面理解 Kubernetes Secret 面臨的安全威脅與風險來源。Secret 的安全風險可以從多個維度來分析,包含儲存安全、傳輸安全、存取控制與使用安全等面向。每個維度都存在特定的威脅模型與對應的防護策略。

從儲存安全的角度來看,Kubernetes 將 Secret 資料儲存在 etcd 資料庫中。在預設配置下,這些資料以未加密的形式存放,任何能夠存取 etcd 資料庫的人員都能直接讀取所有 Secret 內容。這包含了叢集管理員、具有 etcd 備份存取權限的營運人員,以及可能入侵到 etcd 伺服器的攻擊者。更嚴重的是,etcd 的備份檔案通常也包含這些未加密的資料,如果備份管理不當,可能導致敏感資訊的大規模外洩。

傳輸安全方面,雖然 Kubernetes API Server 與 etcd 之間的通訊預設使用 TLS 加密,但在 Pod 內部存取 Secret 時,資料會被解密並以環境變數或檔案系統掛載的形式提供給容器。這個過程中,Secret 資料會以明文形式存在於容器的記憶體與檔案系統中,可能透過容器逃逸、記憶體傾印等攻擊手段被竊取。

存取控制的挑戰在於,Kubernetes 的 RBAC 機制雖然強大,但配置複雜且容易出錯。過於寬鬆的權限設定可能讓不應該存取特定 Secret 的使用者或服務帳戶取得敏感資訊。更隱蔽的風險來自於 ServiceAccount 的預設行為,每個 Pod 都會自動掛載一個包含 API 認證令牌的 Secret,如果應用程式存在漏洞,攻擊者可能利用這個令牌來存取更多的叢集資源。

@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi 300
skinparam shadowing false
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 14
skinparam roundcorner 8
skinparam minClassWidth 140

package "威脅來源層" {
  actor "惡意使用者" as Attacker
  actor "內部人員" as Insider
  cloud "外部攻擊" as External
}

package "攻擊向量層" {
  rectangle "etcd 直接存取" as EtcdAccess
  rectangle "API Server\n未授權存取" as APIAccess
  rectangle "容器逃逸" as ContainerEscape
  rectangle "記憶體傾印" as MemoryDump
  rectangle "備份檔案外洩" as BackupLeak
}

package "資料儲存層" {
  database "etcd\n資料庫" as ETCD
  rectangle "備份檔案" as Backup
  rectangle "日誌記錄" as Logs
}

package "資料使用層" {
  rectangle "API Server" as API
  rectangle "Pod\n運行時" as Pod
  rectangle "環境變數" as EnvVar
  rectangle "檔案系統\n掛載" as VolumeMount
}

package "防護機制層" {
  rectangle "etcd 靜態\n加密" as EtcdEncrypt
  rectangle "KMS\n整合" as KMS
  rectangle "RBAC\n存取控制" as RBAC
  rectangle "Network\nPolicy" as NetPolicy
  rectangle "Pod Security\nStandards" as PodSecurity
  rectangle "稽核日誌" as Audit
}

Attacker --> EtcdAccess
Attacker --> APIAccess
Attacker --> ContainerEscape
Insider --> EtcdAccess
Insider --> BackupLeak
External --> APIAccess

EtcdAccess --> ETCD
APIAccess --> API
ContainerEscape --> Pod
MemoryDump --> Pod
BackupLeak --> Backup

ETCD --> API
API --> Pod
Pod --> EnvVar
Pod --> VolumeMount

EtcdEncrypt -up-> ETCD : 保護
KMS -up-> EtcdEncrypt : 金鑰管理
RBAC -up-> API : 權限控制
NetPolicy -up-> Pod : 網路隔離
PodSecurity -up-> Pod : 安全策略
Audit -up-> API : 行為監控

@enduml

此威脅模型圖清楚展示了 Kubernetes Secret 面臨的多層次安全威脅與對應的防護機制。威脅來源包含外部攻擊者、惡意內部人員與系統漏洞。這些威脅透過不同的攻擊向量來竊取敏感資訊,包含直接存取 etcd、未授權的 API 呼叫、容器逃逸等。防護機制則需要在每個層級建立防禦措施,形成縱深防禦的安全架構。

etcd 靜態加密的實作與配置

保護儲存在 etcd 中的 Secret 資料是整個安全策略的基石。Kubernetes 提供了 etcd 靜態加密機制,能夠在資料寫入 etcd 前先進行加密,確保即使 etcd 資料庫或備份檔案被未授權存取,攻擊者也無法直接讀取 Secret 內容。這個機制需要在 API Server 層級進行配置,透過加密配置檔案來定義加密提供者與金鑰。

加密配置檔案定義了如何加密不同類型的資源。Kubernetes 支援多種加密提供者,包含 AES-CBC、AES-GCM、Secretbox 等對稱加密演算法,以及與外部金鑰管理系統整合的 KMS 提供者。在實務應用中,建議使用 AES-GCM 或 KMS 提供者,它們提供了更強的安全性與更好的效能表現。

# Kubernetes 加密配置檔案
# 定義 Secret 資源的加密策略
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  # 定義需要加密的資源類型
  - resources:
      # Secret 資源的加密設定
      - secrets
    # 定義加密提供者清單
    # Kubernetes 會依序嘗試使用這些提供者
    # 第一個提供者用於加密新資料
    # 其他提供者用於解密舊資料(金鑰輪換時使用)
    providers:
      # 第一層:使用 AES-GCM 加密
      # AES-GCM 提供認證加密,能夠偵測資料被篡改
      - aescbc:
          # 定義加密金鑰
          # 每個金鑰都有唯一的名稱與實際的金鑰值
          keys:
            # 目前使用的主要金鑰
            - name: key1
              # 金鑰內容必須是 32 位元組的 Base64 編碼字串
              # 可以使用以下指令產生:
              # head -c 32 /dev/urandom | base64
              secret: <BASE64_ENCODED_32_BYTE_KEY>
            # 保留舊金鑰以支援金鑰輪換
            # 當更換新金鑰時,舊金鑰仍需保留
            # 直到所有資料都重新加密為止
            - name: key2
              secret: <BASE64_ENCODED_32_BYTE_KEY_OLD>
      # 第二層:identity 提供者(不加密)
      # 用於讀取尚未加密的舊資料
      # 在完成加密遷移後應該移除此提供者
      - identity: {}

  # ConfigMap 資源的加密設定(可選)
  # ConfigMap 通常不包含敏感資訊
  # 但某些情況下可能需要加密保護
  - resources:
      - configmaps
    providers:
      # 使用相同的 AES-GCM 加密
      - aescbc:
          keys:
            - name: key1
              secret: <BASE64_ENCODED_32_BYTE_KEY>
      - identity: {}

這個加密配置檔案定義了完整的加密策略。重要的是理解加密提供者的順序,Kubernetes 總是使用清單中第一個提供者來加密新資料,而解密時會嘗試所有提供者直到成功。這個設計支援了金鑰輪換的需求,讓我們能夠在不中斷服務的情況下更換加密金鑰。

啟用 etcd 加密需要修改 API Server 的啟動參數,指定加密配置檔案的路徑。在使用 kubeadm 部署的叢集中,這些配置通常位於 /etc/kubernetes/manifests/kube-apiserver.yaml 檔案中。

# API Server 靜態 Pod 配置
# 啟用 etcd 加密功能
apiVersion: v1
kind: Pod
metadata:
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - name: kube-apiserver
    image: k8s.gcr.io/kube-apiserver:v1.28.0
    command:
      - kube-apiserver
      # 基本配置參數
      - --advertise-address=192.168.1.10
      - --allow-privileged=true
      - --authorization-mode=Node,RBAC
      - --client-ca-file=/etc/kubernetes/pki/ca.crt
      - --enable-admission-plugins=NodeRestriction
      - --enable-bootstrap-token-auth=true
      # etcd 連線設定
      - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
      - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
      - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
      - --etcd-servers=https://127.0.0.1:2379
      # 加密配置
      # 指定加密配置檔案的路徑
      - --encryption-provider-config=/etc/kubernetes/encryption/config.yaml
      # 其他安全相關設定
      - --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
      - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
      - --secure-port=6443
      - --service-account-key-file=/etc/kubernetes/pki/sa.pub
      - --service-account-signing-key-file=/etc/kubernetes/pki/sa.key
      - --service-cluster-ip-range=10.96.0.0/12
      - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
      - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
    # 資源限制
    resources:
      requests:
        cpu: 250m
        memory: 512Mi
    # 掛載卷
    volumeMounts:
      # 掛載加密配置檔案
      - name: encryption-config
        mountPath: /etc/kubernetes/encryption
        readOnly: true
      # 掛載 PKI 憑證
      - name: k8s-certs
        mountPath: /etc/kubernetes/pki
        readOnly: true
      # 掛載 etcd 憑證
      - name: etcd-certs
        mountPath: /etc/kubernetes/pki/etcd
        readOnly: true
  # 卷定義
  volumes:
    # 加密配置卷
    - name: encryption-config
      hostPath:
        path: /etc/kubernetes/encryption
        type: DirectoryOrCreate
    # PKI 憑證卷
    - name: k8s-certs
      hostPath:
        path: /etc/kubernetes/pki
        type: DirectoryOrCreate
    # etcd 憑證卷
    - name: etcd-certs
      hostPath:
        path: /etc/kubernetes/pki/etcd
        type: DirectoryOrCreate

啟用加密後,新建立的 Secret 會自動以加密形式儲存。然而,既有的 Secret 仍然以未加密形式存在於 etcd 中。為了確保所有資料都受到保護,我們需要執行重新加密操作。

# 重新加密所有既有的 Secret
# 這會強制 Kubernetes 重新寫入所有 Secret
# 使新的加密配置生效

# 方法一:使用 kubectl 重新建立所有 Secret
# 這種方法適合 Secret 數量較少的情況

# 列出所有命名空間
kubectl get namespaces -o jsonpath='{.items[*].metadata.name}'

# 對每個命名空間執行以下操作
for namespace in $(kubectl get namespaces -o jsonpath='{.items[*].metadata.name}'); do
  echo "Processing namespace: $namespace"
  
  # 列出該命名空間中的所有 Secret
  for secret in $(kubectl get secrets -n $namespace -o jsonpath='{.items[*].metadata.name}'); do
    echo "  Re-encrypting secret: $secret"
    
    # 讀取 Secret 並重新套用
    # 這會觸發重新寫入,套用新的加密配置
    kubectl get secret $secret -n $namespace -o yaml | \
      kubectl replace -f -
  done
done

# 方法二:使用 Kubernetes 內建的加密輪換功能
# 這種方法更有效率,適合大規模環境

# 對 Secret 資源執行儲存遷移
kubectl get secrets --all-namespaces -o json | \
  kubectl replace -f -

# 驗證加密是否生效
# 直接從 etcd 讀取資料,確認已被加密

# 取得一個 Secret 的 etcd 金鑰
SECRET_NAME="my-secret"
NAMESPACE="default"
ETCD_KEY="/registry/secrets/${NAMESPACE}/${SECRET_NAME}"

# 從 etcd 讀取資料
# 需要 etcd 的 CA 憑證與客戶端憑證
ETCDCTL_API=3 etcdctl \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key \
  get "${ETCD_KEY}"

# 如果加密成功,輸出應該是二進位資料
# 而非可讀的 YAML 格式
# 輸出範例:
# /registry/secrets/default/my-secret
# k8s:enc:aescbc:v1:key1:<binary encrypted data>

這個重新加密過程確保了所有既有的 Secret 都受到加密保護。在執行這個操作時需要注意,大規模的重新加密可能對 API Server 與 etcd 造成負載壓力,建議在低峰時段分批執行。

金鑰管理系統的整合與最佳實務

將加密金鑰直接儲存在加密配置檔案中存在明顯的安全風險。如果攻擊者能夠存取 API Server 的檔案系統,就能取得加密金鑰並解密所有 Secret 資料。更好的做法是整合外部的金鑰管理系統,將金鑰的儲存與管理委託給專門的安全基礎設施。

Kubernetes 支援透過 KMS 外掛程式與各種金鑰管理系統整合,包含 AWS KMS、Azure Key Vault、Google Cloud KMS、HashiCorp Vault 等。這些系統提供了硬體安全模組支援、金鑰輪換、存取稽核等企業級功能,大幅提升了金鑰管理的安全性。

以 HashiCorp Vault 為例,我們可以建立一個完整的金鑰管理方案。Vault 提供了動態金鑰生成、自動輪換、詳細的存取日誌等功能,是企業環境中常用的金鑰管理解決方案。

# Kubernetes KMS 外掛程式配置
# 整合 HashiCorp Vault 作為金鑰管理系統
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      # 使用 KMS 提供者
      # 加密金鑰由 Vault 管理
      - kms:
          # KMS 外掛程式名稱
          # 必須與 KMS 外掛程式的註冊名稱一致
          name: vault-kms-plugin
          # KMS 外掛程式的 Unix socket 路徑
          # API Server 透過此 socket 與外掛程式通訊
          endpoint: unix:///var/run/kmsplugin/socket.sock
          # 快取加密金鑰的時間
          # 減少對 KMS 系統的呼叫次數
          cachesize: 1000
          # 快取的有效期限
          timeout: 3s
      # 保留 identity 提供者用於遷移
      - identity: {}

配置 KMS 外掛程式後,我們需要部署實際的外掛程式程式。這個程式作為 DaemonSet 運行在每個控制平面節點上,負責與 Vault 溝通執行實際的加密解密操作。

# KMS 外掛程式 DaemonSet
# 在每個控制平面節點上運行 Vault KMS 外掛程式
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: vault-kms-plugin
  namespace: kube-system
  labels:
    app: vault-kms-plugin
spec:
  selector:
    matchLabels:
      app: vault-kms-plugin
  template:
    metadata:
      labels:
        app: vault-kms-plugin
    spec:
      # 僅在控制平面節點上運行
      nodeSelector:
        node-role.kubernetes.io/control-plane: ""
      # 容忍控制平面節點的污點
      tolerations:
        - key: node-role.kubernetes.io/control-plane
          operator: Exists
          effect: NoSchedule
      # 使用主機網路
      # 以便與本機的 API Server 通訊
      hostNetwork: true
      # 服務帳戶
      serviceAccountName: vault-kms-plugin
      containers:
        - name: vault-kms-plugin
          # KMS 外掛程式映像
          image: hashicorp/vault-k8s:latest
          imagePullPolicy: Always
          # 指令參數
          args:
            # Vault 伺服器位址
            - --vault-addr=https://vault.example.com:8200
            # Vault 認證方法
            - --vault-auth-method=kubernetes
            # Vault 金鑰路徑
            - --vault-key-path=secret/data/kubernetes/encryption
            # Unix socket 路徑
            - --socket-path=/var/run/kmsplugin/socket.sock
          # 環境變數
          env:
            # Vault 命名空間(Vault Enterprise 功能)
            - name: VAULT_NAMESPACE
              value: "kubernetes"
            # TLS 設定
            - name: VAULT_SKIP_VERIFY
              value: "false"
            - name: VAULT_CACERT
              value: /var/run/vault/ca.crt
          # 卷掛載
          volumeMounts:
            # 掛載 socket 目錄
            - name: socket-dir
              mountPath: /var/run/kmsplugin
            # 掛載 Vault CA 憑證
            - name: vault-ca
              mountPath: /var/run/vault
              readOnly: true
          # 資源限制
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 200m
              memory: 256Mi
          # 安全上下文
          securityContext:
            # 以非 root 使用者執行
            runAsNonRoot: true
            runAsUser: 1000
            # 限制能力
            capabilities:
              drop:
                - ALL
      # 卷定義
      volumes:
        # Socket 目錄
        # 在主機上建立目錄供 API Server 存取
        - name: socket-dir
          hostPath:
            path: /var/run/kmsplugin
            type: DirectoryOrCreate
        # Vault CA 憑證
        - name: vault-ca
          secret:
            secretName: vault-ca-cert

整合 KMS 系統後,加密金鑰的生命週期管理變得更加安全與便利。我們可以實作自動化的金鑰輪換策略,定期更換加密金鑰以降低金鑰外洩的風險。

# 金鑰輪換流程
# 定期執行以更新加密金鑰

# 步驟一:在 Vault 中生成新的加密金鑰
vault kv put secret/kubernetes/encryption/key-new \
  value="$(openssl rand -base64 32)"

# 步驟二:更新加密配置檔案
# 將新金鑰加入配置檔案的第一位置
# 保留舊金鑰以支援解密既有資料

cat > /etc/kubernetes/encryption/config.yaml <<EOF
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - kms:
          name: vault-kms-plugin
          endpoint: unix:///var/run/kmsplugin/socket.sock
          # 更新為使用新金鑰
          keyID: key-new
      - kms:
          name: vault-kms-plugin
          endpoint: unix:///var/run/kmsplugin/socket.sock
          # 保留舊金鑰用於解密
          keyID: key-old
      - identity: {}
EOF

# 步驟三:重新啟動 API Server
# 讓新配置生效
kubectl delete pod -n kube-system -l component=kube-apiserver

# 等待 API Server 重新啟動
kubectl wait --for=condition=Ready \
  pod -n kube-system -l component=kube-apiserver \
  --timeout=300s

# 步驟四:重新加密所有 Secret
# 使用新金鑰重新寫入資料
kubectl get secrets --all-namespaces -o json | \
  kubectl replace -f -

# 步驟五:驗證金鑰輪換成功
# 確認新 Secret 使用新金鑰加密

# 建立測試 Secret
kubectl create secret generic test-key-rotation \
  --from-literal=data=test

# 從 etcd 檢查加密資訊
ETCDCTL_API=3 etcdctl \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key \
  get /registry/secrets/default/test-key-rotation

# 輸出應該顯示使用新金鑰 ID
# k8s:enc:kms:v2:key-new:<encrypted data>

# 步驟六:清理舊金鑰
# 確認所有資料都重新加密後,可以移除舊金鑰
# 更新配置檔案移除舊金鑰提供者
# 再次重啟 API Server

這個完整的金鑰輪換流程展示了如何在不中斷服務的情況下更新加密金鑰。透過保留多個金鑰提供者的機制,Kubernetes 能夠在輪換過程中同時支援新舊金鑰的解密,確保既有資料的可用性。

RBAC 存取控制的精細化設計

儲存層的加密保護了資料在靜態狀態下的安全,但我們還需要確保只有授權的使用者與服務能夠存取 Secret。Kubernetes 的 RBAC 機制提供了細粒度的存取控制能力,讓我們能夠精確定義誰可以對哪些資源執行什麼操作。

在設計 RBAC 策略時,應該遵循最小權限原則。每個使用者或服務帳戶只應該被授予完成其工作所需的最小權限集合。對於 Secret 的存取,這意味著大多數使用者不應該有列出或讀取 Secret 的權限,只有在明確需要時才授予特定 Secret 的存取權限。

# Secret 存取控制範例
# 展示如何設計細粒度的 RBAC 策略

---
# 角色定義:應用程式部署者
# 只能建立與更新 Deployment,但不能直接存取 Secret
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-deployer
  namespace: production
rules:
  # Deployment 管理權限
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["create", "get", "list", "update", "patch"]
  # Pod 查看權限
  # 用於檢查部署狀態
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["get", "list"]
  # 不包含 Secret 存取權限
  # 避免部署者直接讀取敏感資訊

---
# 角色定義:Secret 管理員
# 負責管理特定命名空間的 Secret
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: secret-manager
  namespace: production
rules:
  # Secret 完整管理權限
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["create", "get", "list", "update", "patch", "delete"]
  # 但不包含其他資源的權限
  # 避免權限過度擴張

---
# 角色定義:應用程式服務帳戶
# 只能讀取特定的 Secret
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-secret-reader
  namespace: production
rules:
  # 限制只能讀取特定名稱的 Secret
  - apiGroups: [""]
    resources: ["secrets"]
    # 使用 resourceNames 限制存取範圍
    resourceNames:
      - "app-database-credentials"
      - "app-api-keys"
    verbs: ["get"]
  # 不允許列出所有 Secret
  # 防止發現其他 Secret 的存在

---
# 角色綁定:將角色授予使用者
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-deployer-binding
  namespace: production
subjects:
  # 綁定到特定使用者
  - kind: User
    name: "john@example.com"
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: app-deployer
  apiGroup: rbac.authorization.k8s.io

---
# 角色綁定:服務帳戶
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-secret-reader-binding
  namespace: production
subjects:
  # 綁定到服務帳戶
  - kind: ServiceAccount
    name: myapp-sa
    namespace: production
roleRef:
  kind: Role
  name: app-secret-reader
  apiGroup: rbac.authorization.k8s.io

這個 RBAC 設計展示了權限分離的原則。應用程式部署者能夠管理 Deployment,但無法直接讀取 Secret 內容。Secret 管理員專門負責管理 Secret,但不涉及應用程式的部署。應用程式的服務帳戶只能讀取其所需的特定 Secret,且不能列出命名空間中的所有 Secret,防止資訊洩漏。

在實際應用中,我們還需要定期稽核 RBAC 權限設定,確保沒有過度授權的情況。

# RBAC 權限稽核腳本
# 檢查可能的安全風險

#!/bin/bash

echo "=== Kubernetes RBAC 安全稽核 ==="
echo ""

# 檢查一:尋找能夠存取所有 Secret 的角色
echo "檢查一:尋找具有 Secret 萬用存取權限的角色"
echo "----------------------------------------"

# 查詢所有 Role 與 ClusterRole
# 篩選出包含 Secret 權限的角色
kubectl get roles,clusterroles --all-namespaces -o json | \
  jq -r '
    .items[] |
    select(
      .rules[]? |
      select(
        .resources[]? == "secrets" and
        .verbs[]? == "*" or
        (.verbs[]? == "get" and .verbs[]? == "list")
      )
    ) |
    "\(.metadata.namespace // "cluster-wide")/\(.metadata.name)"
  '

echo ""

# 檢查二:尋找綁定到 system:masters 群組的綁定
echo "檢查二:尋找過度授權的角色綁定"
echo "----------------------------------------"

# system:masters 具有完整的叢集權限
# 應該極少使用
kubectl get rolebindings,clusterrolebindings --all-namespaces -o json | \
  jq -r '
    .items[] |
    select(
      .subjects[]? |
      select(.name == "system:masters" or .name == "cluster-admin")
    ) |
    "\(.metadata.namespace // "cluster-wide")/\(.metadata.name)"
  '

echo ""

# 檢查三:列出所有服務帳戶的 Secret 存取權限
echo "檢查三:服務帳戶的 Secret 存取權限"
echo "----------------------------------------"

# 取得所有命名空間
for ns in $(kubectl get namespaces -o jsonpath='{.items[*].metadata.name}'); do
  echo "命名空間: $ns"
  
  # 取得該命名空間的所有服務帳戶
  for sa in $(kubectl get serviceaccounts -n $ns -o jsonpath='{.items[*].metadata.name}'); do
    # 使用 kubectl auth can-i 檢查權限
    if kubectl auth can-i get secrets --as=system:serviceaccount:$ns:$sa -n $ns &>/dev/null; then
      echo "  ✗ 服務帳戶 $sa 可以讀取 Secret"
    fi
  done
  echo ""
done

# 檢查四:尋找未使用命名空間隔離的 ClusterRole
echo "檢查四:ClusterRole 的 Secret 權限"
echo "----------------------------------------"

kubectl get clusterroles -o json | \
  jq -r '
    .items[] |
    select(
      .rules[]? |
      select(.resources[]? == "secrets")
    ) |
    "\(.metadata.name): \(.rules[] | select(.resources[]? == "secrets") | .verbs)"
  '

echo ""
echo "=== 稽核完成 ==="

這個稽核腳本能夠自動檢查常見的 RBAC 安全風險,包含過度授權、不當的權限綁定等。定期執行這個腳本,並根據結果調整權限設定,是維護叢集安全的重要實務。

透過本文深入探討的加密策略、金鑰管理與存取控制機制,我們建構了完整的 Kubernetes Secret 安全防護體系。從 etcd 的靜態加密確保儲存安全,透過 KMS 整合實現企業級的金鑰管理,再以 RBAC 精細化控制實現最小權限原則,這些措施共同構成了縱深防禦的安全架構。在實際應用中,安全性是一個持續改進的過程,需要定期審視配置、更新策略,並隨著威脅態勢的變化調整防護措施。唯有將安全性融入到日常的運維流程中,才能真正保護敏感資訊免受威脅。