使用 Secrets 安全地分發憑證

本頁面說明如何安全地將敏感資料 (例如密碼與加密金鑰) 注入到 Pod 中。

準備開始

您需要有一個 Kubernetes 叢集,而且必須設定 kubectl 命令列工具以與您的叢集通訊。建議在至少有兩個節點且未充當控制平面主機的叢集上執行本教學課程。如果您還沒有叢集,可以使用 minikube 建立一個叢集,或者您可以使用以下 Kubernetes 體驗環境之一

將您的密鑰資料轉換為 base-64 表示法

假設您想要有兩段密鑰資料:使用者名稱 my-app 和密碼 39528$vdg7Jb。首先,使用 base64 編碼工具將您的使用者名稱和密碼轉換為 base64 表示法。以下是使用常見的 base64 程式的範例

echo -n 'my-app' | base64
echo -n '39528$vdg7Jb' | base64

輸出顯示您的使用者名稱的 base-64 表示法為 bXktYXBw,而您的密碼的 base-64 表示法為 Mzk1MjgkdmRnN0pi

建立 Secret

以下是您可以使用的組態檔,以建立保存您的使用者名稱和密碼的 Secret

apiVersion: v1
kind: Secret
metadata:
  name: test-secret
data:
  username: bXktYXBw
  password: Mzk1MjgkdmRnN0pi
  1. 建立 Secret

    kubectl apply -f https://k8s.io/examples/pods/inject/secret.yaml
    
  2. 檢視關於 Secret 的資訊

    kubectl get secret test-secret
    

    輸出

    NAME          TYPE      DATA      AGE
    test-secret   Opaque    2         1m
    
  3. 檢視關於 Secret 的更多詳細資訊

    kubectl describe secret test-secret
    

    輸出

    Name:       test-secret
    Namespace:  default
    Labels:     <none>
    Annotations:    <none>
    
    Type:   Opaque
    
    Data
    ====
    password:   13 bytes
    username:   7 bytes
    

直接使用 kubectl 建立 Secret

如果您想要跳過 Base64 編碼步驟,您可以使用 kubectl create secret 命令建立相同的 Secret。例如

kubectl create secret generic test-secret --from-literal='username=my-app' --from-literal='password=39528$vdg7Jb'

這樣更方便。先前顯示的詳細方法會明確地逐步執行每個步驟,以示範正在發生的事情。

建立可透過卷存取密鑰資料的 Pod

以下是您可以使用的組態檔,以建立 Pod

apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
spec:
  containers:
    - name: test-container
      image: nginx
      volumeMounts:
        # name must match the volume name below
        - name: secret-volume
          mountPath: /etc/secret-volume
          readOnly: true
  # The secret data is exposed to Containers in the Pod through a Volume.
  volumes:
    - name: secret-volume
      secret:
        secretName: test-secret
  1. 建立 Pod

    kubectl apply -f https://k8s.io/examples/pods/inject/secret-pod.yaml
    
  2. 驗證您的 Pod 正在執行中

    kubectl get pod secret-test-pod
    

    輸出

    NAME              READY     STATUS    RESTARTS   AGE
    secret-test-pod   1/1       Running   0          42m
    
  3. 取得 Shell 進入在您的 Pod 中執行的容器

    kubectl exec -i -t secret-test-pod -- /bin/bash
    
  4. 密鑰資料透過掛載在 /etc/secret-volume 下的卷公開給容器。

    在您的 Shell 中,列出 /etc/secret-volume 目錄中的檔案

    # Run this in the shell inside the container
    ls /etc/secret-volume
    

    輸出顯示兩個檔案,每個密鑰資料一段

    password username
    
  5. 在您的 Shell 中,顯示 usernamepassword 檔案的內容

    # Run this in the shell inside the container
    echo "$( cat /etc/secret-volume/username )"
    echo "$( cat /etc/secret-volume/password )"
    

    輸出是您的使用者名稱和密碼

    my-app
    39528$vdg7Jb
    

修改您的映像檔或命令列,以便程式在 mountPath 目錄中尋找檔案。Secret data 對應中的每個鍵都會成為此目錄中的檔案名稱。

將 Secret 鍵投射到特定檔案路徑

您也可以控制卷中投射 Secret 鍵的路徑。使用 .spec.volumes[].secret.items 欄位來變更每個鍵的目標路徑

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username

當您部署此 Pod 時,會發生以下情況

  • mysecretusername 鍵值可在容器中透過路徑 /etc/foo/my-group/my-username 存取,而不是 /etc/foo/username
  • 該 Secret 物件的 password 鍵值不會被投射。

如果您使用 .spec.volumes[].secret.items 明確列出鍵值,請考慮以下事項

  • 只有在 items 中指定的鍵值才會被投射。
  • 若要取用 Secret 中的所有鍵值,必須將它們全部列在 items 欄位中。
  • 所有列出的鍵值都必須存在於對應的 Secret 中。否則,將不會建立 Volume。

設定 Secret 鍵值的 POSIX 權限

您可以為單個 Secret 鍵值設定 POSIX 檔案存取權限位元。如果您未指定任何權限,預設會使用 0644。您也可以為整個 Secret Volume 設定預設的 POSIX 檔案模式,並且可以根據需要覆寫個別鍵值。

例如,您可以像這樣指定預設模式

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      defaultMode: 0400

Secret 掛載在 /etc/foo;由 Secret Volume 掛載建立的所有檔案都具有 0400 權限。

使用 Secret 資料定義容器環境變數

您可以在容器中將 Secrets 中的資料作為環境變數使用。

