Kubernetes使用Secrets Store CSI Driver掛載Vault Secret

Posted by Polin on Sat, Nov 16, 2024

Kubernetes使用Secrets Store CSI Driver掛載Vault Secret

如何透過 Vault CSI 驅動實現 Kubernetes 工作負載的動態密鑰管理。該筆記涵蓋 Vault CSI 的架構、安裝配置步驟,以及實際案例展示如何安全地將密鑰或機密資料掛載至 Pod,從而提升應用程式的安全性與合規性。

先決條件

在開始之前,請確保您的 macOS 環境已經具備以下軟體:

  1. Homebrew:macOS 的套件管理工具,用來安裝其他必要軟體。

  2. KinD: 一種基於 Docker 的本地 Kubernetes 集群工具,用於在本地開發與測試 Kubernetes 工作負載。 可以透過 Homebrew 安裝:

    brew install kind
    

    更多詳細說明可參考 在本機用 KinD 建立 Kubernetes

  3. Helm: Kubernetes 的應用程式包管理工具,用於簡化應用部署與管理。可以透過 Homebrew 安裝

    brew install helm
    

安裝 Vault Helm Chart

在 Kubernetes 中安裝 HashiCorp Vault,可以透過 Helm Chart 簡化部署流程。

以下是安裝步驟:

1. 新增 HashiCorp 的 Helm Chart 儲存庫:

首先,將 HashiCorp 的官方 Helm Chart 儲存庫加入本地 Helm 設定:

helm repo add hashicorp https://helm.releases.hashicorp.com

成功後會顯示以下類似訊息

"hashicorp" has been added to your repositories

2. 更新 Helm Chart 儲存庫

確保您擁有最新的 Chart 資料:

helm repo update hashicorp

成功後會顯示以下類似訊息

Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "hashicorp" chart repository
Update Complete. ⎈Happy Helming!⎈

3. 安裝 Vault

使用 Helm Chart 安裝 Vault 至指定的命名空間,例如 vault:

helm install vault hashicorp/vault \
  --set "server.dev.enabled=true" \
  --set "injector.enabled=false" \
  --set "csi.enabled=true" \
  --namespace vault --create-namespace

成功後會顯示以下類似訊息

NAME: vault
LAST DEPLOYED: Sat Nov 16 19:26:31 2024
NAMESPACE: vault
STATUS: deployed
REVISION: 1
NOTES:
Thank you for installing HashiCorp Vault!

Now that you have deployed Vault, you should look over the docs on using
Vault with Kubernetes available here:

https://developer.hashicorp.com/vault/docs


Your release is named vault. To learn more about the release, try:

  $ helm status vault
  $ helm get manifest vault

4. 驗證部署

確認 Vault Pod 是否成功啟動:

kubectl -n vault get pod

成功後會顯示以下類似訊息

NAME                                    READY   STATUS    RESTARTS   AGE
vault-0                                 1/1     Running   0          10m
vault-csi-provider-nkvpw                2/2     Running   0          10m

在 Vault 中設定 Secret

完成 Vault 部署後,可以開始設定 Secret。以下是設定 Secret 的基本流程:

1. 進入Vault Pod中

首先,啟動一個互動式的 shell session,進入 vault-0 Pod:

kubectl -n vault exec -it vault-0 -- /bin/sh

成功進入後,提示字元會變為:

/ $

2. 在 Vault 中新增 Secret

在路徑 secret/db-pass 建立一個包含密碼的 Secret:

vault kv put secret/db-pass password="db-secret-password"

執行後會顯示成功的輸出:

=== Secret Path ===
secret/data/db-pass

======= Metadata =======
Key                Value
---                -----
created_time       2024-11-16T14:54:08.929095439Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

3. 驗證 Secret 是否成功儲存

vault kv get secret/db-pass

執行後會顯示儲存的密鑰內容:

=== Secret Path ===
secret/data/db-pass

======= Metadata =======
Key                Value
---                -----
created_time       2024-11-16T14:54:08.929095439Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

====== Data ======
Key         Value
---         -----
password    db-secret-password

在 Vault 中配置 Kubernetes 驗證

Vault 支援 Kubernetes 驗證方法,允許 Kubernetes 的 Service Account 使用其 Token 與 Vault 進行認證。以下在 vault-0 Pod中配置步驟:

1. 啟用 Kubernetes 驗證方法

在 Vault 中啟用 Kubernetes 驗證方法:

vault auth enable kubernetes

成功後會顯示以下類似訊息

Success! Enabled kubernetes auth method at: kubernetes/

2. 配置 Kubernetes 驗證

設定 Kubernetes 驗證方法,使用 Vault Pod 自身的 Service Account Token:

vault write auth/kubernetes/config \
    kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"

成功後會顯示以下類似訊息

Success! Data written to: auth/kubernetes/config

$KUBERNETES_PORT_443_TCP_ADDR 是 Kubernetes 主機的內部網址。

如果是外部Vault就依照實際狀況改成實際Vault的網址

3. 建立存取策略

建立一個名為 internal-app 的策略,授權讀取 secret/data/db-pass:

vault policy write internal-app - <<EOF
path "secret/data/db-pass" {
  capabilities = ["read"]
}
EOF

成功後會顯示以下類似訊息

Success! Uploaded policy: internal-app

4. 建立 Kubernetes 驗證角色

