← 返回上一頁
Kubernetes 資安防護

為什麼 Kubernetes Secret 其實沒有你想的那麼安全?

本頁目錄
Kubernetes 搭配 GitOps 的 Secrets 管理流程圖

在 Kubernetes 的世界裡,Secret 是我們拿來存放敏感資訊的標準做法——不管是資料庫連線字串、API Token 還是各種帳號密碼,通通往 Secret 裡面丟就對了。不過如果你曾經用 kubectl get secret -o yaml 看過 Secret 的內容,應該馬上就會發現一件事:所謂的「秘密」根本就是 base64 編碼而已,隨便一個 base64 -d 就能還原明文,這跟沒加密有什麼兩樣?

本篇文章就來聊聊 K8s 原生 Secret 到底有哪些安全上的隱憂,以及社群和業界提供了哪些替代方案來補強這個缺口。

原生 Secret 的三大洩漏途徑

簡單歸納,Secret 的明文內容可能從以下三個地方被不該看到的人取得:

洩漏途徑 說明
etcd 儲存層 API Server 的所有資源都存在 etcd 裡,預設是明文(base64 decode 後的原文)直接寫入
API Server 只要有足夠的 RBAC 權限,任何節點都能透過 API 拿到 Secret 內容
Worker Node 檔案系統 Secret 以 volume 掛載進 Pod 後,在節點上可以直接讀取對應的 tmpfs 檔案

範例情境

假設我們建立一個 Secret,裡面放了使用者名稱 ******** 和密碼 ********

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: ********
  password: ********

接著建一個 Pod,把這個 Secret 當作 volume 掛進去:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: docker.io/containerstack/alpine-stress
    command:
      - top
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret

Pod 跑起來之後,進到容器裡面直接 cat /etc/foo/password 就能看到明文,這是預期行為。但問題在於,不只容器內部,其他途徑也能輕鬆拿到這些資料。

從 etcd 直接撈資料

etcd 是 K8s 的後端資料庫,如果有人能存取 etcd 的資料檔案,用 hexdump 就能直接看到 Secret 的明文內容:

# hexdump -C /var/lib/etcd/member/snap/db | grep -A 5 -B 5 hello

從 Kubernetes 1.7 開始已經支援 etcd 的靜態加密(Encryption at Rest),但這需要額外設定,不是預設啟用的。

從 API Server 查詢

這是最常見的情境,只要擁有對應的 RBAC 權限:

$ kubectl get secret mysecret -o yaml

拿到的 base64 字串,隨手一個 echo "xxxx" | base64 -d 就破解了。在大型團隊裡,很多不該看到這些資訊的人可能都有相關權限。

從 Node 上讀取檔案

在 Worker Node 上,Secret 會被掛載到 tmpfs:

# mount | grep foo
tmpfs on /var/lib/kubelet/pods/<pod-uid>/volumes/kubernetes.io~secret/foo type tmpfs (rw,relatime)

# cat /var/lib/kubelet/pods/<pod-uid>/volumes/kubernetes.io~secret/foo/password

只要有 Node 的 SSH 權限,就能直接讀到 Secret 明文。

GitOps 時代讓問題更嚴重

現在越來越多團隊採用 GitOps 的理念,所有 K8s manifest 都會放進 Git 版本控制。這代表 Secret 的 YAML 檔(即使是 base64 編碼)也會存在 Git repo 裡面,等於又多了一個洩漏的管道。任何能 clone 這個 repo 的人,都能輕鬆解碼看到原始內容。

Kubernetes 搭配 GitOps 的 Secrets 管理流程圖
GitOps 環境下的 Secrets 管理架構(圖片來源:Red Hat)

第三方解決方案一覽

要真正確保 Secret 的安全,光靠原生功能是不夠的。以下整理幾個主流的第三方方案:

