使用 Secret 安全分發憑證
本頁面演示如何安全地將敏感資料(例如密碼和加密金鑰)注入 Pod 中。
準備工作
你需要擁有一個 Kubernetes 叢集,並且 kubectl 命令列工具已配置為與你的叢集通訊。 建議在至少有兩個節點(不作為控制平面主機)的叢集上執行本教程。 如果你還沒有叢集,可以使用 minikube 建立一個,或者使用這些 Kubernetes 遊樂場之一
將你的 Secret 資料轉換為 Base64 格式
假設你有兩段 Secret 資料:使用者名稱 my-app
和密碼 39528$vdg7Jb
。 首先,使用 Base64 編碼工具將你的使用者名稱和密碼轉換為 Base64 格式。 以下是使用常用 Base64 程式的示例
echo -n 'my-app' | base64
echo -n '39528$vdg7Jb' | base64
輸出顯示你的使用者名稱的 Base64 編碼為 bXktYXBw
,密碼的 Base64 編碼為 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,透過卷訪問 Secret 資料
以下是你可用於建立 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
進入你的 Pod 中執行的容器的 Shell
kubectl exec -i -t secret-test-pod -- /bin/bash
Secret 資料透過掛載在
/etc/secret-volume
下的卷暴露給容器。在你的 Shell 中,列出
/etc/secret-volume
目錄中的檔案# Run this in the shell inside the container ls /etc/secret-volume
輸出顯示兩個檔案,每個 Secret 資料對應一個
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 中。否則,卷將不會被建立。
設定 Secret 鍵的 POSIX 許可權
你可以為一個 Secret 鍵設定 POSIX 檔案訪問許可權位。如果你不指定任何許可權,預設使用 0644
。你還可以為整個 Secret 卷設定預設的 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 卷掛載建立的所有檔案都具有許可權 0400
。
注意
如果你使用 JSON 定義 Pod 或 Pod 模板,請注意 JSON 規範不支援數字的八進位制字面量,因為 JSON 將0400
視為十進位制值 400
。在 JSON 中,請使用十進位制值表示 defaultMode
。如果你正在編寫 YAML,則可以八進位制編寫 defaultMode
。使用 Secret 資料定義容器環境變數
你可以在容器中將 Secret 中的資料作為環境變數使用。
如果容器已經在一個環境變數中使用了 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 為 Pod 提供生產/測試憑據
此示例演示了一個 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 清單
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
你可以透過使用兩個 Service Account 進一步簡化基礎 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