如果容器已在環境變數中使用 Secret,則除非重新啟動容器,否則容器不會看到 Secret 更新。有一些第三方解決方案可以在 Secret 變更時觸發重新啟動。

使用來自單個 Secret 的資料定義容器環境變數

  • 將環境變數定義為 Secret 中的鍵值對

    kubectl create secret generic backend-user --from-literal=backend-username='backend-admin'
    
  • 將 Secret 中定義的 backend-username 值指派給 Pod 規格中的 SECRET_USERNAME 環境變數。

    apiVersion: v1
    kind: Pod
    metadata:
      name: env-single-secret
    spec:
      containers:
      - name: envars-test-container
        image: nginx
        env:
        - name: SECRET_USERNAME
          valueFrom:
            secretKeyRef:
              name: backend-user
              key: backend-username
    
  • 建立 Pod

    kubectl create -f https://k8s.io/examples/pods/inject/pod-single-secret-env-variable.yaml
    
  • 在您的 Shell 中,顯示 SECRET_USERNAME 容器環境變數的內容。

    kubectl exec -i -t env-single-secret -- /bin/sh -c 'echo $SECRET_USERNAME'
    

    輸出結果類似於

    backend-admin
    

使用來自多個 Secrets 的資料定義容器環境變數

  • 與先前的範例一樣,首先建立 Secrets。

    kubectl create secret generic backend-user --from-literal=backend-username='backend-admin'
    kubectl create secret generic db-user --from-literal=db-username='db-admin'
    
  • 在 Pod 規格中定義環境變數。

    apiVersion: v1
    kind: Pod
    metadata:
      name: envvars-multiple-secrets
    spec:
      containers:
      - name: envars-test-container
        image: nginx
        env:
        - name: BACKEND_USERNAME
          valueFrom:
            secretKeyRef:
              name: backend-user
              key: backend-username
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: db-user
              key: db-username
    
  • 建立 Pod

    kubectl create -f https://k8s.io/examples/pods/inject/pod-multiple-secret-env-variable.yaml
    
  • 在您的 Shell 中,顯示容器環境變數。

    kubectl exec -i -t envvars-multiple-secrets -- /bin/sh -c 'env | grep _USERNAME'
    

    輸出結果類似於

    DB_USERNAME=db-admin
    BACKEND_USERNAME=backend-admin
    

將 Secret 中的所有鍵值對配置為容器環境變數

  • 建立包含多個鍵值對的 Secret

    kubectl create secret generic test-secret --from-literal=username='my-app' --from-literal=password='39528$vdg7Jb'
    
  • 使用 envFrom 將 Secret 的所有資料定義為容器環境變數。Secret 中的鍵值會變成 Pod 中的環境變數名稱。

    apiVersion: v1
    kind: Pod
    metadata:
      name: envfrom-secret
    spec:
      containers:
      - name: envars-test-container
        image: nginx
        envFrom:
        - secretRef:
            name: test-secret
    
  • 建立 Pod

    kubectl create -f https://k8s.io/examples/pods/inject/pod-secret-envFrom.yaml
    
  • 在您的 Shell 中,顯示 usernamepassword 容器環境變數。

    kubectl exec -i -t envfrom-secret -- /bin/sh -c 'echo "username: $username\npassword: $password\n"'
    

    輸出結果類似於

    username: my-app
    password: 39528$vdg7Jb
    

範例:使用 Secrets 為 Pods 提供生產/測試憑證

此範例說明一個 Pod 取用包含生產憑證的 Secret,以及另一個 Pod 取用包含測試環境憑證的 Secret。

  1. 為生產環境憑證建立 Secret

    kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
    

    輸出結果類似於

    secret "prod-db-secret" created
    
  2. 為測試環境憑證建立 Secret。

    kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests
    

    輸出結果類似於

    secret "test-db-secret" created
    
  3. 建立 Pod Manifests

    cat <<EOF > pod.yaml
    apiVersion: v1
    kind: List
    items:
    - kind: Pod
      apiVersion: v1
      metadata:
        name: prod-db-client-pod
        labels:
          name: prod-db-client
      spec:
        volumes:
        - name: secret-volume
          secret:
            secretName: prod-db-secret
        containers:
        - name: db-client-container
          image: myClientImage
          volumeMounts:
          - name: secret-volume
            readOnly: true
            mountPath: "/etc/secret-volume"
    - kind: Pod
      apiVersion: v1
      metadata:
        name: test-db-client-pod
        labels:
          name: test-db-client
      spec:
        volumes:
        - name: secret-volume
          secret:
            secretName: test-db-secret
        containers:
        - name: db-client-container
          image: myClientImage
          volumeMounts:
          - name: secret-volume
            readOnly: true
            mountPath: "/etc/secret-volume"
    EOF
    
  4. 透過執行以下命令,將所有這些物件套用至 API 伺服器

    kubectl create -f pod.yaml
    

兩個容器的檔案系統中都將存在以下檔案,其中包含每個容器環境的值

/etc/secret-volume/username
/etc/secret-volume/password

您可以使用兩個服務帳戶進一步簡化基礎 Pod 規格

  1. prod-user 搭配 prod-db-secret
  2. test-user 搭配 test-db-secret

Pod 規格縮短為

apiVersion: v1
kind: Pod
metadata:
  name: prod-db-client-pod
  labels:
    name: prod-db-client
spec:
  serviceAccount: prod-db-client
  containers:
  - name: db-client-container
    image: myClientImage

參考資料

接下來

上次修改時間:2023 年 8 月 24 日下午 6:38 PST:使用 code_sample shortcode 而不是 code shortcode (e8b136c3b3)