使用 KMS 提供者進行資料加密
本頁說明如何配置金鑰管理服務 (KMS) 提供者與外掛程式,以啟用密碼資料加密。在 Kubernetes 1.32 中,有兩個版本的 KMS 靜態資料加密。如果可行,您應該使用 KMS v2,因為 KMS v1 已被棄用(自 Kubernetes v1.28 起)且預設為停用(自 Kubernetes v1.29 起)。KMS v2 提供比 KMS v1 顯著更佳的效能特性。
注意
此文件適用於 KMS v2 的正式發行實作(以及已棄用的版本 1 實作)。如果您使用的控制平面元件早於 Kubernetes v1.29,請查看您的叢集正在執行的 Kubernetes 版本的文件中對應的頁面。較早版本的 Kubernetes 有不同的行為,可能與資訊安全相關。準備開始
您需要有一個 Kubernetes 叢集,並且必須配置 kubectl 命令列工具以與您的叢集通訊。建議在至少有兩個節點且未充當控制平面主機的叢集上執行本教學。如果您還沒有叢集,可以使用 minikube 建立一個,或者您可以使用以下 Kubernetes Playground 之一
您需要的 Kubernetes 版本取決於您選擇的 KMS API 版本。Kubernetes 建議使用 KMS v2。
- 如果您選擇 KMS API v1 以支援 v1.27 之前的叢集,或者您有僅支援 KMS v1 的舊版 KMS 外掛程式,則任何支援的 Kubernetes 版本都適用。此 API 自 Kubernetes v1.28 起已棄用。Kubernetes 不建議使用此 API。
kubectl version
。KMS v1
Kubernetes v1.28 [已棄用]
需要 Kubernetes 版本 1.10.0 或更新版本
對於 1.29 及更新版本,KMS 的 v1 實作預設為停用。若要啟用此功能,請設定
--feature-gates=KMSv1=true
以配置 KMS v1 提供者。您的叢集必須使用 etcd v3 或更新版本
KMS v2
Kubernetes v1.29 [穩定]
- 您的叢集必須使用 etcd v3 或更新版本
KMS 加密和每個物件的加密金鑰
KMS 加密提供者使用信封加密方案來加密 etcd 中的資料。資料使用資料加密金鑰 (DEK) 進行加密。DEK 使用金鑰加密金鑰 (KEK) 進行加密,KEK 儲存並管理在遠端 KMS 中。
如果您使用(已棄用的)KMS v1 實作,則每次加密都會產生新的 DEK。
使用 KMS v2,每次加密都會產生新的 DEK:API 伺服器使用金鑰衍生函式,從密碼種子與一些隨機資料組合產生單次使用的資料加密金鑰。每當 KEK 輪換時,種子也會輪換(請參閱下方了解 key_id 與金鑰輪換章節以取得更多詳細資訊)。
KMS 提供者使用 gRPC 透過 UNIX 網域 Socket 與特定的 KMS 外掛程式通訊。KMS 外掛程式實作為 gRPC 伺服器,並部署在與 Kubernetes 控制平面相同的主機上,負責與遠端 KMS 的所有通訊。
配置 KMS 提供者
若要在 API 伺服器上配置 KMS 提供者,請在加密組態檔中的 providers
陣列中包含 kms
類型的提供者,並設定下列屬性
KMS v1
apiVersion
:KMS 提供者的 API 版本。將此值留空或設定為v1
。name
:KMS 外掛程式的顯示名稱。設定後無法變更。endpoint
:gRPC 伺服器(KMS 外掛程式)的監聽位址。端點是 UNIX 網域 Socket。cachesize
:要以明文快取的資料加密金鑰 (DEK) 數量。快取後,DEK 可以在不再次呼叫 KMS 的情況下使用;而未快取的 DEK 則需要呼叫 KMS 才能解包。timeout
:kube-apiserver
在傳回錯誤之前應等待 kms-plugin 回應的時間(預設為 3 秒)。
KMS v2
apiVersion
:KMS 提供者的 API 版本。將此設定為v2
。name
:KMS 外掛程式的顯示名稱。設定後無法變更。endpoint
:gRPC 伺服器(KMS 外掛程式)的監聽位址。端點是 UNIX 網域 Socket。timeout
:kube-apiserver
在傳回錯誤之前應等待 kms-plugin 回應的時間(預設為 3 秒)。
KMS v2 不支援 cachesize
屬性。一旦伺服器透過呼叫 KMS 解包所有資料加密金鑰 (DEK) 後,所有金鑰都將以明文快取。快取後,DEK 可以無限期地用於執行解密,而無需呼叫 KMS。
請參閱了解靜態資料加密配置。
實作 KMS 外掛程式
若要實作 KMS 外掛程式,您可以開發新的外掛程式 gRPC 伺服器,或啟用雲端供應商已提供的 KMS 外掛程式。然後,將此外掛程式與遠端 KMS 整合,並將其部署在 Kubernetes 控制平面上。
啟用您的雲端供應商支援的 KMS
請參閱您的雲端供應商以取得關於啟用雲端供應商特定 KMS 外掛程式的指示。
開發 KMS 外掛程式 gRPC 伺服器
您可以使用 Go 語言可用的 Stub 檔案來開發 KMS 外掛程式 gRPC 伺服器。對於其他語言,您可以使用 proto 檔案來建立 Stub 檔案,以便用於開發 gRPC 伺服器程式碼。
KMS v1
使用 Go 語言:使用 Stub 檔案中的函數和資料結構:api.pb.go 來開發 gRPC 伺服器程式碼
使用 Go 以外的語言:將 protoc 編譯器與 proto 檔案:api.proto 搭配使用,以產生特定語言的 Stub 檔案
KMS v2
使用 Go 語言:提供高階程式庫以簡化流程。低階實作可以使用 Stub 檔案中的函數和資料結構:api.pb.go 來開發 gRPC 伺服器程式碼
使用 Go 以外的語言:將 protoc 編譯器與 proto 檔案:api.proto 搭配使用,以產生特定語言的 Stub 檔案
然後使用 Stub 檔案中的函數和資料結構來開發伺服器程式碼。
注意事項
KMS v1
kms 外掛程式版本:
v1beta1
為了回應程序呼叫 Version,相容的 KMS 外掛程式應傳回
v1beta1
作為VersionResponse.version
。訊息版本:
v1beta1
來自 KMS 供應商的所有訊息都將版本欄位設定為
v1beta1
。協定:UNIX 網域 Socket (
unix
)此外掛程式實作為在 UNIX 網域 Socket 上接聽的 gRPC 伺服器。外掛程式部署應在檔案系統上建立檔案,以執行 gRPC unix 網域 Socket 連線。API 伺服器 (gRPC 用戶端) 配置了 KMS 供應商 (gRPC 伺服器) unix 網域 Socket 端點,以便與其通訊。抽象 Linux Socket 可以透過以
/@
開頭端點來使用,例如unix:///@foo
。使用此類型的 Socket 時必須謹慎,因為它們沒有 ACL 的概念 (與傳統的基於檔案的 Socket 不同)。但是,它們受 Linux 網路命名空間的約束,因此除非使用主機網路,否則僅能由相同 Pod 內的容器存取。
KMS v2
KMS 外掛程式版本:
v2
為了回應
Status
遠端程序呼叫,相容的 KMS 外掛程式應傳回其 KMS 相容性版本作為StatusResponse.version
。該狀態回應也應包含 "ok" 作為StatusResponse.healthz
,以及key_id
(遠端 KMS KEK ID) 作為StatusResponse.key_id
。Kubernetes 專案建議您使您的外掛程式與穩定的v2
KMS API 相容。Kubernetes 1.32 也支援v2beta1
API 用於 KMS;未來的 Kubernetes 版本可能會繼續支援該 Beta 版本。當一切正常時,API 伺服器大約每分鐘輪詢一次
Status
程序呼叫,而當外掛程式不正常時,則每 10 秒輪詢一次。外掛程式必須注意最佳化此呼叫,因為它將處於持續負載下。加密
EncryptRequest
程序呼叫提供純文字和 UID 以用於記錄目的。回應必須包含密文、使用的 KEK 的key_id
,以及選擇性的任何中繼資料,KMS 外掛程式需要這些中繼資料以協助未來的DecryptRequest
呼叫 (透過annotations
欄位)。外掛程式必須保證任何不同的純文字都會產生不同的回應(ciphertext, key_id, annotations)
。如果外掛程式傳回非空的
annotations
對應,則所有對應鍵都必須是完整網域名稱,例如example.com
。annotation
的範例用例是{"kms.example.io/remote-kms-auditid":"<遠端 KMS 使用的稽核 ID>"}
API 伺服器不會以高頻率執行
EncryptRequest
程序呼叫。外掛程式實作仍應力求將每個請求的延遲保持在 100 毫秒以下。解密
DecryptRequest
程序呼叫提供來自EncryptRequest
的(ciphertext, key_id, annotations)
和 UID 以用於記錄目的。如預期的,它是EncryptRequest
呼叫的反向操作。外掛程式必須驗證key_id
是它們理解的 ID 之一 - 它們絕不能嘗試解密資料,除非它們確定資料是先前由它們加密的。API 伺服器可能會在啟動時執行數千個
DecryptRequest
程序呼叫,以填滿其監看快取。因此,外掛程式實作必須盡可能快速地執行這些呼叫,並且應力求將每個請求的延遲保持在 10 毫秒以下。理解
key_id
和金鑰輪換key_id
是目前正在使用的遠端 KMS KEK 的公開、非秘密名稱。它可能會在 API 伺服器的常規操作期間記錄下來,因此絕不能包含任何私密資料。鼓勵外掛程式實作使用雜湊來避免洩露任何資料。KMS v2 指標會注意在透過/metrics
端點公開此值之前對其進行雜湊處理。API 伺服器認為從
Status
程序呼叫傳回的key_id
是權威性的。因此,此值的變更會向 API 伺服器發出訊號,表示遠端 KEK 已變更,並且使用舊 KEK 加密的資料在執行無操作寫入時應標記為過時 (如下所述)。如果EncryptRequest
程序呼叫傳回的key_id
與Status
不同,則回應將被丟棄,並且外掛程式將被視為不正常。因此,實作必須保證從Status
傳回的key_id
將與EncryptRequest
傳回的key_id
相同。此外,外掛程式必須確保key_id
是穩定的,並且不會在值之間來回跳動 (即在遠端 KEK 輪換期間)。外掛程式不得重複使用
key_id
,即使在先前使用的遠端 KEK 已恢復的情況下也是如此。例如,如果外掛程式正在使用key_id=A
,切換到key_id=B
,然後返回到key_id=A
- 外掛程式應報告一些衍生值,例如key_id=A_001
,或使用新值,例如key_id=C
,而不是報告key_id=A
。由於 API 伺服器大約每分鐘輪詢一次
Status
,因此key_id
輪換不是立即的。此外,API 伺服器將在最後一個有效狀態上維持約三分鐘。因此,如果使用者想要對儲存遷移採取被動方法 (即透過等待),他們必須排程在遠端 KEK 輪換後3 + N + M
分鐘進行遷移 (N
是外掛程式觀察到key_id
變更所需的時間,而M
是允許處理組態變更的所需緩衝區 - 建議最小M
為五分鐘)。請注意,執行 KEK 輪換不需要重新啟動 API 伺服器。注意
由於您無法控制使用 DEK 執行的寫入次數,Kubernetes 專案建議至少每 90 天輪換一次 KEK。協定:UNIX 網域 Socket (
unix
)此外掛程式實作為在 UNIX 網域 Socket 上接聽的 gRPC 伺服器。外掛程式部署應在檔案系統上建立檔案,以執行 gRPC unix 網域 Socket 連線。API 伺服器 (gRPC 用戶端) 配置了 KMS 供應商 (gRPC 伺服器) unix 網域 Socket 端點,以便與其通訊。抽象 Linux Socket 可以透過以
/@
開頭端點來使用,例如unix:///@foo
。使用此類型的 Socket 時必須謹慎,因為它們沒有 ACL 的概念 (與傳統的基於檔案的 Socket 不同)。但是,它們受 Linux 網路命名空間的約束,因此除非使用主機網路,否則僅能由相同 Pod 內的容器存取。
將 KMS 外掛程式與遠端 KMS 整合
KMS 外掛程式可以使用 KMS 支援的任何協定與遠端 KMS 通訊。所有組態資料,包括 KMS 外掛程式用於與遠端 KMS 通訊的驗證憑證,都由 KMS 外掛程式獨立儲存和管理。KMS 外掛程式可以使用解密前可能需要的中繼資料來編碼密文,然後再將其傳送到 KMS 進行解密 (KMS v2 透過提供專用的 annotations
欄位使此過程更容易)。
部署 KMS 外掛程式
確保 KMS 外掛程式與 Kubernetes API 伺服器在相同的主機上執行。
使用 KMS 供應商加密您的資料
若要加密資料
使用
kms
供應商的適當屬性建立新的EncryptionConfiguration
檔案,以加密諸如 Secrets 和 ConfigMaps 之類的資源。如果您想要加密在 CustomResourceDefinition 中定義的擴充 API,您的叢集必須執行 Kubernetes v1.26 或更新版本。在 kube-apiserver 上設定
--encryption-provider-config
旗標,以指向組態檔案的位置。--encryption-provider-config-automatic-reload
布林值引數決定是否應在磁碟內容變更時自動重新載入由--encryption-provider-config
設定的檔案。重新啟動您的 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-foo.sock
cachesize: 100
timeout: 3s
- kms:
name: myKmsPluginBar
endpoint: unix:///tmp/socketfile-bar.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-foo.sock
timeout: 3s
- kms:
apiVersion: v2
name: myKmsPluginBar
endpoint: unix:///tmp/socketfile-bar.sock
timeout: 3s
將 --encryption-provider-config-automatic-reload
設定為 true
會將所有健康檢查摺疊到單一健康檢查端點。只有在使用 KMS v1 供應商且未自動重新載入加密組態時,個別健康檢查才可用。
下表總結了每個 KMS 版本的健康檢查端點
KMS 組態 | 不自動重新載入 | 自動重新載入 |
---|---|---|
僅限 KMS v1 | 個別健康檢查 | 單一健康檢查 |
僅限 KMS v2 | 單一健康檢查 | 單一健康檢查 |
KMS v1 和 v2 皆有 | 個別健康檢查 | 單一健康檢查 |
無 KMS | 無 | 單一健康檢查 |
單一健康檢查
表示唯一的健康檢查端點是 /healthz/kms-providers
。
個別健康檢查
表示每個 KMS 外掛程式都有一個相關聯的健康檢查端點,該端點基於其在加密組態中的位置:/healthz/kms-provider-0
、/healthz/kms-provider-1
等。
這些健康檢查端點路徑是硬式編碼的,並且由伺服器產生/控制。個別健康檢查的索引對應於 KMS 加密組態的處理順序。
在執行確保所有密鑰皆已加密中定義的步驟之前,providers
清單應以 identity: {}
供應商結尾,以允許讀取未加密的資料。一旦所有資源都已加密,應移除 identity
供應商,以防止 API 伺服器接受未加密的資料。
有關 EncryptionConfiguration
格式的詳細資訊,請查看API 伺服器加密 API 參考。
驗證資料是否已加密
當靜態加密已正確配置時,資源會在寫入時加密。重新啟動 kube-apiserver
後,任何新建立或更新的 Secret 或在 EncryptionConfiguration
中配置的其他資源類型在儲存時都應加密。若要驗證,您可以使用 etcdctl
命令列程式來檢索您的密鑰資料的內容。
在
default
命名空間中建立一個名為secret1
的新密鑰kubectl create secret generic secret1 -n default --from-literal=mykey=mydata
使用
etcdctl
命令列,從 etcd 中讀取該密鑰ETCDCTL_API=3 etcdctl get /kubernetes.io/secrets/default/secret1 [...] | hexdump -C
其中
[...]
包含用於連接到 etcd 伺服器的其他引數。驗證儲存的密鑰是否以
k8s:enc:kms:v1:
(對於 KMS v1) 或以k8s:enc:kms:v2:
(對於 KMS v2) 作為前綴,這表示kms
供應商已加密產生的資料。驗證透過 API 檢索時密鑰是否已正確解密
kubectl describe secret secret1 -n default
Secret 應包含
mykey: mydata
確保所有密鑰皆已加密
當靜態加密已正確配置時,資源會在寫入時加密。因此,我們可以執行就地無操作更新,以確保資料已加密。
以下命令讀取所有密鑰,然後更新它們以套用伺服器端加密。如果由於寫入衝突而發生錯誤,請重試該命令。對於較大的叢集,您可能希望按命名空間細分密鑰或編寫更新腳本。
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
從本機加密供應商切換到 KMS 供應商
若要從本機加密供應商切換到 kms
供應商並重新加密所有密鑰
將
kms
供應商作為組態檔案中的第一個條目新增,如下列範例所示。apiVersion: apiserver.config.k8s.io/v1 kind: EncryptionConfiguration resources: - resources: - secrets providers: - kms: apiVersion: v2 name : myKmsPlugin endpoint: unix:///tmp/socketfile.sock - aescbc: keys: - name: key1 secret: <BASE 64 ENCODED SECRET>
重新啟動所有
kube-apiserver
程序。執行以下命令以強制使用
kms
供應商重新加密所有密鑰。kubectl get secrets --all-namespaces -o json | kubectl replace -f -