使用用戶端程式庫存取儲存在 GKE 叢集外的機密資料


本教學課程說明如何將 Google Kubernetes Engine (GKE) 叢集使用的機密資料儲存在 Secret Manager 中。您將瞭解如何使用 Workload Identity Federation for GKE 和Google Cloud 用戶端程式庫,以更安全的方式從 Pod 存取資料。

將機密資料儲存在叢集儲存空間以外的位置,可降低發生攻擊時資料遭未授權存取的風險。使用 Workload Identity Federation for GKE 存取資料,可避免管理長期服務帳戶金鑰時的相關風險,並透過 Identity and Access Management (IAM) 控制機密存取權,而非叢集內 RBAC 規則。您可以使用任何外部密鑰儲存供應商,例如 Secret Manager 或 HashiCorp Vault。

本頁面適用於想將機密資料移出叢集內儲存空間的安全性專家。如要進一步瞭解 Google Cloud 內容中提及的常見角色和範例工作,請參閱常見的 GKE Enterprise 使用者角色和工作

本教學課程使用 GKE Autopilot 叢集。如要使用 GKE Standard 執行這些步驟,您必須手動啟用 GKE 適用的工作負載身分聯盟

您可以使用 Workload Identity Federation for GKE,從 GKE 工作負載存取任何 API,不必使用安全性較低的靜態服務帳戶金鑰檔案等方法。 Google Cloud 本教學課程以 Secret Manager 為例,但您可以使用相同步驟存取其他 Google CloudAPI。詳情請參閱「Workload Identity Federation for GKE」。

目標

  • 在 Google Cloud Secret Manager 中建立密鑰。
  • 建立 GKE Autopilot 叢集、Kubernetes 命名空間和 Kubernetes 服務帳戶。
  • 建立 IAM 允許政策,授予 Kubernetes 服務帳戶密鑰的存取權。
  • 使用測試應用程式驗證服務帳戶存取權。
  • 執行會使用 Secret Manager API 存取密鑰的範例應用程式。

費用

在本文件中,您會使用 Google Cloud的下列計費元件:

如要根據預測用量估算費用,請使用 Pricing Calculator

初次使用 Google Cloud 的使用者可能符合免費試用資格。

完成本文所述工作後,您可以刪除已建立的資源,避免繼續計費。詳情請參閱清除所用資源一節。

事前準備

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. Install the Google Cloud CLI.

  3. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

  4. To initialize the gcloud CLI, run the following command:

    gcloud init
  5. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  6. Make sure that billing is enabled for your Google Cloud project.

  7. Enable the Kubernetes Engine and Secret Manager APIs:

    gcloud services enable container.googleapis.com secretmanager.googleapis.com
  8. Install the Google Cloud CLI.

  9. If you're using an external identity provider (IdP), you must first sign in to the gcloud CLI with your federated identity.

  10. To initialize the gcloud CLI, run the following command:

    gcloud init
  11. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  12. Make sure that billing is enabled for your Google Cloud project.

  13. Enable the Kubernetes Engine and Secret Manager APIs:

    gcloud services enable container.googleapis.com secretmanager.googleapis.com
  14. Grant roles to your user account. Run the following command once for each of the following IAM roles: roles/secretmanager.admin, roles/container.clusterAdmin

    gcloud projects add-iam-policy-binding PROJECT_ID --member="user:USER_IDENTIFIER" --role=ROLE
    • Replace PROJECT_ID with your project ID.
    • Replace USER_IDENTIFIER with the identifier for your user account. For example, user:myemail@example.com.

    • Replace ROLE with each individual role.

準備環境

複製包含本教學課程範例檔案的 GitHub 存放區:

git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
cd ~/kubernetes-engine-samples/security/wi-secrets

