Kubernetes 的安全性是雲原生應用程式開發的關鍵環節,尤其在保護敏感資訊方面。本文介紹的集中式秘密管理技術,能有效提升安全性,避免直接在 Kubernetes 中儲存敏感資料。Sealed Secrets 提供了簡單的加密解密機制,External Secrets 則整合了多種外部秘密管理系統,而 SOPS 則適用於加密組態檔案。此外,Secret Store CSI Driver 提供更進階的整合方案,可將外部秘密管理系統掛載為 Kubernetes 卷,讓 Pod 更安全地存取敏感資料。各種方案各有優劣,開發者需根據實際需求選擇合適的工具。

集中式秘密管理(Centralized Secret Management)

在現代的雲端原生應用程式開發中,秘密管理(Secret Management)是一個至關重要的課題。集中式秘密管理利用專門的服務來儲存敏感的組態資料,這些服務可能由雲端供應商提供(如AWS Secrets Manager或Azure Key Vault),或是內部佈署的金鑰函式庫服務(如HashiCorp Vault)。

叢集外的加密技術(Out-of-Cluster Encryption)

叢集外的加密技術的核心概念很簡單:從叢集外部取得秘密和機密資料,並將其轉換為Kubernetes的Secret。許多專案都採用了這種技術,本章節將探討其中三個最具代表性的專案(截至2023年):Sealed Secrets、External Secrets和sops。

Sealed Secrets

Sealed Secrets是Bitnami在2017年推出的一個Kubernetes附加元件,用於協助加密Secret。其主要理念是將Secret的加密資料儲存在一個名為SealedSecret的自定義資源定義(CRD)中。在背景中,一個操作員會監控這些資源,並為每個SealedSecret建立一個Kubernetes Secret,包含解密後的內容。

Sealed Secrets的工作原理

  1. 加密與解密:Sealed Secrets使用AES-256-GCM對稱加密作為會話金鑰,並使用RSA-OAEP非對稱加密會話金鑰,這與TLS使用的設定相同。
  2. 私鑰管理:私鑰儲存在叢集內,由SealedSecret Operator自動建立。管理員需要備份此金鑰並在需要時進行輪替。
  3. 公鑰使用:用於加密的公鑰可以從叢集中擷取,也可以直接從檔案存取,甚至可以與SealedSecret一起安全地儲存在Git中。

SealedSecret的範圍

SealedSecrets支援三種範圍,可以在建立SealedSecret時選擇:

  • Strict(嚴格模式):凍結SealedSecret的名稱空間和名稱,只能在相同的名稱空間和名稱下建立。
  • Namespace-wide(名稱空間範圍):允許使用不同的名稱,但仍固定在相同的名稱空間。
  • Cluster-wide(叢集範圍):允許在不同的名稱空間中使用,並可變更名稱。

示例:使用kubeseal建立SealedSecret

# 建立SealedSecret的命令
kubeseal --scope cluster-wide -f mysecret.yaml
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  annotations:
    sealedsecrets.bitnami.com/cluster-wide: "true"
  name: DB-credentials
spec:
  encryptedData:
    password: AgCrKIIF2gA7tSR/gqw+FH6cEV..wPWWkHJbo=
    user: AgAmvgFQBBNPlt9Gmx..0DNHJpDIMUGgwaQroXT+o=

內容解密:

此範例展示如何使用kubeseal命令列工具將一個Secret轉換為SealedSecret,並儲存在Git中。sealedsecrets.bitnami.com/cluster-wide註解允許SealedSecret在任意名稱空間中使用,並可變更名稱。加密資料儲存在encryptedData欄位中。

External Secrets

External Secrets Operator是一個Kubernetes操作員,整合了越來越多的外部秘密管理系統(SMS)。與Sealed Secrets的主要區別在於,External Secrets不自行管理加密資料的儲存,而是依賴外部SMS來處理加密、解密和安全持久化。

External Secrets架構

  1. SecretStore:儲存外部SMS的型別和組態。
  2. ExternalSecret:參照SecretStore,操作員會建立一個對應的Kubernetes Secret,填入從外部SMS擷取的資料。

示例:連線到AWS Secret Manager的SecretStore

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: secret-store-aws
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        secretRef:
          accessKeyIDSecretRef:
            name: awssm-secret
            key: access-key

內容解密:

此範例定義了一個名為secret-store-aws的SecretStore,用於連線到AWS Secret Manager。spec.provider.aws欄位指定了AWS作為提供者,serviceregion欄位分別指定了服務和區域。auth.secretRef欄位參照了一個名為awssm-secret的Secret,用於身份驗證。

在 GitOps 中管理 Secrets 的最佳實踐

在現代的 DevOps 和 GitOps 環境中,安全管理是至關重要的。其中,Secrets(機密資訊,如資料函式庫密碼、API 金鑰等)的管理更是重中之重。本文將介紹在 GitOps 中管理 Secrets 的最佳實踐,包括使用 External Secrets Operator 和 Sops 工具。