建立一個名為 database 的 Kubernetes 驗證角色,綁定到指定的 Service Account 和策略:

vault write auth/kubernetes/role/database \
    bound_service_account_names=webapp-sa \
    bound_service_account_namespaces=default \
    policies=internal-app \
    ttl=20m

成功後會顯示以下類似訊息

Success! Data written to: auth/kubernetes/role/database

此角色將 Service Account webapp-sa(位於 default 命名空間)與 Vault 策略 internal-app 綁定,並設定 Token 的有效期為 20 分鐘。 完成以上步驟後,Vault 已經成功設定 Secret 並啟用 Kubernetes 驗證,供 Kubernetes 工作負載安全地存取所需的密鑰或敏感資訊。

最後,確認沒問題後離開 Pod:

exit

安裝 Secrets Store CSI Driver

Secrets Store CSI Driver (secrets-store.csi.k8s.io) 允許 Kubernetes 將外部密鑰管理系統中的多個 Secrets、金鑰和憑證以 Volume 的形式掛載到 Pod。

1. 新增 Helm 儲存庫

helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts

成功後會顯示以下類似訊息

"secrets-store-csi-driver" has been added to your repositories

2. 安裝 Secrets Store CSI Driver

使用 Helm 安裝最新版本的 Secrets Store CSI Driver:

helm install csi-secrets-store secrets-store-csi-driver/secrets-store-csi-driver -n kube-system

成功後會顯示以下類似訊息

NAME: csi-secrets-store
LAST DEPLOYED: Sat Nov 16 23:18:30 2024
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The Secrets Store CSI Driver is getting deployed to your cluster.

To verify that Secrets Store CSI Driver has started, run:

  kubectl --namespace=kube-system get pods -l "app=secrets-store-csi-driver"

Now you can follow these steps https://secrets-store-csi-driver.sigs.k8s.io/getting-started/usage.html
to create a SecretProviderClass resource, and a deployment using the SecretProviderClass.

3. 驗證安裝是否成功

kubectl -n kube-system get pods -l "app=secrets-store-csi-driver" 

成功後會顯示以下類似訊息

NAME                                               READY   STATUS    RESTARTS   AGE
csi-secrets-store-secrets-store-csi-driver-xf4jj   3/3     Running   0          42s

驗證 Vault CSI Provider 是否運行

kubectl -n vault get pod
NAME                       READY   STATUS    RESTARTS   AGE
vault-0                    1/1     Running   0          28m
vault-csi-provider-6zxmz   2/2     Running   0          28m

確認 Vault-csi-provider Pod 運行Container全部準備就緒

定義 SecretProviderClass 資源

1. 建立 SecretProviderClass

cat > spc-vault-database.yaml <<EOF
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: vault-database
spec:
  provider: vault
  parameters:
    vaultAddress: "http://vault.vault:8200"
    roleName: "database"
    objects: |
      - objectName: "db-password"
        secretPath: "secret/data/db-pass"
        secretKey: "password"
EOF

套用此配置:

kubectl apply -f spc-vault-database.yaml

成功後會顯示以下類似訊息

secretproviderclass.secrets-store.csi.x-k8s.io/vault-database created

2. 驗證配置是否成功

kubectl describe SecretProviderClass vault-database

成功後會顯示以下類似訊息

Name:         vault-database
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  secrets-store.csi.x-k8s.io/v1
Kind:         SecretProviderClass
Metadata:
  Creation Timestamp:  2024-11-16T15:25:00Z
  Generation:          1
  Resource Version:    3536
  UID:                 0225ef39-7761-4966-bdcd-d51f3f271bb1
Spec:
  Parameters:
    Objects:  - objectName: "db-password"
  secretPath: "secret/data/db-pass"
  secretKey: "password"

    Role Name:      database
    Vault Address:  http://vault.default:8200
  Provider:         vault
Events:             <none>

建立 Pod 並掛載 Secret

1. 創建 Service Account

使用名為 webapp-sa 的 Service Account:

kubectl create serviceaccount webapp-sa

成功後會顯示以下類似訊息

serviceaccount/webapp-sa created

2. 定義 WebApp Pod

創建一個 Pod,將 Secrets Volume 掛載至 /mnt/secrets-store:

cat > webapp-pod.yaml <<EOF
kind: Pod
apiVersion: v1
metadata:
  name: webapp
spec:
  serviceAccountName: webapp-sa
  containers:
  - image: polinhou/golang-http-headers:latest
    name: webapp
    volumeMounts:
    - name: secrets-store-inline
      mountPath: "/mnt/secrets-store"
      readOnly: true
  volumes:
    - name: secrets-store-inline
      csi:
        driver: secrets-store.csi.k8s.io
        readOnly: true
        volumeAttributes:
          secretProviderClass: "vault-database"
EOF

套用此配置:

kubectl apply -f webapp-pod.yaml

成功後會顯示以下類似訊息

pod/webapp created

3. 驗證 Pod 是否成功啟動

kubectl get pods

成功後會顯示以下類似訊息

pod/webapp created

成功後會顯示以下類似訊息

NAME     READY   STATUS    RESTARTS   AGE
webapp   1/1     Running   0          3s

驗證掛載的 Secret

在 Pod 中檢查掛載的 Secret 文件內容:

kubectl exec webapp -- cat /mnt/secrets-store/db-password

成功後會顯示以下類似訊息

db-secret-password

參考

Vault 官方文件