在 Secret Manager 中建立密鑰

  1. 以下範例顯示用於建立密鑰的資料:

    key=my-api-key
  2. 建立密鑰來儲存範例資料:

    gcloud secrets create bq-readonly-key \
        --data-file=manifests/bq-readonly-key \
        --ttl=3600s
    

    這項指令會執行以下作業:

    • us-central1 Google Cloud 區域中,使用範例金鑰建立新的 Secret Manager 密鑰。
    • 設定密鑰在執行指令後一小時失效。

建立叢集和 Kubernetes 資源

建立 GKE 叢集、Kubernetes 命名空間和 Kubernetes 服務帳戶。您會建立兩個命名空間,一個用於密鑰的唯讀存取權,另一個用於密鑰的讀取/寫入存取權。您也會在每個命名空間中建立 Kubernetes 服務帳戶,以便搭配 Workload Identity Federation for GKE 使用。

  1. 建立 GKE Autopilot 叢集:

    gcloud container clusters create-auto secret-cluster \
        --region=us-central1
    

    叢集部署作業可能需要約五分鐘。Autopilot 叢集一律會啟用 Workload Identity Federation for GKE。如要改用 GKE Standard 叢集,請務必先手動啟用 GKE 適用的工作負載身分聯盟,再繼續操作。

  2. 建立 readonly-ns 命名空間和 admin-ns 命名空間:

    kubectl create namespace readonly-ns
    kubectl create namespace admin-ns
    
  3. 建立 readonly-sa Kubernetes 服務帳戶和 admin-sa Kubernetes 服務帳戶:

    kubectl create serviceaccount readonly-sa --namespace=readonly-ns
    kubectl create serviceaccount admin-sa --namespace=admin-ns
    

可建立身分與存取權管理允許政策

  1. 授予 readonly-sa 服務帳戶密鑰的唯讀權限:

    gcloud secrets add-iam-policy-binding bq-readonly-key \
        --member=principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/readonly-ns/sa/readonly-sa \
        --role='roles/secretmanager.secretAccessor' \
        --condition=None
    

    更改下列內容:

    • PROJECT_NUMBER:您的數字 Google Cloud專案編號。
    • PROJECT_ID:您的 Google Cloud 專案 ID。
  2. 授予 admin-sa 服務帳戶密鑰的讀寫權限:

    gcloud secrets add-iam-policy-binding bq-readonly-key \
        --member=principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/admin-ns/sa/admin-sa \
        --role='roles/secretmanager.secretAccessor' \
        --condition=None
    gcloud secrets add-iam-policy-binding bq-readonly-key \
        --member=principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/admin-ns/sa/admin-sa \
        --role='roles/secretmanager.secretVersionAdder' \
        --condition=None
    

驗證存取密鑰

在每個命名空間中部署測試 Pod,驗證唯讀和讀寫存取權。

  1. 查看唯讀 Pod 資訊清單:

    # Copyright 2022 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    apiVersion: v1
    kind: Pod
    metadata:
      name: readonly-test
      namespace: readonly-ns
    spec:
      containers:
      - image: google/cloud-sdk:slim
        name: workload-identity-test
        command: ["sleep","infinity"]
        resources:
          requests:
            cpu: "150m"
            memory: "150Mi"
      serviceAccountName: readonly-sa

    這個 Pod 會使用 readonly-ns 命名空間中的 readonly-sa 服務帳戶。

  2. 檢查讀寫 Pod 資訊清單:

    # Copyright 2022 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    apiVersion: v1
    kind: Pod
    metadata:
      name: admin-test
      namespace: admin-ns
    spec:
      containers:
      - image: google/cloud-sdk:slim
        name: workload-identity-test
        command: ["sleep","infinity"]
        resources:
          requests:
            cpu: "150m"
            memory: "150Mi"
      serviceAccountName: admin-sa

    這個 Pod 會使用 admin-ns 命名空間中的 admin-sa 服務帳戶。

  3. 部署測試 Pod:

    kubectl apply -f manifests/admin-pod.yaml
    kubectl apply -f manifests/readonly-pod.yaml
    

    Pod 可能需要幾分鐘才會開始執行。如要監控進度,請執行下列指令:

    watch kubectl get pods -n readonly-ns
    

    當 Pod 狀態變更為 RUNNING 時,請按下 Ctrl+C 返回指令列。

