一、Secret
Secret 是 Kubernetes 中用于保存敏感信息的特殊對象。它可以包含密碼、令牌、密鑰等少量機密數(shù)據(jù),并且可以被掛載到 Pod 的規(guī)約或鏡像中使用。使用 Secret 的好處是,可以將機密數(shù)據(jù)與應用程序代碼分離,從而增加安全性。
在創(chuàng)建 Secret 時,可以獨立于使用該 Secret 的 Pod 進行操作。這意味著在創(chuàng)建、查看和編輯 Pod 的過程中,相對于 ConfigMap,Secret 的風險較小。此外,Kubernetes 和集群中的應用程序通常會采取額外的措施來保護 Secret 數(shù)據(jù),例如避免將敏感信息寫入非易失性存儲。
需要注意的是,Secret 類似于 ConfigMap,但其主要目的是保存敏感數(shù)據(jù)。詳情可參閱《Kubernetes ConfigMap配置》。
注意:默認情況下,Kubernetes Secret 未加密地存儲在 API 服務器的底層數(shù)據(jù)存儲(etcd)中。 任何擁有 API 訪問權限的人都可以檢索或修改 Secret,任何有權訪問 etcd 的人也可以。 此外,任何有權限在命名空間中創(chuàng)建 Pod 的人都可以使用該訪問權限讀取該命名空間中的任何 Secret; 這包括間接訪問,例如創(chuàng)建 Deployment 的能力。
為了安全地使用 Secret,請執(zhí)行以下步驟:
1、為 Secret 啟用靜態(tài)加密。
2、以最小特權訪問 Secret 并啟用或配置 RBAC 規(guī)則。
3、限制 Secret 對特定容器的訪問。
4、考慮使用外部 Secret 存儲驅動。
二、使用Secret
Secret 可以將用于以下場景:
- 設置容器的環(huán)境變量;
- 向 Pod 提供 SSH 密鑰或密碼等憑據(jù);
- 允許 kubelet 從私有鏡像倉庫中拉取鏡像。
Kubernetes 控制面也使用 Secret, 例如,引導令牌 Secret 是一種幫助自動化節(jié)點注冊的機制。
1、在 Secret 卷中帶句點的文件
通過定義以句點(.)開頭的主鍵,可以“隱藏”數(shù)據(jù), 這些主鍵代表的是以句點開頭的文件或“隱藏”文件。 例如,當以下 Secret 被掛載到 secret-volume 卷上時,該卷中會包含一個名為 .secret-file 的文件,并且容器 dotfile-test-container 中此文件位于路徑 /etc/secret-volume/.secret-file 處。
以句點開頭的文件會在 ls -l 的輸出中被隱藏起來; 列舉目錄內容時必須使用 ls -la 才能看到它們。
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: registry.k8s.io/busybox command: - ls - "-l" - "/etc/secret-volume" volumeMounts: - name: secret-volume readOnly: true mountPath: "/etc/secret-volume"
2、僅對 Pod 中一個容器可見的 Secret
考慮一個需要處理 HTTP 請求,執(zhí)行某些復雜的業(yè)務邏輯,之后使用 HMAC 來對某些消息進行簽名的程序。因為這一程序的應用邏輯很復雜, 其中可能包含未被注意到的遠程服務器文件讀取漏洞, 這種漏洞可能會把私鑰暴露給攻擊者。
這一程序可以分隔成兩個容器中的兩個進程:前端容器要處理用戶交互和業(yè)務邏輯, 但無法看到私鑰,目前簽名容器可以看到私鑰,并對來自前端的簡單簽名請求作出響應 (例如,通過本地主機網絡)。采用這種劃分的方法,攻擊者現(xiàn)在必須欺騙應用服務器來做一些其他操作, 而這些操作可能要比讀取一個文件要復雜很多。
3、Secret的替代方案
除了使用 Secret 來保護機密數(shù)據(jù),也可以選擇其他一些替代方案。下面是一些選項:
- 如果云原生組件需要執(zhí)行身份認證來訪問所知道的、在同一 Kubernetes 集群中運行的另一個應用, 可以使用 ServiceAccount 及其令牌來標識客戶端身份;
- 可以運行的第三方工具也有很多,這些工具可以運行在集群內或集群外,提供機密數(shù)據(jù)管理。 例如,這一工具可能是 Pod 通過 HTTPS 訪問的一個服務,該服務在客戶端能夠正確地通過身份認證 (例如,通過 ServiceAccount 令牌)時,提供機密數(shù)據(jù)內容;
- 就身份認證而言,可以為 X.509 證書實現(xiàn)一個定制的簽名者,并使用 CertificateSigningRequest 來讓該簽名者為需要證書的 Pod 發(fā)放證書;
- 可以使用一個設備插件 來將節(jié)點本地的加密硬件暴露給特定的 Pod。例如,可以將可信任的 Pod 調度到提供可信平臺模塊(Trusted Platform Module,TPM)的節(jié)點上。 這類節(jié)點是另行配置的。
還可以將如上選項的兩種或多種進行組合,包括直接使用 Secret 對象本身也是一種選項。例如:實現(xiàn)(或部署)一個 operator, 從外部服務取回生命期很短的會話令牌,之后基于這些生命期很短的會話令牌來創(chuàng)建 Secret。 運行在集群中的 Pod 可以使用這些會話令牌,而 Operator 則確保這些令牌是合法的。 這種責權分離意味著可以運行那些不了解會話令牌如何發(fā)放與刷新的確切機制的 Pod。
三、Secret類型
創(chuàng)建 Secret 時可以指定 type 字段或等效的 kubectl 命令行參數(shù)來設置 Secret 的類型。這有助于對 Secret 數(shù)據(jù)進行編程處理。Kubernetes 提供了若干種內置的類型,用于一些常見的使用場景。例如:
內置類型 | 用法 |
Opaque | 用戶定義的任意數(shù)據(jù) |
kubernetes.io/service-account-token | 服務賬號令牌 |
kubernetes.io/dockercfg | ~/.dockercfg?文件的序列化形式 |
kubernetes.io/dockerconfigjson | ~/.docker/config.json?文件的序列化形式 |
kubernetes.io/basic-auth | 用于基本身份認證的憑據(jù) |
kubernetes.io/ssh-auth | 用于 SSH 身份認證的憑據(jù) |
kubernetes.io/tls | 用于 TLS 客戶端或者服務器端的數(shù)據(jù) |
bootstrap.kubernetes.io/token | 啟動引導令牌數(shù)據(jù) |
通過為 Secret 對象的 type 字段設置一個非空的字符串值,也可以定義并使用自己 Secret 類型(如果 type 值為空字符串,則被視為 Opaque 類型)。
Kubernetes 并不對類型的名稱作任何限制。不過,如果要使用內置類型之一, 則必須滿足為該類型所定義的所有要求。如果要定義一種公開使用的 Secret 類型,請遵守 Secret 類型的約定和結構, 在類型名簽名添加域名,并用 / 隔開。 例如:cloud-hosting.example.net/cloud-api-credentials。
1、Opaque Secret
當未在 Secret 清單中顯式指定類型時,默認的 Secret 類型是 Opaque; 當使用 kubectl 來創(chuàng)建一個 Secret 時,必須使用 generic 子命令來標明要創(chuàng)建的是一個 Opaque 類型的 Secret。 例如,下面的命令會創(chuàng)建一個空的 Opaque 類型的 Secret:
kubectl create secret generic empty-secret kubectl get secret empty-secret
輸出類似于:
NAME TYPE DATA AGE empty-secret Opaque 0 2m6s
DATA 列顯示 Secret 中保存的數(shù)據(jù)條目個數(shù)。 在這個例子中,0 意味著剛剛創(chuàng)建了一個空的 Secret。
2、ServiceAccount 令牌 Secret
類型為 kubernetes.io/service-account-token 的 Secret 用來存放標識某 ServiceAccount 的令牌憑據(jù),這是為 Pod 提供長期有效 ServiceAccount 憑據(jù)的傳統(tǒng)機制。在 Kubernetes v1.22 及更高版本中,推薦的方法是通過使用 TokenRequest API 來獲取短期自動輪換的 ServiceAccount 令牌??梢允褂靡韵路椒ǐ@取這些短期令牌:
直接調用 TokenRequest API,或者使用像 kubectl 這樣的 API 客戶端。 例如,可以使用 kubectl create token 命令;在 Pod 清單中請求使用投射卷掛載的令牌,Kubernetes 會創(chuàng)建令牌并將其掛載到 Pod 中;當掛載令牌的 Pod 被刪除時,此令牌會自動失效。
注意:只有在無法使用 TokenRequest API 來獲取令牌, 并且能夠接受因為將永不過期的令牌憑據(jù)寫入到可讀取的 API 對象而帶來的安全風險時, 才應該創(chuàng)建 ServiceAccount 令牌 Secret。
使用這種 Secret 類型時,需要確保對象的注解 kubernetes.io/service-account-name 被設置為某個已有的 ServiceAccount 名稱, 如果同時創(chuàng)建 ServiceAccount 和 Secret 對象,應該先創(chuàng)建 ServiceAccount 對象。當 Secret 對象被創(chuàng)建之后,某個 Kubernetes 控制器會填寫 Secret 的其它字段,例如 kubernetes.io/service-account.uid 注解和 data 字段中的 token 鍵值(該鍵包含一個身份認證令牌)。
下面的配置實例聲明了一個 ServiceAccount 令牌 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: extra: YmFyCg==
創(chuàng)建了 Secret 之后,等待 Kubernetes 在 data 字段中填充 token 主鍵。
3、Docker 配置 Secret
如果要創(chuàng)建 Secret 用來存放用于訪問容器鏡像倉庫的憑據(jù),則必須選用以下 type 值之一來創(chuàng)建 Secret:
- kubernetes.io/dockercfg:存放 ~/.dockercfg 文件的序列化形式,它是配置 Docker 命令行的一種老舊形式。Secret 的 data 字段包含名為 .dockercfg 的主鍵, 其值是用 base64 編碼的某 ~/.dockercfg 文件的內容;
- kubernetes.io/dockerconfigjson:存放 JSON 數(shù)據(jù)的序列化形式, 該 JSON 也遵從 ~/.docker/config.json 文件的格式規(guī)則,而后者是 ~/.dockercfg 的新版本格式。使用此 Secret 類型時,Secret 對象的 data 字段必須包含 .dockerconfigjson 鍵,其鍵值為 base64 編碼的字符串包含 ~/.docker/config.json 文件的內容。
下面是一個 kubernetes.io/dockercfg 類型 Secret 的示例:
apiVersion: v1 kind: Secret metadata: name: secret-dockercfg type: kubernetes.io/dockercfg data: .dockercfg: | eyJhdXRocyI6eyJodHRwczovL2V4YW1wbGUvdjEvIjp7ImF1dGgiOiJvcGVuc2VzYW1lIn19fQo=
如果不希望執(zhí)行 base64 編碼轉換,可以使用 stringData 字段代替。
當使用清單文件通過 Docker 配置來創(chuàng)建 Secret 時,Kubernetes API 服務器會對 data 字段進行檢查。它會檢查是否存在期望的主鍵,并驗證提供的鍵值是否是合法的 JSON 數(shù)據(jù)。但是,API 服務器并不會檢查 JSON 數(shù)據(jù)本身是否包含有效的 Docker 配置文件內容。
還可以使用 kubectl 創(chuàng)建一個 Secret 來訪問容器倉庫時, 當沒有 Docker 配置文件時可以這樣做:
kubectl create secret docker-registry secret-tiger-docker \ --docker-email=tiger@acme.example \ --docker-username=tiger \ --docker-password=pass1234 \ --docker-server=my-registry.example:5000
此命令創(chuàng)建一個類型為 kubernetes.io/dockerconfigjson 的 Secret。
從這個新的 Secret 中獲取 .data.dockerconfigjson 字段并執(zhí)行數(shù)據(jù)解碼:
kubectl get secret secret-tiger-docker -o jsonpath='{.data.*}' | base64 -d
輸出等價于以下 JSON 文檔(這也是一個有效的 Docker 配置文件):
{ "auths": { "my-registry.example:5000": { "username": "tiger", "password": "pass1234", "email": "tiger@acme.example", "auth": "dGlnZXI6cGFzczEyMzQ=" } } }
注意:
- auths 值是 base64 編碼的,其內容被屏蔽但未被加密。 任何能夠讀取該 Secret 的人都可以了解鏡像庫的訪問令牌。
- 建議使用憑據(jù)提供程序來動態(tài)、 安全地按需提供拉取 Secret。
4、基本身份認證 Secret
kubernetes.io/basic-auth 類型用來存放用于基本身份認證所需的憑據(jù)信息,使用這種 Secret 類型時,Secret 的 data 字段必須包含以下兩個鍵之一:
- username: 用于身份認證的用戶名;
- password: 用于身份認證的密碼或令牌。
以上兩個鍵的鍵值都是 base64 編碼的字符串。 當然也可以在 Secret 清單中的使用 stringData 字段來提供明文形式的內容。
以下清單是基本身份驗證 Secret 的示例:
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 類型的必需字段
注意:Secret 的 stringData 字段不能很好地與服務器端應用配合使用。
提供基本身份認證類型的 Secret 是為了方便使用者,雖然使用 Opaque 類型來保存基本身份認證憑據(jù)是可行的,但使用預定義的、公開的 Secret 類型(如 kubernetes.io/basic-auth)可以提供更好的可讀性和一致性。這樣可以使他人更容易理解 Secret 的目的,并且確保在不同環(huán)境中使用相同的主鍵。
5、SSH 身份認證 Secret
Kubernetes 所提供的內置類型 kubernetes.io/ssh-auth 用來存放 SSH 身份認證中所需要的憑據(jù)。 使用這種 Secret 類型時,就必須在其 data (或 stringData) 字段中提供一個 ssh-privatekey 鍵值對,作為要使用的 SSH 憑據(jù)。
下面的清單是一個 SSH 公鑰/私鑰身份認證的 Secret 示例:
apiVersion: v1 kind: Secret metadata: name: secret-ssh-auth type: kubernetes.io/ssh-auth data: # 此例中的實際數(shù)據(jù)被截斷 ssh-privatekey: | MIIEpQIBAAKCAQEAulqb/Y ...
提供 SSH 身份認證類型的 Secret 也是為了方便使用者。 雖然使用 Opaque 類型來保存 SSH 身份認證憑據(jù)是可行的,但使用預定義的、公開的 Secret 類型(如 kubernetes.io/ssh-auth)可以提供更好的可讀性和一致性。這樣可以使其他人更容易理解 Secret 的用途,并且確保在不同環(huán)境中使用相同的主鍵。
注意:SSH 私鑰自身無法建立 SSH 客戶端與服務器端之間的可信連接。 需要其它方式來建立這種信任關系,以緩解“中間人(Man In The Middle)” 攻擊,例如向 ConfigMap 中添加一個 known_hosts 文件。
6、TLS Secret
kubernetes.io/tls Secret 類型用來存放 TLS 場合通常要使用的證書及其相關密鑰。
TLS Secret 的一種典型用法是為 Ingress 資源配置傳輸過程中的數(shù)據(jù)加密,不過也可以用于其他資源或者直接在負載中使用。 當使用此類型的 Secret 時,Secret 配置中的 data (或 stringData)字段必須包含 tls.key 和 tls.crt 主鍵,盡管 API 服務器實際上并不會對每個鍵的取值作進一步的合法性檢查。作為使用 stringData 的替代方法,可以使用 data 字段來指定 base64 編碼的證書和私鑰。
下面的 YAML 包含一個 TLS Secret 的配置示例:
apiVersion: v1 kind: Secret metadata: name: secret-tls type: kubernetes.io/tls data: # 值為 base64 編碼,這樣會掩蓋它們,但不會提供任何有用的機密性級別 tls.crt: | LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNVakNDQWJzQ0FnMytNQTBHQ1NxR1NJYjNE UUVCQlFVQU1JR2JNUXN3Q1FZRFZRUUdFd0pLVURFT01Bd0cKQTFVRUNCTUZWRzlyZVc4eEVEQU9C Z05WQkFjVEIwTm9kVzh0YTNVeEVUQVBCZ05WQkFvVENFWnlZVzVyTkVSRQpNUmd3RmdZRFZRUUxF dzlYWldKRFpYSjBJRk4xY0hCdmNuUXhHREFXQmdOVkJBTVREMFp5WVc1ck5FUkVJRmRsCllpQkRR VEVqTUNFR0NTcUdTSWIzRFFFSkFSWVVjM1Z3Y0c5eWRFQm1jbUZ1YXpSa1pDNWpiMjB3SGhjTk1U TXcKTVRFeE1EUTFNVE01V2hjTk1UZ3dNVEV3TURRMU1UTTVXakJMTVFzd0NRWURWUVFHREFKS1VE RVBNQTBHQTFVRQpDQXdHWEZSdmEzbHZNUkV3RHdZRFZRUUtEQWhHY21GdWF6UkVSREVZTUJZR0Ex VUVBd3dQZDNkM0xtVjRZVzF3CmJHVXVZMjl0TUlHYU1BMEdDU3FHU0liM0RRRUJBUVVBQTRHSUFE Q0JoQUo5WThFaUhmeHhNL25PbjJTbkkxWHgKRHdPdEJEVDFKRjBReTliMVlKanV2YjdjaTEwZjVN Vm1UQllqMUZTVWZNOU1vejJDVVFZdW4yRFljV29IcFA4ZQpqSG1BUFVrNVd5cDJRN1ArMjh1bklI QkphVGZlQ09PekZSUFY2MEdTWWUzNmFScG04L3dVVm16eGFLOGtCOWVaCmhPN3F1TjdtSWQxL2pW cTNKODhDQXdFQUFUQU5CZ2txaGtpRzl3MEJBUVVGQUFPQmdRQU1meTQzeE15OHh3QTUKVjF2T2NS OEtyNWNaSXdtbFhCUU8xeFEzazlxSGtyNFlUY1JxTVQ5WjVKTm1rWHYxK2VSaGcwTi9WMW5NUTRZ RgpnWXcxbnlESnBnOTduZUV4VzQyeXVlMFlHSDYyV1hYUUhyOVNVREgrRlowVnQvRGZsdklVTWRj UUFEZjM4aU9zCjlQbG1kb3YrcE0vNCs5a1h5aDhSUEkzZXZ6OS9NQT09Ci0tLS0tRU5EIENFUlRJ RklDQVRFLS0tLS0K # 在這個例子中,密鑰數(shù)據(jù)不是真正的 PEM 編碼的私鑰 tls.key: | RXhhbXBsZSBkYXRhIGZvciB0aGUgVExTIGNydCBmaWVsZA==
提供 TLS 類型的 Secret 僅僅是出于方便性考慮,可以創(chuàng)建 Opaque 類型的 Secret 來保存用于 TLS 身份認證的憑據(jù)。 不過,使用已定義和公開的 Secret 類型有助于確保自己項目中的 Secret 格式的一致性, API 服務器會驗證這種類型的 Secret 是否設定了所需的主鍵。
要使用 kubectl 創(chuàng)建 TLS Secret,可以使用 tls 子命令:
kubectl create secret tls my-tls-secret \ --cert=path/to/cert/file \ --key=path/to/key/file
公鑰/私鑰對必須事先存在,–cert 的公鑰證書必須采用 .PEM 編碼, 并且必須與 –key 的給定私鑰匹配。
7、啟動引導令牌 Secret
bootstrap.kubernetes.io/token Secret 類型針對的是節(jié)點啟動引導過程所用的令牌,其中包含用來為周知的 ConfigMap 簽名的令牌。啟動引導令牌 Secret 通常創(chuàng)建于 kube-system 名字空間內,并以 bootstrap-token-<令牌 ID> 的形式命名; 其中 <令牌 ID> 是一個由 6 個字符組成的字符串,用作令牌的標識。
以 Kubernetes 清單文件的形式,某啟動引導令牌 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==
啟動引導令牌類型的 Secret 通常包含以下主鍵在其 data 字段中:
- token-id:一個由6個隨機字符組成的字符串,用作令牌的標識符,是必需的;
- token-secret:一個由16個隨機字符組成的字符串,包含實際的令牌機密,也是必需的;
- description:一個可選的字符串,用于描述令牌的用途,供用戶閱讀;
- expiration:一個可選的使用 RFC3339 編碼的 UTC 絕對時間,指示令牌的過期時間;
- usage-bootstrap-<usage>:一組布爾類型的標志,用于標識啟動引導令牌的其他用途。這些標志可以根據(jù)具體情況命名為 usage-bootstrap-xxx,其中 xxx 是用途的名稱;
- auth-extra-groups:一個用逗號分隔的組名列表,用于身份認證時將額外添加到被認證用戶的組中。這些組名除了 system:bootstrappers 組之外會被添加進去。
也可以在 Secret 的 stringData 字段中提供值,而無需對其進行 base64 編碼:
apiVersion: v1 kind: Secret metadata: # 注意 Secret 的命名方式 name: bootstrap-token-5emitj # 啟動引導令牌 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 (身份認證) usage-bootstrap-authentication: "true" # 且可用于 signing (證書簽名) usage-bootstrap-signing: "true"
Secret 的 stringData 字段不能很好地與服務器端應用配合使用。
四、使用Secret
1、創(chuàng)建Secret
創(chuàng)建 Secret 有以下幾種可選方式:
- 使用 kubectl;
- 使用配置文件;
- 使用 Kustomize 工具。
2、對 Secret 名稱與數(shù)據(jù)的約束
創(chuàng)建 Kubernetes Secret 對象時,其名稱必須是合法的 DNS 子域名。 在 Secret 的 YAML 配置文件中,數(shù)據(jù)可以通過 data 和 stringData 字段指定,并且這兩個字段都是可選的。
在 data 字段中,鍵值對中的值必須是 base64 編碼的字符串。但是,在 stringData 字段中可以使用任何字符串作為值,而不需要進行編碼操作。
Secret 中的鍵名必須是合法的 DNS 子域名,只能包含字母、數(shù)字、-、_ 或 . 字符。 如果在 stringData 和 data 字段中都存在同名鍵,則以 stringData 中的鍵值對為準,其將覆蓋 data 字段中的同名鍵值對。
3、尺寸限制
每個 Secret 的尺寸最多為 1MiB,施加這一限制是為了避免用戶創(chuàng)建非常大的 Secret, 進而導致 API 服務器和 kubelet 內存耗盡。不過創(chuàng)建很多小的 Secret 也可能耗盡內存,可以使用資源配額來約束每個名字空間中 Secret(或其他資源)的個數(shù)。
4、編輯 Secret
正常情況下可以編輯一個已有的 Secret,除非它是不可變更的。 要編輯一個 Secret,可使用以下方法之一:
- 使用 kubectl;
- 使用配置文件。
也可以使用 Kustomize 工具編輯數(shù)據(jù),但是這種方法會用編輯過的數(shù)據(jù)創(chuàng)建新的 Secret 對象。
5、使用 Secret
Secret 可以以數(shù)據(jù)卷的形式掛載,也可以作為環(huán)境變量暴露給 Pod 中的容器使用,還可用于系統(tǒng)中的其他部分,而不是一定要直接暴露給 Pod。 例如,Secret 也可以包含系統(tǒng)中其他部分在替與外部系統(tǒng)交互時要使用的憑證數(shù)據(jù)。
Kubernetes 會檢查 Secret 的卷數(shù)據(jù)源,確保所指定的對象引用確實指向類型為 Secret 的對象。因此,如果 Pod 依賴于某 Secret,該 Secret 必須先于 Pod 被創(chuàng)建。
如果 Secret 內容無法取回(可能因為 Secret 尚不存在或者臨時性地出現(xiàn) API 服務器網絡連接問題),kubelet 會周期性地重試 Pod 運行操作。kubelet 也會為該 Pod 報告 Event 事件,給出讀取 Secret 時遇到的問題細節(jié)。
6、可選的 Secret
當在 Pod 中引用 Secret 時,可以將該 Secret 標記為可選,就像下面例子中所展示的那樣。 如果可選的 Secret 不存在,Kubernetes 將忽略它。
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: true
默認情況下,Secret 是必需的。在所有非可選的 Secret 都可用之前,Pod 的所有容器都不會啟動。如果 Pod 引用了非可選 Secret 中的特定鍵,并且該 Secret 確實存在,但缺少所指定的鍵, 則 Pod 在啟動期間會失敗。
7、在 Pod 以文件形式使用 Secret
如果要在 Pod 中訪問來自 Secret 的數(shù)據(jù),一種方式是讓 Kubernetes 將該 Secret 的值以 文件的形式呈現(xiàn),該文件存在于 Pod 中一個或多個容器內的文件系統(tǒng)內。
當卷中包含來自 Secret 的數(shù)據(jù),而對應的 Secret 被更新,Kubernetes 會跟蹤到這一操作并更新卷中的數(shù)據(jù)。更新的方式是保證最終一致性。對于以 subPath 形式掛載 Secret 卷的容器而言, 它們無法收到自動的 Secret 更新。
Kubelet 組件會維護一個緩存,在其中保存節(jié)點上 Pod 卷中使用的 Secret 的當前主鍵和取值,可以配置 kubelet 如何檢測所緩存數(shù)值的變化。 kubelet 配置中的 configMapAndSecretChangeDetectionStrategy 字段控制 kubelet 所采用的策略, 默認的策略是 Watch。
對 Secret 的更新操作既可以通過 API 的 watch 機制(默認)來傳播, 基于設置了生命期的緩存獲取,也可以通過 kubelet 的同步回路來從集群的 API 服務器上輪詢獲取。因此,從 Secret 被更新到新的主鍵被投射到 Pod 中,中間存在一個延遲, 這一延遲的上限是 kubelet 的同步周期加上緩存的傳播延遲, 其中緩存的傳播延遲取決于所選擇的緩存類型, 對應上一段中提到的幾種傳播機制,延遲時長為 watch 的傳播延遲、所配置的緩存 TTL 或者對于直接輪詢而言是零。
8、以環(huán)境變量的方式使用 Secret
如果需要在 Pod 中以環(huán)境變量的形式使用 Secret:
- 對于 Pod 規(guī)約中的每個容器,針對要使用的每個 Secret 鍵,將對應的環(huán)境變量添加到 env[].valueFrom.secretKeyRef 中;
- 更改鏡像或命令行,以便程序能夠從指定的環(huán)境變量找到所需要的值。
9、非法環(huán)境變量
如果 Pod 規(guī)約中環(huán)境變量定義會被視為非法的環(huán)境變量名,這些主鍵將在容器中不可用。 Pod 仍然可以啟動。
Kubernetes 添加一個 Event,其 reason 設置為 InvalidVariableNames,其消息將列舉被略過的非法主鍵。 下面的例子中展示了一個 Pod,引用的是名為 mysecret 的 Secret, 其中包含兩個非法的主鍵: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.
10、容器鏡像拉取 Secret
如果嘗試從私有倉庫拉取容器鏡像,需要一種方式讓每個節(jié)點上的 kubelet 能夠完成與鏡像庫的身份認證??梢耘渲?鏡像拉取 Secret 來實現(xiàn)這點。 Secret 是在 Pod 層面來配置的。
11、使用 imagePullSecrets
imagePullSecrets 字段是一個列表,包含對同一名字空間中 Secret 的引用。 可以使用 imagePullSecrets 將包含 Docker(或其他)鏡像倉庫密碼的 Secret 傳遞給 kubelet。
還可以手動創(chuàng)建 imagePullSecret,并在一個 ServiceAccount 中引用它。 對使用該 ServiceAccount 創(chuàng)建的所有 Pod,或者默認使用該 ServiceAccount 創(chuàng)建的 Pod 而言,其 imagePullSecrets 字段都會設置為該服務賬號。
12、在靜態(tài) Pod 中使用 Secret
不可以在靜態(tài) Pod 中使用 ConfigMap 或 Secret。
五、Secret使用場景
1、作為容器環(huán)境變量
可以創(chuàng)建 Secret 并使用它為容器設置環(huán)境變量。
2、帶 SSH 密鑰的 Pod
創(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)建一個 kustomization.yaml 文件,在其 secretGenerator 字段中包含 SSH 密鑰。
注意:
- 在提供自己的 SSH 密鑰之前要仔細思考:集群的其他用戶可能有權訪問該 Secret;
- 也可以創(chuàng)建一個 SSH 私鑰,代表一個希望與共享 Kubernetes 集群的其他用戶分享的服務標識。 當憑據(jù)信息被泄露時,可以收回該訪問權限。
現(xiàn)在可以創(chuàng)建一個 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ù)據(jù)可以在下面的位置訪問到:
/etc/secret-volume/ssh-publickey /etc/secret-volume/ssh-privatekey
容器就可以隨便使用 Secret 數(shù)據(jù)來建立 SSH 連接。
3、帶有生產、測試環(huán)境憑據(jù)的 Pod
這一示例所展示的一個 Pod 會使用包含生產環(huán)境憑據(jù)的 Secret,另一個 Pod 使用包含測試環(huán)境憑據(jù)的 Secret??梢詣?chuàng)建一個帶有 secretGenerator 字段的 kustomization.yaml 文件或者運行 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)建一個包含測試環(huán)境憑據(jù)的 Secret:
kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests
輸出類似于:
secret "test-db-secret" created
注意:
特殊字符(例如 $、\、*、= 和 !)會被 Shell 解釋,因此需要轉義。
在大多數(shù) Shell 中,對密碼進行轉義的最簡單方式是用單引號(’)將其括起來。 例如,如果實際密碼是 S!B\*d$zDsb,則應通過以下方式執(zhí)行命令:
kubectl create secret generic dev-db-secret --from-literal=username=devuser --from-literal=password='S!B\*d$zDsb='
無需對文件中的密碼(–from-file)中的特殊字符進行轉義。
現(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 服務器上應用所有這些對象:
kubectl apply -k .
兩個文件都會在其文件系統(tǒng)中出現(xiàn)下面的文件,文件中內容是各個容器的環(huán)境值:
/etc/secret-volume/username /etc/secret-volume/password
注意這兩個 Pod 的規(guī)約中只有一個字段不同。 這便于基于相同的 Pod 模板生成具有不同能力的 Pod。
可以通過使用兩個服務賬號來進一步簡化這一基本的 Pod 規(guī)約:
- prod-user 服務賬號使用 prod-db-secret;
- test-user 服務賬號使用 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
六、不可更改的Secret
在 Kubernetes 中,可以將某些 Secret 和 ConfigMap 標記為不可更改(Immutable)。這樣做有以下好處:
- 防止意外或非預期的更新導致應用程序中斷。如果一個 Secret 或 ConfigMap 是不可更改的,那么試圖對其進行更新操作將會失敗,并在 Kubernetes 事件中記錄一條錯誤消息。這可以幫助防止應用程序由于意外的更改而中斷。
- 對于大型使用 Secret 的集群,標記 Secret 為不可變可以顯著降低 kube-apiserver 的負載,提高整個集群的性能。因為 kubelet 不需要監(jiān)視被標記為不可更改的 Secret,所以它們不會被頻繁地輪詢和檢查。這有助于減少 kube-apiserver 的負載,從而提高集群的性能。
需要注意的是,一旦將 Secret 或 ConfigMap 標記為不可更改,就不能再對其進行更新操作,除非先將其重新標記為可更改。因此,在將 Secret 或 ConfigMap 標記為不可更改之前,請確保這些對象的數(shù)據(jù)已經是最終版本。
1、將 Secret 標記為不可更改
可以通過將 Secret 的 immutable 字段設置為 true 創(chuàng)建不可更改的 Secret。 例如:
apiVersion: v1 kind: Secret metadata: ... data: ... immutable: true
也可以更改現(xiàn)有的 Secret,令其不可更改。
一旦一個 Secret 或 ConfigMap 被標記為不可更改,撤銷此操作或者更改 data 字段的內容都是 不 可能的。 只能刪除并重新創(chuàng)建這個 Secret?,F(xiàn)有的 Pod 將維持對已刪除 Secret 的掛載點 — 建議重新創(chuàng)建這些 Pod。
七、Secret信息安全
盡管 ConfigMap 和 Secret 的工作方式類似,但 Kubernetes 對 Secret 有一些額外的保護。Secret 通常保存重要性各異的數(shù)值,其中很多都可能會導致 Kubernetes 中 (例如,服務賬號令牌)或對外部系統(tǒng)的特權提升。 即使某些個別應用能夠推導它期望使用的 Secret 的能力, 同一名字空間中的其他應用可能會讓這種假定不成立。
只有當某個節(jié)點上的 Pod 需要某 Secret 時,對應的 Secret 才會被發(fā)送到該節(jié)點上。 如果將 Secret 掛載到 Pod 中,kubelet 會將數(shù)據(jù)的副本保存在在 tmpfs 中, 這樣機密的數(shù)據(jù)不會被寫入到持久性存儲中。 一旦依賴于該 Secret 的 Pod 被刪除,kubelet 會刪除來自于該 Secret 的機密數(shù)據(jù)的本地副本。
同一個 Pod 中可能包含多個容器。默認情況下,所定義的容器只能訪問默認 ServiceAccount 及其相關 Secret。必須顯式地定義環(huán)境變量或者將卷映射到容器中,才能為容器提供對其他 Secret 的訪問。針對同一節(jié)點上的多個 Pod 可能有多個 Secret。不過,只有某個 Pod 所請求的 Secret 才有可能對 Pod 中的容器可見。因此,一個 Pod 不會獲得訪問其他 Pod 的 Secret 的權限。
注意:在一個節(jié)點上以 privileged: true 運行的所有容器可以訪問該節(jié)點上使用的所有 Secret。