使用 External Secrets Operator 管理 Secrets

External Secrets Operator 是一個 Kubernetes Operator,它允許你將外部 Secrets 管理系統(如 AWS Secrets Manager)中的 Secrets 同步到 Kubernetes Secrets 中。這樣,你就可以在 Kubernetes 中使用這些 Secrets,而無需直接在 Kubernetes 中儲存它們。

設定 External Secrets Operator

首先,你需要建立一個 SecretStore 物件,用於連線外部 Secrets 管理系統。例如,使用 AWS Secrets Manager:

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: secret-store-aws
spec:
  provider:
    aws:
      region: us-west-2
      auth:
        secretRef:
          accessKeyIdSecretRef:
            name: awssm-secret
            key: access-key-id
          secretAccessKeySecretRef:
            name: awssm-secret
            key: secret-access-key

同步 Secrets 到 Kubernetes

建立一個 ExternalSecret 物件,用於將外部 Secrets 同步到 Kubernetes Secrets:

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: secret-store-aws
    kind: SecretStore
  target:
    name: db-credentials-secrets
    creationPolicy: Owner
  data:
  - key: cluster/db-username
    name: username
  - key: cluster/db-password
    name: password

內容解密:

ExternalSecret 物件會將 AWS Secrets Manager 中的 cluster/db-usernamecluster/db-password 同步到 Kubernetes 中的 db-credentials-secrets Secret。refreshInterval 指定了同步的頻率。

使用 Sops 管理 Secrets

Sops(Secret OPerationS)是一個用於加密和解密 YAML 或 JSON 檔案的工具,可以用於在 GitOps 中安全地儲存 Secrets。

使用 Sops 加密 Secrets

首先,產生一個 age 金鑰:

$ age-keygen -o keys.txt
Public key: age1j49ugcg2rzyye07ksyvj5688m6hmv

然後,使用 Sops 加密一個 ConfigMap:

$ cat configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name_unencrypted: db-auth
data:
  # User and Password
  USER: "batman"
  PASSWORD: "r0b1n"

$ sops --encrypt \
  --age age1j49ugcg2rzyye07ksyvj5688m6hmv \
  configmap.yaml > configmap_encrypted.yaml

解密 Secrets

Sops 可以在 CI/CD 管道中用於解密 Secrets,然後將其應用到 Kubernetes 中。

SMS 與 KMS 的比較

SMS(Secret Management System)如 AWS Secrets Manager,提供了一個用於儲存和存取 Secrets 的 API。KMS(Key Management System)如 AWS KMS,則用於管理加密金鑰,可以用於加密和解密資料。

Kubernetes 中的加密與秘密管理

在 Kubernetes 環境中,保護敏感資訊是至關重要的。ConfigMap 和 Secret 是兩種常見的資源型別,用於儲存組態資料和敏感資訊。然而,這些資料在預設情況下並未加密,因此需要額外的措施來保護它們。

使用 SOPS 加密 ConfigMap

SOPS(Secrets OPerationS)是一個流行的工具,用於加密和解密檔案。它支援多種加密方式,包括使用 Age 金鑰進行加密。

首先,我們需要建立一個 ConfigMap,並使用 SOPS 對其進行加密。範例如下:

apiVersion: v1
kind: ConfigMap
metadata:
  name: db-auth
data:
  username: root
  password: secret

內容解密:

  • 這是一個基本的 ConfigMap 資源,用於儲存資料函式庫的驗證資訊。
  • metadata.name 指定了 ConfigMap 的名稱。
  • data 部分包含了實際的組態資料。

使用 SOPS 對 ConfigMap 進行加密的過程如下:

  1. 使用 Age 金鑰的公鑰部分對 ConfigMap 進行加密。
  2. 將加密後的結果儲存在 configmap_encrypted.yml 檔案中。

加密後的 ConfigMap 如下所示:

apiVersion: v1
kind: ConfigMap
metadata:
  name: db-auth
data:
  username: ENC[AES256_GCM,data:...]
  password: ENC[AES256_GCM,data:...]
sops:
  ...

內容解密:

  • 每個值都被替換為加密版本,格式為 ENC[...]
  • sops 部分包含了解密所需的元資料。
  • name 欄位保持不變。

解密與應用

要解密並應用加密的 ConfigMap,可以使用以下命令:

$ export SOPS_AGE_KEY_FILE=keys.txt
$ sops --decrypt configmap_encrypted.yaml | kubectl apply -f -

內容解密:

  • 指定私鑰檔案的位置。
  • 使用 SOPS 解密檔案,並將結果透過管道傳遞給 kubectl apply 命令。

集中式秘密管理

雖然 SOPS 提供了一種簡單的方式來加密和解密檔案,但一旦組態被應用到叢集中,具有提升存取許可權的使用者仍然可以讀取敏感資料。因此,考慮使用集中式秘密管理系統(SMS)是必要的。

Secrets Store CSI Driver