測試唯讀存取權

  1. readonly-test Pod 中開啟殼層:

    kubectl exec -it readonly-test --namespace=readonly-ns -- /bin/bash
    
  2. 嘗試讀取密鑰:

    gcloud secrets versions access 1 --secret=bq-readonly-key
    

    輸出內容為 key=my-api-key

  3. 嘗試將新資料寫入密鑰:

    printf "my-second-api-key" | gcloud secrets versions add bq-readonly-key --data-file=-
    

    輸出結果會與下列內容相似:

    ERROR: (gcloud.secrets.versions.add) PERMISSION_DENIED: Permission 'secretmanager.versions.add' denied for resource 'projects/PROJECT_ID/secrets/bq-readonly-key' (or it may not exist).
    

    使用唯讀服務帳戶的 Pod 只能讀取密鑰,無法寫入新資料。

  4. 退出 Pod:

    exit
    

測試讀取/寫入權限

  1. admin-test Pod 中開啟殼層:

    kubectl exec -it admin-test --namespace=admin-ns -- /bin/bash
    
  2. 嘗試讀取密鑰:

    gcloud secrets versions access 1 --secret=bq-readonly-key
    

    輸出內容為 key=my-api-key

  3. 嘗試將新資料寫入密鑰:

    printf "my-second-api-key" | gcloud secrets versions add bq-readonly-key --data-file=-
    

    輸出結果會與下列內容相似:

    Created version [2] of the secret [bq-readonly-key].
    
  4. 讀取新的密鑰版本:

    gcloud secrets versions access 2 --secret=bq-readonly-key
    

    輸出內容為 my-second-api-key

  5. 退出 Pod:

    exit
    

Pod 只能取得您授予 Pod 資訊清單所用 Kubernetes 服務帳戶的存取層級。使用 admin-ns 命名空間中 Kubernetes 帳戶的任何 Pod 都可以寫入新版本的密鑰,但使用 readonly-sa Kubernetes 服務帳戶的 readonly-ns 命名空間中任何 Pod 只能讀取密鑰。admin-sa

從程式碼存取密鑰

在本節中,執行以下操作:

  1. 部署範例應用程式,使用用戶端程式庫讀取 Secret Manager 中的密碼。

  2. 確認應用程式可以存取密鑰。