方案 類型 特色
Sealed Secrets 加密後存入 Git 使用非對稱加密,只有叢集內的 Controller 能解密
HashiCorp Vault 外部 KMS 業界最成熟的方案,支援 Sidecar Injector 和 CSI Driver
External Secrets Operator 外部同步 從 AWS/GCP/Azure 等雲端 KMS 同步 Secret 到 K8s
mozilla/sops 加密檔案編輯器 支援多種 KMS 後端,可整合 GitOps 流程
Kamus 應用層加密 以 Service Account 為單位加密,透過 init container 解密
kubesec Secret 值加密 僅加密 Secret 中的資料欄位,支援 AWS/GCP KMS 和 GPG

Sealed Secrets — 最適合 GitOps 的方案

Bitnami 推出的 Sealed Secrets 是目前在 GitOps 場景下最受歡迎的選擇之一。它的運作原理是:

  1. 叢集內部署一個 SealedSecret Controller,持有一對 RSA 金鑰
  2. 使用 kubeseal CLI 工具搭配 Controller 的公鑰,將 Secret 加密為 SealedSecret 資源
  3. SealedSecret 可以安全地放進 Git——因為只有叢集內的 Controller 才有私鑰能解密
  4. Controller 監聽到 SealedSecret 資源後,自動解密並建立對應的 K8s Secret
Kubernetes Sealed Secrets 架構圖
Sealed Secrets 架構示意圖

值得注意的是,SealedSecret Controller 的金鑰本身也是用普通 Secret 儲存的,這算是一個已知的限制。

HashiCorp Vault — 企業級首選

Vault 對 K8s 提供兩種整合方式:

Agent Sidecar Injector:透過 Mutation Webhook 自動注入 init container 和 sidecar,負責從 Vault 取得並解密機密資料,寫入指定的 volume 路徑。Sidecar 還會定期更新 Secret,應用程式只需要讀取檔案即可。

spec:
  template:
    metadata:
      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/agent-inject-secret-helloworld: "secrets/helloworld"
        vault.hashicorp.com/role: "myapp"

Vault CSI Provider:透過 Secrets Store CSI Driver 介面,以 volume 的形式將 Vault 中的 Secret 掛載到 Pod 裡。

External Secrets Operator — 雲原生整合

如果你的團隊已經在使用雲端供應商的 KMS 服務(如 AWS Secrets Manager、GCP Secret Manager、Azure Key Vault),External Secrets Operator 會是最無痛的選擇。它透過 CRD 定義 ExternalSecret 資源,自動從外部 KMS 拉取機密資訊並建立 K8s Secret。

不過要注意,這種方式的 Secret 在 K8s 叢集內仍然是明文儲存的,主要解決的是「不把 Secret 放進 Git」和「統一管理外部金鑰」的問題。

Secrets Store CSI Driver — 社群標準方案

Kubernetes SIG 維護的 Secrets Store CSI Driver 提供了一個標準化的介面,可以搭配不同的 Provider(Vault、Azure、GCP 等)使用:

apiVersion: secrets-store.csi.x-k8s.io/v1alpha1
kind: SecretProviderClass
metadata:
  name: my-provider
spec:
  provider: vault
  parameters:
    # provider-specific parameters

Pod 只需要掛載對應的 CSI volume,就能透過檔案系統存取解密後的 Secret。

我的建議

以我自己的經驗來說,沒有單一方案能解決所有問題,實務上建議採取多層防禦:

  1. 基本功先做好:啟用 etcd Encryption at Rest、嚴格設計 RBAC 權限、限制 Node SSH 存取
  2. GitOps 場景:用 Sealed Secrets 或 sops 加密後再 commit
  3. 企業環境:導入 Vault 或雲端 KMS,搭配 External Secrets Operator 或 CSI Driver
  4. 最小權限原則:不是每個開發者都需要 get secret 的權限

總之,K8s 原生的 Secret 只是一個最基本的資源抽象,base64 編碼從來就不是加密。在正式環境中,一定要搭配額外的安全措施,別讓你的「秘密」變成公開的秘密。

分享這篇
X LinkedIn Facebook Hacker News Reddit

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料