Secrets Store CSI Driver 是一種 Kubernetes API,用於將外部儲存系統掛載為卷。它允許存取各種集中式 SMS,並將其掛載為普通的 Kubernetes 卷。

設定步驟:

  1. 安裝 Secrets Store CSI Driver。
  2. 組態存取規則和策略。

範例組態

以下是一個使用 HashiCorp Vault 作為後端的範例組態:

apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: vault-database
spec:
  provider: vault
  parameters:
    vaultAddress: "http://vault.default:8200"
    roleName: "database"
    objects: |
      - objectName: "database-password"
        secretPath: "secret/data/database-creds"
        secretKey: "password"

內容解密:

  • 指定提供者型別為 Vault。
  • 設定 Vault 的連線 URL 和角色名稱。
  • 定義要掛載的秘密資訊。

Pod 組態範例

以下是一個 Pod 組態範例,它掛載了由 Secrets Store CSI Driver 管理的秘密卷:

kind: Pod
apiVersion: v1
metadata:
  name: shell-pod
spec:
  serviceAccountName: vault-access-sa
  volumes:
  - name: secrets-store01-inline
    csi:
      driver: secrets.csi.k8s.io
      volumeAttributes:
        secretProviderClass: "vault-database"
      nodePublishSecretRef:
        name: secrets-store-creds

內容解密:

  • 指定服務帳戶名稱。
  • 定義一個卷,使用 Secrets Store CSI Driver。
  • 參照之前建立的 SecretProviderClass

安全地管理機密資訊:Kubernetes 中的多種解決方案

在 Kubernetes 環境中,安全地管理機密資訊是一項至關重要的任務。隨著應用程式變得越來越複雜,對於機密資訊(如資料函式庫密碼、API 金鑰等)的安全存取和管理需求也日益增加。本文將探討多種在 Kubernetes 中安全地提供機密資訊給應用程式的方法。

使用 CSI Secret Store 驅動程式掛載機密資訊

CSI(Container Storage Interface)Secret Store 驅動程式提供了一種將外部機密管理系統(SMS,如 HashiCorp Vault、Azure Key Vault 等)中的機密資訊掛載到 Pod 中的方法。以下是一個使用 Vault 作為 SMS 的範例組態:

serviceAccountName: vault-access-sa
containers:
- image: k8spatterns/random
  volumeMounts:
  - name: secrets-store
    mountPath: "/secrets-store"
volumes:
- name: secrets-store
  csi:
    driver: secrets-store.csi.k8s.io
    readOnly: true
    volumeAttributes:
      secretProviderClass: "vault-database"

內容解密:

  1. serviceAccountName: vault-access-sa:指定用於向 Vault 進行身份驗證的服務帳戶名稱。
  2. volumeMounts:將名為 secrets-store 的卷掛載到容器內的 /secrets-store 目錄。
  3. volumes.csi.driver: secrets-store.csi.k8s.io:宣告使用 Secrets Store CSI 驅動程式來掛載機密資訊。
  4. volumeAttributes.secretProviderClass: "vault-database":指定用於連線到 Vault 服務的 SecretProviderClass 名稱。

這種方法的優點是避免了在 Kubernetes 中儲存機密資訊,但設定相對複雜,且需要更多的元件。

Pod 注入

另一種方法是使用 Init Container 或 Sidecar 從外部 SMS 中取得機密資訊,並將其複製或同步到一個分享的本地卷中。HashiCorp Vault Sidecar Agent Injector 是一個優秀的例子,它可以自動將 Sidecar 容器注入到 Pod 中,以實作與 Vault 的同步。

圖示說明:

此圖示展示了 Vault Injector 的工作原理。

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title 圖示說明:

rectangle "包含特定註解" as node1
rectangle "修改 Pod 定義" as node2
rectangle "同步機密資訊" as node3
rectangle "應用程式存取" as node4

node1 --> node2
node2 --> node3
node3 --> node4

@enduml

討論與比較

在選擇合適的機密資訊管理方案時,需要考慮以下因素:

  • 簡單性:對於簡單的客戶端加密需求,Sops 提供了一個簡單的解決方案。
  • 關注點分離:External Secrets Operator 提供了一種將取得憑證和使用的關注點分離的方法。
  • 臨時性:Secret Storage CSI Providers 可以確保機密資訊不會永久儲存在叢集中。
  • 易用性與安全性:Sidecar 注入(如 Vault Sidecar Agent Injector)提供了易用性,但可能會模糊開發者和系統管理員之間的界限。

更多資訊

  • Alex Soto Bueno 和 Andrew Block 的《Kubernetes Secrets Management》(Manning, 2022)
  • Kubernetes: Sealed Secrets
  • External Secrets Operator
  • Kubernetes Secrets Store CSI Driver
  • Retrieve HashiCorp Vault Secrets with Kubernetes CSI
  • HashiCorp Vault

支援的 Secret 管理系統:

  • Azure Key Vault
  • AWS Secrets Manager
  • AWS Systems Manager Parameter Store
  • GCP Secret Manager