請盡可能使用 Secret Manager API,從應用程式程式碼存取 Secret Manager 密鑰。

  1. 查看範例應用程式原始碼:

    // Copyright 2022 Google LLC
    //
    // Licensed under the Apache License, Version 2.0 (the "License");
    // you may not use this file except in compliance with the License.
    // You may obtain a copy of the License at
    //
    //     http://www.apache.org/licenses/LICENSE-2.0
    //
    // Unless required by applicable law or agreed to in writing, software
    // distributed under the License is distributed on an "AS IS" BASIS,
    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    // See the License for the specific language governing permissions and
    // limitations under the License.
    
    package main
    
    import (
    	"context"
    	"fmt"
    	"log"
    	"os"
    
    	secretmanager "cloud.google.com/go/secretmanager/apiv1"
    	secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
    )
    
    func main() {
    
            // Get environment variables from Pod spec.
            projectID := os.Getenv("PROJECT_ID")
            secretId := os.Getenv("SECRET_ID")
            secretVersion := os.Getenv("SECRET_VERSION")
    
            // Create the Secret Manager client.
            ctx := context.Background()
            client, err := secretmanager.NewClient(ctx)
            if err != nil {
                    log.Fatalf("failed to setup client: %v", err)
            }
            defer client.Close()
    
            // Create the request to access the secret.
            accessSecretReq := &secretmanagerpb.AccessSecretVersionRequest{
                    Name: fmt.Sprintf("projects/%s/secrets/%s/versions/%s", projectID, secretId, secretVersion),
            }
    
            secret, err := client.AccessSecretVersion(ctx, accessSecretReq)
            if err != nil {
                    log.Fatalf("failed to access secret: %v", err)
            }
    
            // Print the secret payload.
            //
            // WARNING: Do not print the secret in a production environment - this
            // snippet is showing how to access the secret material.
            log.Printf("Welcome to the key store, here's your key:\nKey: %s", secret.Payload.Data)
    }
    

    這個應用程式會呼叫 Secret Manager API,嘗試讀取密碼。

  2. 查看範例應用程式 Pod 資訊清單:

    # Copyright 2022 Google LLC
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    #     http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.
    
    apiVersion: v1
    kind: Pod
    metadata:
      name: readonly-secret-test
      namespace: readonly-ns
    spec:
      containers:
      - image: us-docker.pkg.dev/google-samples/containers/gke/wi-secret-store:latest
        name: secret-app
        env:
          - name: PROJECT_ID
            value: "YOUR_PROJECT_ID"
          - name: SECRET_ID
            value: "bq-readonly-key"
          - name: SECRET_VERSION
            value: "latest"
        resources:
          requests:
            cpu: "125m"
            memory: "64Mi"
      serviceAccountName: readonly-sa

    這個資訊清單會執行下列操作:

    • readonly-ns 命名空間中建立 Pod,並使用 readonly-sa 服務帳戶。
    • 從 Google 映像檔登錄檔提取範例應用程式。這個應用程式會使用Google Cloud 用戶端程式庫呼叫 Secret Manager API。您可以在存放區的 /main.go 中查看應用程式程式碼。
    • 設定範例應用程式使用的環境變數。
  3. 在範例應用程式中取代環境變數:

    sed -i "s/YOUR_PROJECT_ID/PROJECT_ID/g" "manifests/secret-app.yaml"
    
  4. 部署範例應用程式:

    kubectl apply -f manifests/secret-app.yaml
    

    Pod 可能需要幾分鐘才會開始運作。如果 Pod 需要叢集中的新節點,您可能會在 GKE 佈建節點時,看到 CrashLoopBackOff 類型的事件。節點成功佈建後,當機問題就會停止。

  5. 驗證私密存取權:

    kubectl logs readonly-secret-test -n readonly-ns
    

    輸出內容為 my-second-api-key。如果輸出結果空白,表示 Pod 可能尚未執行。請稍候片刻,然後再試一次。

替代做法

如要將機密資料掛接至 Pod,請使用 GKE 適用的 Secret Manager 外掛程式。這個外掛程式會在 GKE 叢集中,為 Kubernetes Secret Store CSI 驅動程式部署及管理 Google Cloud Secret Manager 提供者。如需操作說明,請參閱「將 Secret Manager 外掛程式用於 GKE」。

將密鑰掛接為磁碟區有下列風險:

  1. 掛接的磁碟區容易受到目錄遍歷攻擊。
  2. 如果設定錯誤 (例如開啟偵錯端點),環境變數可能會遭到入侵。

我們建議盡可能透過 Secret Manager API 以程式輔助方式存取密鑰。如需操作說明,請使用本教學課程中的範例應用程式,或參閱 Secret Manager 用戶端程式庫

清除所用資源

如要避免系統向您的 Google Cloud 帳戶收取本教學課程中所用資源的相關費用,請刪除含有該項資源的專案,或者保留專案但刪除個別資源。

刪除個別資源

  1. 刪除叢集:

    gcloud container clusters delete secret-cluster \
        --region=us-central1
    
  2. 選用:刪除 Secret Manager 中的密鑰:

    gcloud secrets delete bq-readonly-key
    

    如果您未執行這個步驟,密鑰就會自動過期,因為您在建立密鑰時設定了 --ttl 旗標。

刪除專案

    Delete a Google Cloud project:

    gcloud projects delete PROJECT_ID

後續步驟