W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
Secret 是一種包含少量敏感信息例如密碼、令牌或密鑰的對(duì)象。 這樣的信息可能會(huì)被放在 Pod 規(guī)約中或者鏡像中。 使用 Secret 意味著你不需要在應(yīng)用程序代碼中包含機(jī)密數(shù)據(jù)。
由于創(chuàng)建 Secret 可以獨(dú)立于使用它們的 Pod, 因此在創(chuàng)建、查看和編輯 Pod 的工作流程中暴露 Secret(及其數(shù)據(jù))的風(fēng)險(xiǎn)較小。 Kubernetes 和在集群中運(yùn)行的應(yīng)用程序也可以對(duì) Secret 采取額外的預(yù)防措施, 例如避免將機(jī)密數(shù)據(jù)寫入非易失性存儲(chǔ)。
Secret 類似于 ConfigMap 但專門用于保存機(jī)密數(shù)據(jù)。
Caution:
默認(rèn)情況下,Kubernetes Secret 未加密地存儲(chǔ)在 API 服務(wù)器的底層數(shù)據(jù)存儲(chǔ)(etcd)中。 任何擁有 API 訪問權(quán)限的人都可以檢索或修改 Secret,任何有權(quán)訪問 etcd 的人也可以。 此外,任何有權(quán)限在命名空間中創(chuàng)建 Pod 的人都可以使用該訪問權(quán)限讀取該命名空間中的任何 Secret; 這包括間接訪問,例如創(chuàng)建 Deployment 的能力。
為了安全地使用 Secret,請(qǐng)至少執(zhí)行以下步驟:
- 為 Secret 啟用靜態(tài)加密;
- 啟用或配置 RBAC 規(guī)則來限制讀取和寫入 Secret 的數(shù)據(jù)(包括通過間接方式)。需要注意的是,被準(zhǔn)許創(chuàng)建 Pod 的人也隱式地被授權(quán)獲取 Secret 內(nèi)容。
- 在適當(dāng)?shù)那闆r下,還可以使用 RBAC 等機(jī)制來限制允許哪些主體創(chuàng)建新 Secret 或替換現(xiàn)有 Secret。
Pod 可以用三種方式之一來使用 Secret:
Kubernetes 控制面也使用 Secret; 例如,引導(dǎo)令牌 Secret 是一種幫助自動(dòng)化節(jié)點(diǎn)注冊(cè)的機(jī)制。
除了使用 Secret 來保護(hù)機(jī)密數(shù)據(jù),你也可以選擇一些替代方案。
下面是一些選項(xiàng):
你還可以將如上選項(xiàng)的兩種或多種進(jìn)行組合,包括直接使用 Secret 對(duì)象本身也是一種選項(xiàng)。
例如:實(shí)現(xiàn)(或部署)一個(gè) operator, 從外部服務(wù)取回生命期很短的會(huì)話令牌,之后基于這些生命期很短的會(huì)話令牌來創(chuàng)建 Secret。 運(yùn)行在集群中的 Pod 可以使用這些會(huì)話令牌,而 Operator 則確保這些令牌是合法的。 這種責(zé)權(quán)分離意味著你可以運(yùn)行那些不了解會(huì)話令牌如何發(fā)放與刷新的確切機(jī)制的 Pod。
Secret 對(duì)象的名稱必須是合法的 DNS 子域名。
在為創(chuàng)建 Secret 編寫配置文件時(shí),你可以設(shè)置 ?data
?與/或 ?stringData
?字段。 ?data
?和 ?stringData
?字段都是可選的。?data
?字段中所有鍵值都必須是 base64 編碼的字符串。如果不希望執(zhí)行這種 base64 字符串的轉(zhuǎn)換操作,你可以選擇設(shè)置 ?stringData
?字段,其中可以使用任何字符串作為其取值。
?data
?和 ?stringData
?中的鍵名只能包含字母、數(shù)字、?-
?、?_
? 或 ?.
? 字符。 ?stringData
?字段中的所有鍵值對(duì)都會(huì)在內(nèi)部被合并到 ?data
?字段中。 如果某個(gè)主鍵同時(shí)出現(xiàn)在 ?data
?和 ?stringData
?字段中,?stringData
?所指定的鍵值具有高優(yōu)先級(jí)。
每個(gè) Secret 的尺寸最多為 1MiB。施加這一限制是為了避免用戶創(chuàng)建非常大的 Secret, 進(jìn)而導(dǎo)致 API 服務(wù)器和 kubelet 內(nèi)存耗盡。不過創(chuàng)建很多小的 Secret 也可能耗盡內(nèi)存。 你可以使用資源配額來約束每個(gè)名字空間中 Secret(或其他資源)的個(gè)數(shù)。
你可以使用 kubectl 來編輯一個(gè)已有的 Secret:
kubectl edit secrets mysecret
這一命令會(huì)啟動(dòng)你的默認(rèn)編輯器,允許你更新 ?data
?字段中存放的 base64 編碼的 Secret 值; 例如:
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file, it will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
kind: Secret
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: { ... }
creationTimestamp: 2020-01-22T18:41:56Z
name: mysecret
namespace: default
resourceVersion: "164619"
uid: cfee02d6-c137-11e5-8d73-42010af00002
type: Opaque
這一示例清單定義了一個(gè) Secret,其 ?data
?字段中包含兩個(gè)主鍵:?username
?和 ?password
?。 清單中的字段值是 Base64 字符串,不過,當(dāng)你在 Pod 中使用 Secret 時(shí),kubelet 為 Pod 及其中的容器提供的是解碼后的數(shù)據(jù)。
你可以在一個(gè) Secret 中打包多個(gè)主鍵和數(shù)值,也可以選擇使用多個(gè) Secret, 完全取決于哪種方式最方便。
Secret 可以以數(shù)據(jù)卷的形式掛載,也可以作為環(huán)境變量 暴露給 Pod 中的容器使用。Secret 也可用于系統(tǒng)中的其他部分,而不是一定要直接暴露給 Pod。 例如,Secret 也可以包含系統(tǒng)中其他部分在替你與外部系統(tǒng)交互時(shí)要使用的憑證數(shù)據(jù)。
Kubernetes 會(huì)檢查 Secret 的卷數(shù)據(jù)源,確保所指定的對(duì)象引用確實(shí)指向類型為 Secret 的對(duì)象。因此,如果 Pod 依賴于某 Secret,該 Secret 必須先于 Pod 被創(chuàng)建。
如果 Secret 內(nèi)容無法取回(可能因?yàn)?nbsp;Secret 尚不存在或者臨時(shí)性地出現(xiàn) API 服務(wù)器網(wǎng)絡(luò)連接問題),kubelet 會(huì)周期性地重試 Pod 運(yùn)行操作。kubelet 也會(huì)為該 Pod 報(bào)告 Event 事件,給出讀取 Secret 時(shí)遇到的問題細(xì)節(jié)。
當(dāng)你定義一個(gè)基于 Secret 的環(huán)境變量時(shí),你可以將其標(biāo)記為可選。 默認(rèn)情況下,所引用的 Secret 都是必需的。
只有所有非可選的 Secret 都可用時(shí),Pod 中的容器才能啟動(dòng)運(yùn)行。
如果 Pod 引用了 Secret 中的特定主鍵,而雖然 Secret 本身存在,對(duì)應(yīng)的主鍵不存在, Pod 啟動(dòng)也會(huì)失敗。
如果你希望在 Pod 中訪問 Secret 內(nèi)的數(shù)據(jù),一種方式是讓 Kubernetes 將 Secret 以 Pod 中一個(gè)或多個(gè)容器的文件系統(tǒng)中的文件的形式呈現(xiàn)出來。
要配置這種行為,你需要:
.spec.volumes[]
? 下添加一個(gè)卷。根據(jù)需要為卷設(shè)置其名稱, 并將 ?.spec.volumes[].secret.secretName
? 字段設(shè)置為 Secret 對(duì)象的名稱。.spec.containers[].volumeMounts[]
?。 并將 ?.spec.containers[].volumeMounts[].readyOnly
? 設(shè)置為 ?true
?, 將 ?.spec.containers[].volumeMounts[].mountPath
? 設(shè)置為希望 Secret 被放置的、目前尚未被使用的路徑名。data
? 映射中的每個(gè)主鍵都成為 ?mountPath
?下面的文件名。下面是一個(gè)通過卷來掛載名為 ?mysecret
?的 Secret 的 Pod 示例:
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
optional: false # 默認(rèn)設(shè)置,意味著 "mysecret" 必須已經(jīng)存在
你要訪問的每個(gè) Secret 都需要通過 ?.spec.volumes
? 來引用。
如果 Pod 中包含多個(gè)容器,則每個(gè)容器需要自己的 ?volumeMounts
?塊, 不過針對(duì)每個(gè) Secret 而言,只需要一份 ?.spec.volumes
? 設(shè)置。
Note:
Kubernetes v1.22 版本之前都會(huì)自動(dòng)創(chuàng)建用來訪問 Kubernetes API 的憑證。 這一老的機(jī)制是基于創(chuàng)建可被掛載到 Pod 中的令牌 Secret 來實(shí)現(xiàn)的。 在最近的版本中,包括 Kubernetes v1.24 中,API 憑據(jù)是直接通過 TokenRequest API 來獲得的,這一憑據(jù)會(huì)使用投射卷 掛載到 Pod 中。使用這種方式獲得的令牌有確定的生命期,并且在掛載它們的 Pod 被刪除時(shí)自動(dòng)作廢。
你仍然可以手動(dòng)創(chuàng)建 服務(wù)賬號(hào)令牌。例如,當(dāng)你需要一個(gè)永遠(yuǎn)都不過期的令牌時(shí)。 不過,仍然建議使用 TokenRequest 子資源來獲得訪問 API 服務(wù)器的令牌。
你也可以控制 Secret 鍵所投射到的卷中的路徑。 你可以使用 ?.spec.volumes[].secret.items
? 字段來更改每個(gè)主鍵的目標(biāo)路徑:
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
將發(fā)生的事情如下:
mysecret
?中的鍵 ?username
?會(huì)出現(xiàn)在容器中的路徑為 ?/etc/foo/my-group/my-username
?, 而不是? /etc/foo/username
?。password
?鍵不會(huì)被投射。如果使用了 ?.spec.volumes[].secret.items
?,則只有 ?items
?中指定了的主鍵會(huì)被投射。 如果要使用 Secret 中的所有主鍵,則需要將它們?nèi)棵杜e到 ?items
?字段中。
如果你顯式地列舉了主鍵,則所列舉的主鍵都必須在對(duì)應(yīng)的 Secret 中存在。 否則所在的卷不會(huì)被創(chuàng)建。
你可以為某個(gè) Secret 主鍵設(shè)置 POSIX 文件訪問權(quán)限位。 如果你不指定訪問權(quán)限,默認(rèn)會(huì)使用 ?0644
?。 你也可以為整個(gè) Secret 卷設(shè)置默認(rèn)的訪問模式,然后再根據(jù)需要在主鍵層面重載。
例如,你可以像下面這樣設(shè)置默認(rèn)的模式:
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 卷掛載所創(chuàng)建的所有文件的訪問模式都是 ?0400
?。
Note:
如果你是使用 JSON 來定義 Pod 或 Pod 模板,需要注意 JSON 規(guī)范不支持八進(jìn)制的記數(shù)方式。 你可以在 ?defaultMode
?中設(shè)置十進(jìn)制的值(例如,八進(jìn)制中的 0400 在十進(jìn)制中為 256)。 如果你使用 YAML 來編寫定義,你可以用八進(jìn)制值來設(shè)置 ?defaultMode
?。
在掛載了 Secret 卷的容器內(nèi),Secret 的主鍵都呈現(xiàn)為文件。 Secret 的取值都是 Base64 編碼的,保存在這些文件中。
下面是在上例中的容器內(nèi)執(zhí)行命令的結(jié)果:
ls /etc/foo/
輸出類似于:
username
password
cat /etc/foo/username
輸出類似于:
admin
cat /etc/foo/password
輸出類似于:
1f2d1e2e67df
容器中的程序要負(fù)責(zé)根據(jù)需要讀取 Secret 數(shù)據(jù)。
當(dāng)卷中包含來自 Secret 的數(shù)據(jù),而對(duì)應(yīng)的 Secret 被更新,Kubernetes 會(huì)跟蹤到這一操作并更新卷中的數(shù)據(jù)。更新的方式是保證最終一致性。
Note:
對(duì)于以 subPath 形式掛載 Secret 卷的容器而言, 它們無法收到自動(dòng)的 Secret 更新。
Kubelet 組件會(huì)維護(hù)一個(gè)緩存,在其中保存節(jié)點(diǎn)上 Pod 卷中使用的 Secret 的當(dāng)前主鍵和取值。 你可以配置 kubelet 如何檢測(cè)所緩存數(shù)值的變化。 kubelet 配置中的 ?configMapAndSecretChangeDetectionStrategy
?字段控制 kubelet 所采用的策略。 默認(rèn)的策略是 ?Watch
?。
對(duì) Secret 的更新操作既可以通過 API 的 watch 機(jī)制(默認(rèn))來傳播, 基于設(shè)置了生命期的緩存獲取,也可以通過 kubelet 的同步回路來從集群的 API 服務(wù)器上輪詢獲取。
因此,從 Secret 被更新到新的主鍵被投射到 Pod 中,中間存在一個(gè)延遲。 這一延遲的上限是 kubelet 的同步周期加上緩存的傳播延遲, 其中緩存的傳播延遲取決于所選擇的緩存類型。 對(duì)應(yīng)上一段中提到的幾種傳播機(jī)制,延遲時(shí)長為 watch 的傳播延遲、所配置的緩存 TTL 或者對(duì)于直接輪詢而言是零。
如果需要在 Pod 中以環(huán)境變量 的形式使用 Secret:
env[].valueFrom.secretKeyRef
? 中填寫 Secret 的名稱和主鍵名稱。下面是一個(gè)通過環(huán)境變量來使用 Secret 的示例 Pod:
apiVersion: v1
kind: Pod
metadata:
name: secret-env-pod
spec:
containers:
- name: mycontainer
image: redis
env:
- name: SECRET_USERNAME
valueFrom:
secretKeyRef:
name: mysecret
key: username
optional: false # 此值為默認(rèn)值;意味著 "mysecret"
# 必須存在且包含名為 "username" 的主鍵
- name: SECRET_PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password
optional: false # 此值為默認(rèn)值;意味著 "mysecret"
# 必須存在且包含名為 "password" 的主鍵
restartPolicy: Never
對(duì)于通過 ?envFrom
?字段來填充環(huán)境變量的 Secret 而言, 如果其中包含的主鍵不能被當(dāng)做合法的環(huán)境變量名,這些主鍵會(huì)被忽略掉。 Pod 仍然可以啟動(dòng)。
如果你定義的 Pod 中包含非法的變量名稱,則 Pod 可能啟動(dòng)失敗, 會(huì)形成 reason 為 ?InvalidVariableNames
?的事件,以及列舉被略過的非法主鍵的消息。 下面的例子中展示了一個(gè) Pod,引用的是名為 ?mysecret
?的 Secret, 其中包含兩個(gè)非法的主鍵:?1badkey
?和 ?2alsobad
?。
kubectl get events
輸出類似于:
LASTSEEN FIRSTSEEN COUNT NAME KIND SUBOBJECT TYPE REASON
0s 0s 1 dapi-test-pod Pod Warning InvalidEnvironmentVariableNames kubelet, 127.0.0.1 Keys [1badkey, 2alsobad] from the EnvFrom secret default/mysecret were skipped since they are considered invalid environment variable names.
在通過環(huán)境變量來使用 Secret 的容器中,Secret 主鍵展現(xiàn)為普通的環(huán)境變量。 這些變量的取值是 Secret 數(shù)據(jù)的 Base64 解碼值。
下面是在前文示例中的容器內(nèi)執(zhí)行命令的結(jié)果:
echo "$SECRET_USERNAME"
輸出類似于:
admin
echo "$SECRET_PASSWORD"
輸出類似于:
1f2d1e2e67df
Note:
如果容器已經(jīng)在通過環(huán)境變量來使用 Secret,Secret 更新在容器內(nèi)是看不到的, 除非容器被重啟。有一些第三方的解決方案,能夠在 Secret 發(fā)生變化時(shí)觸發(fā)容器重啟。
如果你嘗試從私有倉庫拉取容器鏡像,你需要一種方式讓每個(gè)節(jié)點(diǎn)上的 kubelet 能夠完成與鏡像庫的身份認(rèn)證。你可以配置 鏡像拉取 Secret 來實(shí)現(xiàn)這點(diǎn)。 Secret 是在 Pod 層面來配置的。
Pod 的 ?imagePullSecrets
?字段是一個(gè)對(duì) Pod 所在的名字空間中的 Secret 的引用列表。你可以使用 ?imagePullSecrets
?來將鏡像倉庫訪問憑據(jù)傳遞給 kubelet。 kubelet 使用這個(gè)信息來替你的 Pod 拉取私有鏡像。
?imagePullSecrets
?字段是一個(gè)列表,包含對(duì)同一名字空間中 Secret 的引用。 你可以使用 ?imagePullSecrets
?將包含 Docker(或其他)鏡像倉庫密碼的 Secret 傳遞給 kubelet。kubelet 使用此信息來替 Pod 拉取私有鏡像。
你可以通過閱讀容器鏡像 文檔了解如何設(shè)置 ?imagePullSecrets
?。
你可以手動(dòng)創(chuàng)建 ?imagePullSecret
?,并在一個(gè) ServiceAccount 中引用它。 對(duì)使用該 ServiceAccount 創(chuàng)建的所有 Pod,或者默認(rèn)使用該 ServiceAccount 創(chuàng)建的 Pod 而言,其 ?imagePullSecrets
?字段都會(huì)設(shè)置為該服務(wù)賬號(hào)。
你不可以在靜態(tài) Pod. 中使用 ConfigMap 或 Secret。
創(chuàng)建 Secret:
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
USER_NAME: YWRtaW4=
PASSWORD: MWYyZDFlMmU2N2Rm
創(chuàng)建 Secret:
kubectl apply -f mysecret.yaml
使用 ?envFrom
?來將 Secret 的所有數(shù)據(jù)定義為容器的環(huán)境變量。 來自 Secret 的主鍵成為 Pod 中的環(huán)境變量名稱:
apiVersion: v1
kind: Pod
metadata:
name: secret-test-pod
spec:
containers:
- name: test-container
image: k8s.gcr.io/busybox
command: [ "/bin/sh", "-c", "env" ]
envFrom:
- secretRef:
name: mysecret
restartPolicy: Never
創(chuàng)建包含一些 SSH 密鑰的 Secret:
kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub
輸出類似于:
secret "ssh-key-secret" created
你也可以創(chuàng)建一個(gè) ?kustomization.yaml
? 文件,在其 ?secretGenerator
?字段中包含 SSH 密鑰。
Caution:
在提供你自己的 SSH 密鑰之前要仔細(xì)思考:集群的其他用戶可能有權(quán)訪問該 Secret。
你也可以創(chuàng)建一個(gè) SSH 私鑰,代表一個(gè)你希望與你共享 Kubernetes 集群的其他用戶分享的服務(wù)標(biāo)識(shí)。 當(dāng)憑據(jù)信息被泄露時(shí),你可以收回該訪問權(quán)限。
現(xiàn)在你可以創(chuàng)建一個(gè) Pod,在其中訪問包含 SSH 密鑰的 Secret,并通過卷的方式來使用它:
apiVersion: v1
kind: Pod
metadata:
name: secret-test-pod
labels:
name: secret-test
spec:
volumes:
- name: secret-volume
secret:
secretName: ssh-key-secret
containers:
- name: ssh-test-container
image: mySshImage
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
容器命令執(zhí)行時(shí),秘鑰的數(shù)據(jù)可以在下面的位置訪問到:
/etc/secret-volume/ssh-publickey
/etc/secret-volume/ssh-privatekey
容器就可以隨便使用 Secret 數(shù)據(jù)來建立 SSH 連接。
這一示例所展示的一個(gè) Pod 會(huì)使用包含生產(chǎn)環(huán)境憑據(jù)的 Secret,另一個(gè) Pod 使用包含測(cè)試環(huán)境憑據(jù)的 Secret。
你可以創(chuàng)建一個(gè)帶有 ?secretGenerator
?字段的 ?kustomization.yaml
? 文件或者運(yùn)行 ?kubectl create secret
? 來創(chuàng)建 Secret。
kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
輸出類似于:
secret "prod-db-secret" created
你也可以創(chuàng)建一個(gè)包含測(cè)試環(huán)境憑據(jù)的 Secret:
kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests
輸出類似于:
secret "test-db-secret" created
Note:
特殊字符(例如 ?$
?、?\
?、?*
?、?=
? 和 ?!
?)會(huì)被你的 Shell解釋,因此需要轉(zhuǎn)義。
在大多數(shù) Shell 中,對(duì)密碼進(jìn)行轉(zhuǎn)義的最簡單方式是用單引號(hào)(?'
?)將其括起來。 例如,如果你的實(shí)際密碼是 ?S!B\*d$zDsb
?,則應(yīng)通過以下方式執(zhí)行命令:你無需對(duì)文件中的密碼(?kubectl create secret generic dev-db-secret --from-literal=username=devuser --from-literal=password='S!B\*d$zDsb='
--from-file
?)中的特殊字符進(jìn)行轉(zhuǎn)義。
現(xiàn)在生成 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 添加到同一 ?kustomization.yaml
? 文件中:
cat <<EOF >> kustomization.yaml
resources:
- pod.yaml
EOF
通過下面的命令在 API 服務(wù)器上應(yīng)用所有這些對(duì)象:
kubectl apply -k .
兩個(gè)文件都會(huì)在其文件系統(tǒng)中出現(xiàn)下面面的文件,文件中內(nèi)容是各個(gè)容器的環(huán)境值:
/etc/secret-volume/username
/etc/secret-volume/password
注意這兩個(gè) Pod 的規(guī)約中只有一個(gè)字段不同。 這便于基于相同的 Pod 模板生成具有不同能力的 Pod。
你可以通過使用兩個(gè)服務(wù)賬號(hào)來進(jìn)一步簡化這一基本的 Pod 規(guī)約:
prod-user
? 服務(wù)賬號(hào)使用 ?prod-db-secret
?test-user
? 服務(wù)賬號(hào)使用 ?test-db-secret
?Pod 規(guī)約簡化為:
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
通過定義以句點(diǎn)(?.
?)開頭的主鍵,你可以“隱藏”你的數(shù)據(jù)。 這些主鍵代表的是以句點(diǎn)開頭的文件或“隱藏”文件。 例如,當(dāng)下面的 Secret 被掛載到 ?secret-volume
? 卷中時(shí):
apiVersion: v1
kind: Secret
metadata:
name: dotfile-secret
data:
.secret-file: dmFsdWUtMg0KDQo=
---
apiVersion: v1
kind: Pod
metadata:
name: secret-dotfiles-pod
spec:
volumes:
- name: secret-volume
secret:
secretName: dotfile-secret
containers:
- name: dotfile-test-container
image: k8s.gcr.io/busybox
command:
- ls
- "-l"
- "/etc/secret-volume"
volumeMounts:
- name: secret-volume
readOnly: true
mountPath: "/etc/secret-volume"
卷中會(huì)包含一個(gè)名為 ?.secret-file
? 的文件,并且容器 ?dotfile-test-container
? 中此文件位于路徑 ?/etc/secret-volume/.secret-file
? 處。
Note:
以句點(diǎn)開頭的文件會(huì)在 ?ls -l
? 的輸出中被隱藏起來; 列舉目錄內(nèi)容時(shí)你必須使用 ?ls -la
? 才能看到它們。
考慮一個(gè)需要處理 HTTP 請(qǐng)求,執(zhí)行某些復(fù)雜的業(yè)務(wù)邏輯,之后使用 HMAC 來對(duì)某些消息進(jìn)行簽名的程序。因?yàn)檫@一程序的應(yīng)用邏輯很復(fù)雜, 其中可能包含未被注意到的遠(yuǎn)程服務(wù)器文件讀取漏洞, 這種漏洞可能會(huì)把私鑰暴露給攻擊者。
這一程序可以分隔成兩個(gè)容器中的兩個(gè)進(jìn)程:前端容器要處理用戶交互和業(yè)務(wù)邏輯, 但無法看到私鑰;簽名容器可以看到私鑰,并對(duì)來自前端的簡單簽名請(qǐng)求作出響應(yīng) (例如,通過本地主機(jī)網(wǎng)絡(luò))。
采用這種劃分的方法,攻擊者現(xiàn)在必須欺騙應(yīng)用服務(wù)器來做一些其他操作, 而這些操作可能要比讀取一個(gè)文件要復(fù)雜很多。
創(chuàng)建 Secret 時(shí),你可以使用 Secret 資源的 ?type
?字段,或者與其等價(jià)的 ?kubectl
?命令行參數(shù)(如果有的話)為其設(shè)置類型。 Secret 類型有助于對(duì) Secret 數(shù)據(jù)進(jìn)行編程處理。
Kubernetes 提供若干種內(nèi)置的類型,用于一些常見的使用場(chǎng)景。 針對(duì)這些類型,Kubernetes 所執(zhí)行的合法性檢查操作以及對(duì)其所實(shí)施的限制各不相同。
內(nèi)置類型 | 用法 |
---|---|
Opaque
|
用戶定義的任意數(shù)據(jù) |
kubernetes.io/service-account-token
|
服務(wù)賬號(hào)令牌 |
kubernetes.io/dockercfg
|
~/.dockercfg 文件的序列化形式 |
kubernetes.io/dockerconfigjson
|
~/.docker/config.json 文件的序列化形式 |
kubernetes.io/basic-auth
|
用于基本身份認(rèn)證的憑據(jù) |
kubernetes.io/ssh-auth
|
用于 SSH 身份認(rèn)證的憑據(jù) |
kubernetes.io/tls
|
用于 TLS 客戶端或者服務(wù)器端的數(shù)據(jù) |
bootstrap.kubernetes.io/token
|
啟動(dòng)引導(dǎo)令牌數(shù)據(jù) |
通過為 Secret 對(duì)象的 ?type
?字段設(shè)置一個(gè)非空的字符串值,你也可以定義并使用自己 Secret 類型。如果 ?type
?值為空字符串,則被視為 ?Opaque
?類型。
Kubernetes 并不對(duì)類型的名稱作任何限制。不過,如果你要使用內(nèi)置類型之一, 則你必須滿足為該類型所定義的所有要求。
如果你要定義一種公開使用的 Secret 類型,請(qǐng)遵守 Secret 類型的約定和結(jié)構(gòu), 在類型名簽名添加域名,并用 ?/
? 隔開。 例如:?cloud-hosting.example.net/cloud-api-credentials
?。
當(dāng) Secret 配置文件中未作顯式設(shè)定時(shí),默認(rèn)的 Secret 類型是 ?Opaque
?。 當(dāng)你使用 ?kubectl
?來創(chuàng)建一個(gè) Secret 時(shí),你會(huì)使用 ?generic
?子命令來標(biāo)明 要?jiǎng)?chuàng)建的是一個(gè) ?Opaque
?類型 Secret。 例如,下面的命令會(huì)創(chuàng)建一個(gè)空的 ?Opaque
?類型 Secret 對(duì)象:
kubectl create secret generic empty-secret
kubectl get secret empty-secret
輸出類似于
NAME TYPE DATA AGE
empty-secret Opaque 0 2m6s
?DATA
?列顯示 Secret 中保存的數(shù)據(jù)條目個(gè)數(shù)。 在這個(gè)例子種,?0
? 意味著我們剛剛創(chuàng)建了一個(gè)空的 Secret。
類型為 ?kubernetes.io/service-account-token
? 的 Secret 用來存放標(biāo)識(shí)某 服務(wù)賬號(hào)的令牌。 使用這種 Secret 類型時(shí),你需要確保對(duì)象的注解 ?kubernetes.io/service-account-name
? 被設(shè)置為某個(gè)已有的服務(wù)賬號(hào)名稱。某個(gè) Kubernetes 控制器會(huì)填寫 Secret 的其它字段,例如 ?kubernetes.io/service-account.uid
? 注解以及 ?data
?字段中的 ?token
?鍵值,使之包含實(shí)際的令牌內(nèi)容。
下面的配置實(shí)例聲明了一個(gè)服務(wù)賬號(hào)令牌 Secret:
apiVersion: v1
kind: Secret
metadata:
name: secret-sa-sample
annotations:
kubernetes.io/service-account.name: "sa-name"
type: kubernetes.io/service-account-token
data:
# 你可以像 Opaque Secret 一樣在這里添加額外的鍵/值偶對(duì)
extra: YmFyCg==
Kubernetes 在創(chuàng)建 Pod 時(shí)會(huì)自動(dòng)創(chuàng)建一個(gè)服務(wù)賬號(hào) Secret 并自動(dòng)修改你的 Pod 以使用該 Secret。該服務(wù)賬號(hào)令牌 Secret 中包含了訪問 Kubernetes API 所需要的憑據(jù)。
如果需要,可以禁止或者重載這種自動(dòng)創(chuàng)建并使用 API 憑據(jù)的操作。 不過,如果你僅僅是希望能夠安全地訪問 API 服務(wù)器,這是建議的工作方式。
你可以使用下面兩種 ?type
?值之一來創(chuàng)建 Secret,用以存放訪問 Docker 倉庫 來下載鏡像的憑據(jù)。
kubernetes.io/dockercfg
?kubernetes.io/dockerconfigjson
??kubernetes.io/dockercfg
? 是一種保留類型,用來存放 ?~/.dockercfg
? 文件的序列化形式。 該文件是配置 Docker 命令行的一種老舊形式。使用此 Secret 類型時(shí),你需要確保 Secret 的 ?data
?字段中包含名為 ?.dockercfg
? 的主鍵,其對(duì)應(yīng)鍵值是用 base64 編碼的某 ?~/.dockercfg
? 文件的內(nèi)容。
類型 ?kubernetes.io/dockerconfigjson
? 被設(shè)計(jì)用來保存 JSON 數(shù)據(jù)的序列化形式, 該 JSON 也遵從 ?~/.docker/config.json
? 文件的格式規(guī)則,而后者是 ?~/.dockercfg
? 的新版本格式。使用此 Secret 類型時(shí),Secret 對(duì)象的 ?data
?字段必須包含 ?.dockerconfigjson
? 鍵,其鍵值為 base64 編碼的字符串包含 ?~/.docker/config.json
? 文件的內(nèi)容。
下面是一個(gè) ?kubernetes.io/dockercfg
? 類型 Secret 的示例:
apiVersion: v1
kind: Secret
metadata:
name: secret-dockercfg
type: kubernetes.io/dockercfg
data:
.dockercfg: |
"<base64 encoded ~/.dockercfg file>"
Note:
如果你不希望執(zhí)行 base64 編碼轉(zhuǎn)換,可以使用 ?stringData
? 字段代替。
當(dāng)你使用清單文件來創(chuàng)建這兩類 Secret 時(shí),API 服務(wù)器會(huì)檢查 ?data
?字段中是否 存在所期望的主鍵,并且驗(yàn)證其中所提供的鍵值是否是合法的 JSON 數(shù)據(jù)。 不過,API 服務(wù)器不會(huì)檢查 JSON 數(shù)據(jù)本身是否是一個(gè)合法的 Docker 配置文件內(nèi)容。
kubectl create secret docker-registry secret-tiger-docker \
--docker-email=tiger@acme.example \
--docker-username=tiger \
--docker-password=pass113 \
--docker-server=my-registry.example:5000
上面的命令創(chuàng)建一個(gè)類型為 ?kubernetes.io/dockerconfigjson
? 的 Secret。 如果你對(duì) ?.data.dockerconfigjson
? 內(nèi)容進(jìn)行轉(zhuǎn)儲(chǔ)并執(zhí)行 base64 解碼:
{
"auths": {
"my-registry.example:5000": {
"username": "tiger",
"password": "pass113",
"email": "tiger@acme.com",
"auth": "dGlnZXI6cGFzczExMw=="
}
}
}
Note:
?auths
?值是 base64 編碼的,其內(nèi)容被屏蔽但未被加密。 任何能夠讀取該 Secret 的人都可以了解鏡像庫的訪問令牌。
?kubernetes.io/basic-auth
? 類型用來存放用于基本身份認(rèn)證所需的憑據(jù)信息。 使用這種 Secret 類型時(shí),Secret 的 ?data
?字段必須包含以下兩個(gè)鍵:
username
?: 用于身份認(rèn)證的用戶名;password
?: 用于身份認(rèn)證的密碼或令牌。以上兩個(gè)鍵的鍵值都是 base64 編碼的字符串。 當(dāng)然你也可以在創(chuàng)建 Secret 時(shí)使用 ?stringData
?字段來提供明文形式的內(nèi)容。 下面的 YAML 是基本身份認(rèn)證 Secret 的一個(gè)示例清單:
apiVersion: v1
kind: Secret
metadata:
name: secret-basic-auth
type: kubernetes.io/basic-auth
stringData:
username: admin # kubernetes.io/basic-auth 類型的必需字段
password: t0p-Secret # kubernetes.io/basic-auth 類型的必需字段
提供基本身份認(rèn)證類型的 Secret 僅僅是出于方便性考慮。 你也可以使用 ?Opaque
?類型來保存用于基本身份認(rèn)證的憑據(jù)。 不過,使用預(yù)定義的、公開的 Secret 類型(?kubernetes.io/basic-auth
?) 有助于幫助其他用戶理解 Secret 的目的,并且對(duì)其中存在的主鍵形成一種約定。 API 服務(wù)器會(huì)檢查 Secret 配置中是否提供了所需要的主鍵。
Kubernetes 所提供的內(nèi)置類型 ?kubernetes.io/ssh-auth
? 用來存放 SSH 身份認(rèn)證中 所需要的憑據(jù)。使用這種 Secret 類型時(shí),你就必須在其 ?data
?(或 ?stringData
?) 字段中提供一個(gè) ?ssh-privatekey
? 鍵值對(duì),作為要使用的 SSH 憑據(jù)。
下面的清單是一個(gè) SSH 公鑰/私鑰身份認(rèn)證的 Secret 示例:
apiVersion: v1
kind: Secret
metadata:
name: secret-ssh-auth
type: kubernetes.io/ssh-auth
data:
# 此例中的實(shí)際數(shù)據(jù)被截?cái)? ssh-privatekey: |
MIIEpQIBAAKCAQEAulqb/Y ...
提供 SSH 身份認(rèn)證類型的 Secret 僅僅是出于用戶方便性考慮。 你也可以使用 ?Opaque
?類型來保存用于 SSH 身份認(rèn)證的憑據(jù)。 不過,使用預(yù)定義的、公開的 Secret 類型(?kubernetes.io/ssh-auth
?) 有助于其他人理解你的 Secret 的用途,也可以就其中包含的主鍵名形成約定。 API 服務(wù)器確實(shí)會(huì)檢查 Secret 配置中是否提供了所需要的主鍵。
Caution:
SSH 私鑰自身無法建立 SSH 客戶端與服務(wù)器端之間的可信連接。 需要其它方式來建立這種信任關(guān)系,以緩解“中間人(Man In The Middle)” 攻擊,例如向 ConfigMap 中添加一個(gè) ?known_hosts
?文件。
Kubernetes 提供一種內(nèi)置的 ?kubernetes.io/tls Secret
? 類型,用來存放 TLS 場(chǎng)合通常要使用的證書及其相關(guān)密鑰。 TLS Secret 的一種典型用法是為 Ingress 資源配置傳輸過程中的數(shù)據(jù)加密,不過也可以用于其他資源或者直接在負(fù)載中使用。 當(dāng)使用此類型的 Secret 時(shí),Secret 配置中的 ?data
?(或 ?stringData
?)字段必須包含 ?tls.key
? 和 ?tls.crt
? 主鍵,盡管 API 服務(wù)器實(shí)際上并不會(huì)對(duì)每個(gè)鍵的取值作進(jìn)一步的合法性檢查。
下面的 YAML 包含一個(gè) TLS Secret 的配置示例:
apiVersion: v1
kind: Secret
metadata:
name: secret-tls
type: kubernetes.io/tls
data:
# 此例中的數(shù)據(jù)被截?cái)? tls.crt: |
MIIC2DCCAcCgAwIBAgIBATANBgkqh ...
tls.key: |
MIIEpgIBAAKCAQEA7yn3bRHQ5FHMQ ...
提供 TLS 類型的 Secret 僅僅是出于用戶方便性考慮。 你也可以使用 ?Opaque
?類型來保存用于 TLS 服務(wù)器與/或客戶端的憑據(jù)。 不過,使用內(nèi)置的 Secret 類型的有助于對(duì)憑據(jù)格式進(jìn)行歸一化處理,并且 API 服務(wù)器確實(shí)會(huì)檢查 Secret 配置中是否提供了所需要的主鍵。
當(dāng)使用 ?kubectl
?來創(chuàng)建 TLS Secret 時(shí),你可以像下面的例子一樣使用 ?tls
?子命令:
kubectl create secret tls my-tls-secret \
--cert=path/to/cert/file \
--key=path/to/key/file
這里的公鑰/私鑰對(duì)都必須事先已存在。用于 ?--cert
? 的公鑰證書必須是 RFC 7468 中 5.1 節(jié) 中所規(guī)定的 DER 格式,且與 ?--key
? 所給定的私鑰匹配。 私鑰必須是 DER 格式的 PKCS #8 (參見
RFC 7468 第 11節(jié))。
Note:
類型為 ?kubernetes.io/tls
? 的 Secret 中包含密鑰和證書的 DER 數(shù)據(jù),以 Base64 格式編碼。 如果你熟悉私鑰和證書的 PEM 格式,base64 與該格式相同,只是你需要略過 PEM 數(shù)據(jù)中所包含的第一行和最后一行。
例如,對(duì)于證書而言,你 不要 包含 ?--------BEGIN CERTIFICATE-----
? 和 ?-------END CERTIFICATE----
? 這兩行。
通過將 Secret 的 ?type
?設(shè)置為 ?bootstrap.kubernetes.io/token
? 可以創(chuàng)建 啟動(dòng)引導(dǎo)令牌類型的 Secret。這種類型的 Secret 被設(shè)計(jì)用來支持節(jié)點(diǎn)的啟動(dòng)引導(dǎo)過程。 其中包含用來為周知的 ConfigMap 簽名的令牌。
啟動(dòng)引導(dǎo)令牌 Secret 通常創(chuàng)建于 ?kube-system
? 名字空間內(nèi),并以 ?bootstrap-token-<令牌 ID>
?的形式命名;其中 ?<令牌 ID>
? 是一個(gè)由 6 個(gè)字符組成 的字符串,用作令牌的標(biāo)識(shí)。
以 Kubernetes 清單文件的形式,某啟動(dòng)引導(dǎo)令牌 Secret 可能看起來像下面這樣:
apiVersion: v1
kind: Secret
metadata:
name: bootstrap-token-5emitj
namespace: kube-system
type: bootstrap.kubernetes.io/token
data:
auth-extra-groups: c3lzdGVtOmJvb3RzdHJhcHBlcnM6a3ViZWFkbTpkZWZhdWx0LW5vZGUtdG9rZW4=
expiration: MjAyMC0wOS0xM1QwNDozOToxMFo=
token-id: NWVtaXRq
token-secret: a3E0Z2lodnN6emduMXAwcg==
usage-bootstrap-authentication: dHJ1ZQ==
usage-bootstrap-signing: dHJ1ZQ==
啟動(dòng)引導(dǎo)令牌類型的 Secret 會(huì)在 ?data
?字段中包含如下主鍵:
token-id
?:由 6 個(gè)隨機(jī)字符組成的字符串,作為令牌的標(biāo)識(shí)符。必需。token-secret
?:由 16 個(gè)隨機(jī)字符組成的字符串,包含實(shí)際的令牌機(jī)密。必需。description
?:供用戶閱讀的字符串,描述令牌的用途。可選。expiration
?:一個(gè)使用 RFC3339 來編碼的 UTC 絕對(duì)時(shí)間,給出令牌要過期的時(shí)間??蛇x。usage-bootstrap-<usage>
?:布爾類型的標(biāo)志,用來標(biāo)明啟動(dòng)引導(dǎo)令牌的其他用途。auth-extra-groups
?:用逗號(hào)分隔的組名列表,身份認(rèn)證時(shí)除被認(rèn)證為 system:bootstrappers 組之外,還會(huì)被添加到所列的用戶組中。上面的 YAML 文件可能看起來令人費(fèi)解,因?yàn)槠渲械臄?shù)值均為 base64 編碼的字符串。 實(shí)際上,你完全可以使用下面的 YAML 來創(chuàng)建一個(gè)一模一樣的 Secret:
apiVersion: v1
kind: Secret
metadata:
# 注意 Secret 的命名方式
name: bootstrap-token-5emitj
# 啟動(dòng)引導(dǎo)令牌 Secret 通常位于 kube-system 名字空間
namespace: kube-system
type: bootstrap.kubernetes.io/token
stringData:
auth-extra-groups: "system:bootstrappers:kubeadm:default-node-token"
expiration: "2020-09-13T04:39:10Z"
# 此令牌 ID 被用于生成 Secret 名稱
token-id: "5emitj"
token-secret: "kq4gihvszzgn1p0r"
# 此令牌還可用于 authentication (身份認(rèn)證)
usage-bootstrap-authentication: "true"
# 且可用于 signing (證書簽名)
usage-bootstrap-signing: "true"
FEATURE STATE: Kubernetes v1.21 [stable]
Kubernetes 允許你將特定的 Secret(和 ConfigMap)標(biāo)記為 不可更改(Immutable)。 禁止更改現(xiàn)有 Secret 的數(shù)據(jù)有下列好處:
你可以通過將 Secret 的 ?immutable
?字段設(shè)置為 ?true
?創(chuàng)建不可更改的 Secret。 例如:
apiVersion: v1
kind: Secret
metadata:
...
data:
...
immutable: true
你也可以更改現(xiàn)有的 Secret,令其不可更改。
Note:
一旦一個(gè) Secret 或 ConfigMap 被標(biāo)記為不可更改,撤銷此操作或者更改 ?data
?字段的內(nèi)容都是 不 可能的。 只能刪除并重新創(chuàng)建這個(gè) Secret。現(xiàn)有的 Pod 將維持對(duì)已刪除 Secret 的掛載點(diǎn) -- 建議重新創(chuàng)建這些 Pod。
盡管 ConfigMap 和 Secret 的工作方式類似,但 Kubernetes 對(duì) Secret 有一些額外的保護(hù)。
Secret 通常保存重要性各異的數(shù)值,其中很多都可能會(huì)導(dǎo)致 Kubernetes 中 (例如,服務(wù)賬號(hào)令牌)或?qū)ν獠肯到y(tǒng)的特權(quán)提升。 即使某些個(gè)別應(yīng)用能夠推導(dǎo)它期望使用的 Secret 的能力, 同一名字空間中的其他應(yīng)用可能會(huì)讓這種假定不成立。
只有當(dāng)某個(gè)節(jié)點(diǎn)上的 Pod 需要某 Secret 時(shí),對(duì)應(yīng)的 Secret 才會(huì)被發(fā)送到該節(jié)點(diǎn)上。 如果將 Secret 掛載到 Pod 中,kubelet 會(huì)將數(shù)據(jù)的副本保存在在 ?tmpfs
?中, 這樣機(jī)密的數(shù)據(jù)不會(huì)被寫入到持久性存儲(chǔ)中。 一旦依賴于該 Secret 的 Pod 被刪除,kubelet 會(huì)刪除來自于該 Secret 的機(jī)密數(shù)據(jù)的本地副本。
同一個(gè) Pod 中可能包含多個(gè)容器。默認(rèn)情況下,你所定義的容器只能訪問默認(rèn) ServiceAccount 及其相關(guān) Secret。你必須顯式地定義環(huán)境變量或者將卷映射到容器中,才能為容器提供對(duì)其他 Secret 的訪問。
針對(duì)同一節(jié)點(diǎn)上的多個(gè) Pod 可能有多個(gè) Secret。不過,只有某個(gè) Pod 所請(qǐng)求的 Secret 才有可能對(duì) Pod 中的容器可見。因此,一個(gè) Pod 不會(huì)獲得訪問其他 Pod 的 Secret 的權(quán)限。
Warning:
節(jié)點(diǎn)上的所有特權(quán)容器都可能訪問到該節(jié)點(diǎn)上使用的所有 Secret。
watch
?和 ?list
?請(qǐng)求是非常強(qiáng)大的能力。 在可能的時(shí)候應(yīng)該避免授予這類訪問權(quán)限,因?yàn)橥ㄟ^列舉 Secret, 客戶端能夠查看對(duì)應(yīng)名字空間內(nèi)所有 Secret 的取值。Caution:
能夠創(chuàng)建使用 Secret 的 Pod 的用戶也可以查看該 Secret 的取值。 即使集群策略不允許某用戶直接讀取 Secret 對(duì)象,這一用戶仍然可以通過運(yùn)行一個(gè) Pod 來訪問 Secret 的內(nèi)容。
watch
?或 ?list
?操作的能力, 這樣只有特權(quán)級(jí)最高、系統(tǒng)級(jí)別的組件能夠執(zhí)行這類操作。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: