本頁展示了如何配置密鑰管理服務(Key Management Service,KMS)驅(qū)動和插件以啟用 Secret 數(shù)據(jù)加密。 目前有兩個 KMS API 版本。KMS v1 將繼續(xù)工作,而 KMS v2 將開發(fā)得逐漸成熟。 如果你不確定要選用哪個 KMS API 版本,可選擇 v1。
準備開始
你必須擁有一個 Kubernetes 的集群,同時你的 Kubernetes 集群必須帶有 kubectl 命令行工具。 建議在至少有兩個節(jié)點的集群上運行本教程,且這些節(jié)點不作為控制平面主機。 如果你還沒有集群,你可以通過 Minikube 構建一個你自己的集群,或者你可以使用下面任意一個 Kubernetes 工具構建:
你所需要的 Kubernetes 版本取決于你已選擇的 KMS API 版本。
- 如果你選擇了 KMS API v1,所有受支持的 Kubernetes 版本都可以正常工作。
- 如果你選擇了 KMS API v2,則應使用 Kubernetes v1.26 (如果你正在運行也支持 KMS API v2 的其他 Kubernetes 版本,需查閱該 Kubernetes 版本的文檔)。要獲知版本信息,請輸入 kubectl version.
KMS v1
- 需要 Kubernetes 1.10.0 或更高版本
- 你的集群必須使用 etcd v3 或更高版本
- 特性狀態(tài): Kubernetes v1.12 [beta]
KMS v2
- 需要 Kubernetes 1.25.0 或更高版本
- 設置 kube-apiserver 特性門控:--feature-gates=KMSv2=true 以配置 KMS v2 驅(qū)動
- 你的集群必須使用 etcd v3 或更高版本
- 特性狀態(tài): Kubernetes v1.25 [alpha]
KMS 加密驅(qū)動使用封套加密模型來加密 etcd 中的數(shù)據(jù)。 數(shù)據(jù)使用數(shù)據(jù)加密密鑰(DEK)加密;每次加密都生成一個新的 DEK。 這些 DEK 經(jīng)一個密鑰加密密鑰(KEK)加密后在一個遠端的 KMS 中存儲和管理。 KMS 驅(qū)動使用 gRPC 與一個特定的 KMS 插件通信。這個 KMS 插件作為一個 gRPC 服務器被部署在 Kubernetes 控制平面的相同主機上,負責與遠端 KMS 的通信。
配置 KMS 驅(qū)動
為了在 API 服務器上配置 KMS 驅(qū)動,在加密配置文件中的 providers
數(shù)組中加入一個類型為 kms
的驅(qū)動,并設置下列屬性:
KMS v1
name
: KMS 插件的顯示名稱。一旦設置,就無法更改。endpoint
: gRPC 服務器(KMS 插件)的監(jiān)聽地址。該端點是一個 UNIX 域套接字。cachesize
: 以明文緩存的數(shù)據(jù)加密密鑰(DEK)的數(shù)量。一旦被緩存, 就可以直接使用 DEK 而無需另外調(diào)用 KMS;而未被緩存的 DEK 需要調(diào)用一次 KMS 才能解包。timeout
: 在返回一個錯誤之前,kube-apiserver
等待 kms-plugin 響應的時間(默認是 3 秒)。
KMS v2
apiVersion
:針對 KMS 驅(qū)動的 API 版本(允許的值:v2、v1 或空值。任何其他值都將產(chǎn)生一個錯誤。) 必須設置為 v2 才能使用 KMS v2 API。name
:KMS 插件的顯示名稱。一旦設置,就無法更改。endpoint
:gRPC 服務器(KMS 插件)的監(jiān)聽地址。該端點是一個 UNIX 域套接字。cachesize
:以明文緩存的數(shù)據(jù)加密密鑰(DEK)的數(shù)量。一旦被緩存, 就可以直接使用 DEK 而無需另外調(diào)用 KMS;而未被緩存的 DEK 需要調(diào)用一次 KMS 才能解包。timeout
:在返回一個錯誤之前,kube-apiserver
等待 kms-plugin 響應的時間(默認是 3 秒)。
實現(xiàn) KMS 插件
為實現(xiàn)一個 KMS 插件,你可以開發(fā)一個新的插件 gRPC 服務器或啟用一個由你的云服務驅(qū)動提供的 KMS 插件。 你可以將這個插件與遠程 KMS 集成,并把它部署到 Kubernetes 的主服務器上。
啟用由云服務驅(qū)動支持的 KMS
有關啟用云服務驅(qū)動特定的 KMS 插件的說明,請咨詢你的云服務驅(qū)動商。
開發(fā) KMS 插件 gRPC 服務器
你可以使用 Go 語言的存根文件開發(fā) KMS 插件 gRPC 服務器。 對于其他語言,你可以用 proto 文件創(chuàng)建可以用于開發(fā) gRPC 服務器代碼的存根文件。
KMS v1
- 使用 Go:使用存根文件 api.pb.go 中的函數(shù)和數(shù)據(jù)結構開發(fā) gRPC 服務器代碼。
- 使用 Go 以外的其他語言:用 protoc 編譯器編譯 proto 文件: api.proto 為指定語言生成存根文件。
KMS v2
- 使用 Go:使用存根文件 api.pb.go 中的函數(shù)和數(shù)據(jù)結構開發(fā) gRPC 服務器代碼。
- 使用 Go 以外的其他語言:用 protoc 編譯器編譯 proto 文件: api.proto 為指定語言生成存根文件。
然后使用存根文件中的函數(shù)和數(shù)據(jù)結構開發(fā)服務器代碼。
注意
KMS v1
- kms 插件版本:v1beta1作為對過程調(diào)用 Version 的響應,兼容的 KMS 插件應把 v1beta1 作為 VersionResponse.version 版本返回。
- 消息版本:v1beta1所有來自 KMS 驅(qū)動的消息都把 version 字段設置為當前版本 v1beta1
- 協(xié)議:UNIX 域套接字 (unix)該插件被實現(xiàn)為一個在 UNIX 域套接字上偵聽的 gRPC 服務器。 該插件部署時應在文件系統(tǒng)上創(chuàng)建一個文件來運行 gRPC UNIX 域套接字連接。 API 服務器(gRPC 客戶端)配置了 KMS 驅(qū)動(gRPC 服務器)UNIX 域套接字端點,以便與其通信。 通過以 /@ 開頭的端點,可以使用一個抽象的 Linux 套接字,即 unix:///@foo。 使用這種類型的套接字時必須小心,因為它們沒有 ACL 的概念(與傳統(tǒng)的基于文件的套接字不同)。 然而,這些套接字遵從 Linux 網(wǎng)絡命名空間約束,因此只能由同一 Pod 中的容器進行訪問,除非使用了主機網(wǎng)絡。
KMS v2
- kms 插件版本:v2alpha1作為對過程調(diào)用 Status 的響應,兼容的 KMS 插件應把 v2alpha1 作為 StatusResponse.Version 版本、 “ok” 作為 StatusResponse.Healthz 并且 keyID(KMS KEK ID)作為 StatusResponse.KeyID 返回。
- 協(xié)議:UNIX 域套接字 (unix)該插件被實現(xiàn)為一個在 UNIX 域套接字上偵聽的 gRPC 服務器。 該插件部署時應在文件系統(tǒng)上創(chuàng)建一個文件來運行 gRPC UNIX 域套接字連接。 API 服務器(gRPC 客戶端)配置了 KMS 驅(qū)動(gRPC 服務器)UNIX 域套接字端點,以便與其通信。 通過以 /@ 開頭的端點,可以使用一個抽象的 Linux 套接字,即 unix:///@foo。 使用這種類型的套接字時必須小心,因為它們沒有 ACL 的概念(與傳統(tǒng)的基于文件的套接字不同)。 然而,這些套接字遵從 Linux 網(wǎng)絡命名空間,因此只能由同一 Pod 中的容器進行訪問,除非使用了主機網(wǎng)絡。
將 KMS 插件與遠程 KMS 整合
KMS 插件可以用任何受 KMS 支持的協(xié)議與遠程 KMS 通信。 所有的配置數(shù)據(jù),包括 KMS 插件用于與遠程 KMS 通信的認證憑據(jù),都由 KMS 插件獨立地存儲和管理。 KMS 插件可以用額外的元數(shù)據(jù)對密文進行編碼,這些元數(shù)據(jù)是在把它發(fā)往 KMS 進行解密之前可能要用到的。
部署 KMS 插件
確保 KMS 插件與 Kubernetes 主服務器運行在同一主機上。
使用 KMS 驅(qū)動加密數(shù)據(jù)
為了加密數(shù)據(jù):
- 使用適合于 kms 驅(qū)動的屬性創(chuàng)建一個新的 EncryptionConfiguration 文件,以加密 Secret 和 ConfigMap 等資源。 如果要加密使用 CustomResourceDefinition 定義的擴展 API,你的集群必須運行 Kubernetes v1.26 或更高版本。
- 設置 kube-apiserver 的 --encryption-provider-config 參數(shù)指向配置文件的位置。
- --encryption-provider-config-automatic-reload 布爾參數(shù)決定了磁盤內(nèi)容發(fā)生變化時是否應自動重新加載 通過 --encryption-provider-config 設置的文件。這樣可以在不重啟 API 服務器的情況下進行密鑰輪換。
- 重啟你的 API 服務器。
KMS v1
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
- pandas.awesome.bears.example
providers:
- kms:
name: myKmsPluginFoo
endpoint: unix:///tmp/socketfile.sock
cachesize: 100
timeout: 3s
- kms:
name: myKmsPluginBar
endpoint: unix:///tmp/socketfile.sock
cachesize: 100
timeout: 3s
KMS v2
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
- pandas.awesome.bears.example
providers:
- kms:
apiVersion: v2
name: myKmsPluginFoo
endpoint: unix:///tmp/socketfile.sock
cachesize: 100
timeout: 3s
- kms:
name: myKmsPluginBar
endpoint: unix:///tmp/socketfile.sock
cachesize: 100
timeout: 3s
--encryption-provider-config-automatic-reload
設置為 true
會將所有健康檢查集中到同一個健康檢查端點。 只有 KMS v1 驅(qū)動正使用且加密配置未被自動重新加載時,才能進行獨立的健康檢查。
下表總結了每個 KMS 版本的健康檢查端點:
KMS 配置 | 沒有自動重新加載 | 有自動重新加載 |
---|---|---|
僅 KMS v1 | Individual Healthchecks | Single Healthcheck |
僅 KMS v2 | Single Healthcheck | Single Healthcheck |
KMS v1 和 v2 | Individual Healthchecks | Single Healthcheck |
沒有 KMS | 無 | Single Healthcheck |
Single Healthcheck
意味著唯一的健康檢查端點是 /healthz/kms-providers
。
Individual Healthchecks
意味著每個 KMS 插件都有一個對應的健康檢查端點, 并且這一端點基于插件在加密配置中的位置確定,例如 /healthz/kms-provider-0
、/healthz/kms-provider-1
等。
這些健康檢查端點路徑是由服務器硬編碼、生成并控制的。 Individual Healthchecks
的索引序號對應于 KMS 加密配置被處理的順序。
在執(zhí)行確保所有 Secret 都加密中所給步驟之前, providers
列表應以 identity: {}
提供程序作為結尾,以允許讀取未加密的數(shù)據(jù)。 加密所有資源后,應移除 identity
提供程序,以防止 API 服務器接受未加密的數(shù)據(jù)。
有關 EncryptionConfiguration
格式的更多詳細信息,請參閱 kube-apiserver 加密 API 參考 (v1).
驗證數(shù)據(jù)已經(jīng)加密
寫入 etcd 時數(shù)據(jù)被加密。重啟 kube-apiserver
后,所有新建或更新的 Secret 或在 EncryptionConfiguration
中配置的其他資源類型在存儲時應該已被加密。 要驗證這點,你可以用 etcdctl
命令行程序獲取私密數(shù)據(jù)的內(nèi)容。
- 在默認的命名空間里創(chuàng)建一個名為 secret1 的 Secret:
kubectl create secret generic secret1 -n default --from-literal=mykey=mydata
- 用 etcdctl 命令行,從 etcd 讀取出 Secret:
ETCDCTL_API=3 etcdctl get /kubernetes.io/secrets/default/secret1 [...] | hexdump -C
其中 [...] 包含連接 etcd 服務器的額外參數(shù)。
3.驗證對于 KMS v1,保存的 Secret 以 k8s:enc:kms:v1:
開頭, 對于 KMS v2,保存的 Secret 以 k8s:enc:kms:v2:
開頭,這表明 kms
驅(qū)動已經(jīng)對結果數(shù)據(jù)加密。
4. 驗證通過 API 獲取的 Secret 已被正確解密:
kubectl describe secret secret1 -n default Secret 應包含 mykey: mydata。
確保所有 Secret 都已被加密
因為 Secret 是在寫入時被加密的,所以在更新 Secret 時也會加密該內(nèi)容。
下列命令讀取所有 Secret 并更新它們以便應用服務器端加密。如果因為寫入沖突導致錯誤發(fā)生, 請重試此命令。對較大的集群,你可能希望根據(jù)命名空間或腳本更新去細分 Secret 內(nèi)容。
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
從本地加密驅(qū)動切換到 KMS 驅(qū)動
為了從本地加密驅(qū)動切換到 kms
驅(qū)動并重新加密所有 Secret 內(nèi)容:
- 在配置文件中加入 kms 驅(qū)動作為第一個條目,如下列樣例所示
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- kms:
name : myKmsPlugin
endpoint: unix:///tmp/socketfile.sock
cachesize: 100
- aescbc:
keys:
- name: key1
secret: <BASE 64 ENCODED SECRET>
2. 重啟所有 kube-apiserver 進程。
3. 運行下列命令使用 kms 驅(qū)動強制重新加密所有 Secret。
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
禁用靜態(tài)數(shù)據(jù)加密
要禁用靜態(tài)數(shù)據(jù)加密:
- 將 identity 驅(qū)動作為配置文件中的第一個條目:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- identity: {}
- kms:
name : myKmsPlugin
endpoint: unix:///tmp/socketfile.sock
cachesize: 100
2. 重啟所有 kube-apiserver 進程。
3. 運行下列命令強制重新加密所有 Secret。
kubectl get secrets --all-namespaces -o json | kubectl replace -f -