使用 KMS 提供程式進行資料加密
本頁面展示瞭如何配置金鑰管理服務(KMS)提供程式和外掛以啟用 Secret 資料加密。在 Kubernetes 1.34 中,存在兩個版本的 KMS 靜態加密。如果可行,應使用 KMS v2,因為 KMS v1 已棄用(自 Kubernetes v1.28 起)並在預設情況下停用(自 Kubernetes v1.29 起)。KMS v2 提供了比 KMS v1 顯著更好的效能特性。
注意
此文件適用於 KMS v2 的通用實現(以及已棄用的 v1 實現)。如果您使用的任何控制平面元件版本低於 Kubernetes v1.29,請查閱與您的叢集正在執行的 Kubernetes 版本相對應的文件頁面。早期版本的 Kubernetes 具有可能與資訊安全相關的不同行為。準備工作
你需要有一個 Kubernetes 叢集,並且 kubectl 命令列工具已配置為與你的叢集通訊。建議在至少有兩個不作為控制平面主機的節點叢集上執行本教程。如果你還沒有叢集,可以使用 minikube 建立一個,或者使用這些 Kubernetes 演練場之一
所需的 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 [stable]
- 你的叢集必須使用 etcd v3 或更高版本。
KMS 加密和每個物件的加密金鑰
KMS 加密提供程式使用信封加密方案來加密 etcd 中的資料。資料使用資料加密金鑰 (DEK) 進行加密。DEK 使用儲存在遠端 KMS 中的金鑰加密金鑰 (KEK) 進行加密和管理。
如果你使用(已棄用的)KMS v1 實現,每次加密都會生成一個新的 DEK。
使用 KMS v2,**每次加密**都會生成一個新的 DEK:API 伺服器使用**金鑰派生函式**從秘密種子和一些隨機資料生成一次性資料加密金鑰。當 KEK 輪換時,種子也會輪換(詳見下面的“理解 key_id 和金鑰輪換”部分)。
KMS 提供程式使用 gRPC 透過 UNIX 域套接字與特定的 KMS 外掛進行通訊。KMS 外掛作為一個 gRPC 伺服器實現,並部署在與 Kubernetes 控制平面相同的宿主機上,負責與遠端 KMS 的所有通訊。
配置 KMS 提供程式
要在 API 伺服器上配置 KMS 提供程式,請在加密配置檔案中的 providers
陣列中包含一個 kms
型別的提供程式,並設定以下屬性:
KMS v1
apiVersion
: KMS 提供程式的 API 版本。將此值留空或將其設定為v1
。name
:KMS 外掛的顯示名稱。一旦設定,無法更改。endpoint
:gRPC 伺服器(KMS 外掛)的監聽地址。該端點是一個 UNIX 域套接字。cachesize
:要以明文形式快取的資料加密金鑰 (DEK) 的數量。快取後,無需再次呼叫 KMS 即可使用 DEK;而未快取的 DEK 則需要呼叫 KMS 才能解密。timeout
:在返回錯誤之前,kube-apiserver
應等待 kms-plugin 響應多長時間(預設為 3 秒)。
KMS v2
apiVersion
: KMS 提供程式的 API 版本。將其設定為v2
。name
:KMS 外掛的顯示名稱。一旦設定,無法更改。endpoint
:gRPC 伺服器(KMS 外掛)的監聽地址。該端點是一個 UNIX 域套接字。timeout
:在返回錯誤之前,kube-apiserver
應等待 kms-plugin 響應多長時間(預設為 3 秒)。
KMS v2 不支援 cachesize
屬性。所有資料加密金鑰 (DEK) 在伺服器透過呼叫 KMS 解密後都將以明文形式快取。一旦快取,DEK 就可以無限期地用於解密而無需呼叫 KMS。
請參閱 瞭解靜態加密配置。
實現 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 域套接字端點,以便與其通訊。可以透過以
/@
開頭(即unix:///@foo
)來使用抽象的 Linux 套接字。使用這種型別的套接字時必須小心,因為它們沒有 ACL 的概念(與傳統的檔案套接字不同)。但是,它們受 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.34 也支援 KMS 的v2beta1
API;未來的 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
是否是它們理解的——它們不得嘗試解密資料,除非它們確定資料是它們在早些時候加密的。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
,而是應該報告一些派生值,例如key_id=A_001
,或者使用一個新的值,例如key_id=C
。由於 API 伺服器大約每分鐘輪詢一次
Status
,因此key_id
輪換並非立即完成。此外,API 伺服器將在上次有效狀態上執行大約三分鐘。因此,如果使用者希望採用被動方式進行儲存遷移(即透過等待),他們必須安排在遠端 KEK 輪換後3 + N + M
分鐘進行遷移(N
是外掛觀察key_id
更改所需的時間,M
是允許處理配置更改的所需緩衝區——建議最小M
為五分鐘)。請注意,執行 KEK 輪換不需要重啟 API 伺服器。注意
因為你無法控制使用 DEK 進行的寫入次數,所以 Kubernetes 專案建議至少每 90 天輪換一次 KEK。協議:UNIX 域套接字(
unix
)該外掛實現為監聽 UNIX 域套接字的 gRPC 伺服器。外掛部署應該在檔案系統上建立一個檔案,以執行 gRPC UNIX 域套接字連線。API 伺服器(gRPC 客戶端)配置了 KMS 提供程式(gRPC 伺服器)的 UNIX 域套接字端點,以便與其通訊。可以透過以
/@
開頭(即unix:///@foo
)來使用抽象的 Linux 套接字。使用這種型別的套接字時必須小心,因為它們沒有 ACL 的概念(與傳統的檔案套接字不同)。但是,它們受 Linux 網路名稱空間的限制,因此除非使用主機網路,否則只能在同一 Pod 中的容器訪問。
將 KMS 外掛與遠端 KMS 整合
KMS 外掛可以使用 KMS 支援的任何協議與遠端 KMS 通訊。所有配置資料,包括 KMS 外掛用於與遠端 KMS 通訊的身份驗證憑據,都由 KMS 外掛獨立儲存和管理。KMS 外掛可以在將密文傳送到 KMS 進行解密之前,用可能需要的附加元資料對密文進行編碼(KMS v2 透過提供專用的 annotations
欄位使此過程更容易)。
部署 KMS 外掛
確保 KMS 外掛在與 Kubernetes API 伺服器相同的宿主機上執行。
使用 KMS 提供程式加密資料
要加密資料
建立一個新的
EncryptionConfiguration
檔案,使用kms
提供程式的相應屬性來加密 Secret 和 ConfigMap 等資源。如果你想加密在 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 | 無 | 單個健康檢查 |
Single Healthcheck
表示唯一的健康檢查端點是 /healthz/kms-providers
。
Individual Healthchecks
意味著每個 KMS 外掛都有一個相關的健康檢查端點,該端點基於其在加密配置中的位置:/healthz/kms-provider-0
、/healthz/kms-provider-1
等。
這些健康檢查端點路徑是硬編碼的,並由伺服器生成/控制。單獨健康檢查的索引對應於 KMS 加密配置的處理順序。
在執行 確保所有 Secret 都已加密 中定義的步驟之前,providers
列表應以 identity: {}
提供程式結束,以允許讀取未加密的資料。一旦所有資源都已加密,應刪除 identity
提供程式,以防止 API 伺服器接受未加密的資料。
有關 EncryptionConfiguration
格式的詳細資訊,請查閱 API 伺服器加密 API 參考。
驗證資料是否已加密
當靜態加密配置正確時,資源在寫入時被加密。重新啟動 kube-apiserver
後,任何新建立或更新的 Secret 或 EncryptionConfiguration
中配置的其他資源型別都應在儲存時加密。要驗證,您可以使用 etcdctl
命令列程式檢索 Secret 資料的內容。
在
default
名稱空間中建立一個名為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 伺服器的其他引數。驗證儲存的 Secret 是否以
k8s:enc:kms:v1:
(KMS v1) 或k8s:enc:kms:v2:
(KMS v2) 為字首,這表示kms
提供程式已加密生成的資料。驗證 Secret 在透過 API 檢索時是否已正確解密
kubectl describe secret secret1 -n default
Secret 應包含
mykey: mydata
確保所有 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: apiVersion: v2 name : myKmsPlugin endpoint: unix:///tmp/socketfile.sock - aescbc: keys: - name: key1 secret: <BASE 64 ENCODED SECRET>
重啟所有
kube-apiserver
程序。執行以下命令,強制所有 Secret 使用
kms
提供程式重新加密。kubectl get secrets --all-namespaces -o json | kubectl replace -f -