使用 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
建立 Secret
kubectl apply -f https://k8s.io/examples/pods/inject/secret.yaml
檢視關於 Secret 的資訊
kubectl get secret test-secret
輸出
NAME TYPE DATA AGE test-secret Opaque 2 1m
檢視關於 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
建立 Pod
kubectl apply -f https://k8s.io/examples/pods/inject/secret-pod.yaml
驗證您的 Pod 正在執行中
kubectl get pod secret-test-pod
輸出
NAME READY STATUS RESTARTS AGE secret-test-pod 1/1 Running 0 42m
取得 Shell 進入在您的 Pod 中執行的容器
kubectl exec -i -t secret-test-pod -- /bin/bash
密鑰資料透過掛載在
/etc/secret-volume
下的卷公開給容器。在您的 Shell 中,列出
/etc/secret-volume
目錄中的檔案# Run this in the shell inside the container ls /etc/secret-volume
輸出顯示兩個檔案,每個密鑰資料一段
password username
在您的 Shell 中,顯示
username
和password
檔案的內容# 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 時,會發生以下情況
mysecret
的username
鍵值可在容器中透過路徑/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
權限。
注意
如果您使用 JSON 定義 Pod 或 Pod 範本,請注意 JSON 規格不支援數字的八進位字面值,因為 JSON 會將0400
視為十進位值 400
。在 JSON 中,請改用十進位值作為 defaultMode
。如果您正在編寫 YAML,則可以使用八進位寫入 defaultMode
。使用 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 中的所有鍵值對配置為容器環境變數
注意
此功能在 Kubernetes v1.6 及更高版本中可用。建立包含多個鍵值對的 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 中,顯示
username
和password
容器環境變數。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。
為生產環境憑證建立 Secret
kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
輸出結果類似於
secret "prod-db-secret" created
為測試環境憑證建立 Secret。
kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests
輸出結果類似於
secret "test-db-secret" created
注意
特殊字元,例如
$
、\
、*
、=
和!
將由您的 shell 解釋,並且需要跳脫字元。在大多數 Shell 中,跳脫密碼最簡單的方法是用單引號 (
'
) 將其括起來。例如,如果您的實際密碼是S!B\*d$zDsb=
,您應該如下執行命令kubectl create secret generic dev-db-secret --from-literal=username=devuser --from-literal=password='S!B\*d$zDsb='
您不需要跳脫來自檔案 (
--from-file
) 的密碼中的特殊字元。建立 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
注意
這兩個 Pod 的規格僅在一個欄位中有所不同;這有助於從通用 Pod 範本建立具有不同功能的 Pod。透過執行以下命令,將所有這些物件套用至 API 伺服器
kubectl create -f pod.yaml
兩個容器的檔案系統中都將存在以下檔案,其中包含每個容器環境的值
/etc/secret-volume/username
/etc/secret-volume/password
您可以使用兩個服務帳戶進一步簡化基礎 Pod 規格
prod-user
搭配prod-db-secret
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