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