GitOps 和 Kubernetes 中的 secret 管理
GitOps 是使用 Git 作為基礎(chǔ)設(shè)施和應(yīng)用程序配置的來源,利用 Git 工作流,實(shí)現(xiàn) Git 倉庫中描述配置的自動化。我們知道基礎(chǔ)設(shè)施配置和應(yīng)用程序配置經(jīng)常都需要訪問某種敏感資產(chǎn),也就是我們通常說的 Secrets(比如身份認(rèn)證 Token、私鑰等),才能正確運(yùn)行、訪問數(shù)據(jù)或以其他方式與第三方系統(tǒng)以安全的方式進(jìn)行通信。但是如果直接在 Git 中存儲 Secrets 數(shù)據(jù)顯然是非常不安全的行為,我們也不應(yīng)該這樣做,即使是有訪問權(quán)限控制的私有 Git 倉庫。
那么我們應(yīng)該如何才能解決 Secrets 數(shù)據(jù)的存儲問題呢?并為實(shí)施 GitOps 的用戶和客戶提供在不損害私密性的情況下為其應(yīng)用程序提供 Secrets 的機(jī)制呢?這 Kubernetes 生態(tài)系統(tǒng)中有一些方法和開源項(xiàng)目可以幫助我們來解決這些問題,我們將在本文介紹幾種比較流行的方案。
在 GitOps 中管理 Secrets 主要有兩種主要的架構(gòu)方法:
將加密后的 secret 數(shù)據(jù)存儲在 Git 倉庫中,通過自動化的一些工具將這些數(shù)據(jù)進(jìn)行解密變成 Kubernetes 的 Secrets 對象。 存儲在 Git 倉庫中的 secert 數(shù)據(jù)的引用,自動化工具可以根據(jù)這些引用檢索到實(shí)際的 secret 數(shù)據(jù),最后將獲取到的數(shù)據(jù)渲染為 Kubernetes Secrets 對象。
Git 中加密的 Secrets
目前有兩個比較流行的開源項(xiàng)目遵循在 Git 中存儲加密數(shù)據(jù)的方法:Bitnami 的 Sealed Secrets[1] 與 Mozilla 的 SOPS[2] 項(xiàng)目,使用這兩個項(xiàng)目需要注意以下事項(xiàng):
純文本私密數(shù)據(jù)由用戶處理加密后存儲在 Git 倉庫中。 通過查看 Git 中的提交日志,可以輕松追蹤加密數(shù)據(jù)的 Git 提交者的身份,此信息可用于發(fā)起社會工程攻擊。 如果加密密鑰被泄露,則可能很難追蹤使用泄露密鑰加密的所有數(shù)據(jù)并將其撤消,因?yàn)檫@些數(shù)據(jù)可能會散布在大量存儲庫中。
Sealed Secrets
Sealed Secrets 項(xiàng)目使用公鑰加密的方式來對私密數(shù)據(jù)進(jìn)行加密,使用起來非常方便。Sealed Secrets 由兩個主要部分組成:
一個 Kubernetes 控制器,它了解用于解密和加密數(shù)據(jù)的私鑰和公鑰,并負(fù)責(zé)對資源對象的調(diào)諧。 一個簡單的 CLI(kubeseal[3])工具提供給開發(fā)人員將數(shù)據(jù)提交到Git 倉庫之前對其進(jìn)行加密操作。
Sealed Secrets 引入了一個新的自定義資源 SealedSecret,控制器會 watch 這些 CRD 的變更,對 SealedSecret 資源中的加密數(shù)據(jù)進(jìn)行解密,并將結(jié)果保存在 Kubernetes Secret 對象中,用于解密的私鑰以 Kubernetes Secret 方式存儲在 etcd 中。

KubeSeal CLI 工具允許開發(fā)人員獲取普通的 Kubernetes Secret 資源并將其轉(zhuǎn)換為 SealedSecret 對象,可以從控制器自動獲取執(zhí)行加密所需的公鑰,否則,公鑰必須由用戶提供作為輸入。

Sealed Secrets 控制器支持私鑰的自動密鑰輪轉(zhuǎn),也支持以前使用過的密鑰過期,以執(zhí)行數(shù)據(jù)的重新加密。此外,在加密數(shù)據(jù)時,會使用一個隨機(jī)的 nonce 與加密數(shù)據(jù)一起進(jìn)行加密,這使得通過暴力破解非常難以實(shí)現(xiàn)。
這種方法雖然看起來非常方便,但是仍然會帶來初始 Secret 資源被意外放進(jìn) Git 倉庫的風(fēng)險(xiǎn),因?yàn)樵嫉?Secret 資源需要供開發(fā)者使用,不過也可以通過一些方式來降低這種風(fēng)險(xiǎn),比如在 Git 倉庫中添加一個 pre-commit 的 hooks,拒絕提交 Kubernetes Secrets 數(shù)據(jù)。
此外,如果在集群中的私鑰丟失(由于意外刪除或在災(zāi)難情況下),并且沒有備份,則必須使用新私鑰的公鑰重新加密所有加密數(shù)據(jù),然后提交到所有 Git 存儲庫。
控制器支持命名空間級別的多租戶,因?yàn)樗枰鞔_指定目標(biāo)命名空間,為命名空間 A 加密的 SealedSecret 在放置在命名空間 B 中時是無法解密的,反之亦然。但是這個約束是由控制器強(qiáng)制執(zhí)行的,而不是通過加密方式實(shí)現(xiàn)的。
隨著集群數(shù)量的增加,SealedSecrets 方法也不能很好地?cái)U(kuò)展。每個集群都會有自己的 SealedSecrets 控制器和私鑰,這意味著如果需要將同一個 Secret 部署到多個集群,則需要創(chuàng)建多個 SealedSecret 對象,增加了維護(hù)開銷和 GitOps 配置的復(fù)雜性。
最后,私鑰的安全性主要取決于集群的 RBAC 和 etcd 的保護(hù)程度,而私鑰的泄露則超出了集群本身的范圍。每一個與私密數(shù)據(jù)相關(guān)的 Git 倉庫也可能會泄露,考慮到 Git 的分布式特性,可能難以追蹤和撤銷。
Mozilla SOPS
SOPS(Secrets OPerationS),簡稱 sops,是 Mozilla 開發(fā)的一種專用的加解密工具,完全不受 Kubernetes Secrets 資源或 Kubernetes 的限制。支持多種輸入格式,包括 YAML、JSON、ENV、INI 和 BINARY 格式。
SOPS 還支持與一些常用的密鑰管理系統(tǒng) (KMS) 集成,例如 AWS KMS、GCP KMS、Azure Key Vault 和 Hashicorp 的 Vault。值得注意的是,這些密鑰管理系統(tǒng)實(shí)際上并不是用來存儲 Secrets 本身的,而是提供用于保護(hù)數(shù)據(jù)安全的加密密鑰,如果沒有這樣的系統(tǒng),則可以使用 PGP 密鑰對代替。這種方法的好處是給用戶提供了迭代需求的機(jī)會,同時讓開發(fā)者能夠快速采用。例如,PGP 可以在開發(fā)環(huán)境中使用,而在更高層次的環(huán)境中使用 KMS,而不需要更換任何底層工具。
但是由于 SOPS 是一個命令行工具,它在?Kubernetes 中的使用是比較有限的,雖然有一些插件,比如 helm-secrets[4] 可以在安裝到集群之前解密與 Helm Chart 一起存儲的 values.yaml 文件,但是在 GitOps 工具(比如 ArgoCD)中并不支持。
對于 Argo CD,用戶必須構(gòu)建包含 SOPS 的自定義容器鏡像,然后使用 Helm 擴(kuò)展(請參閱此處的方法[5])或 Kustomize 擴(kuò)展(請查看此處的方法[6])來處理。而 Flux 中對 SOPS 的支持更加成熟,SOPS 可以直接在 Flux 的清單中配置。

有一個 sops-secrets-operator[7] 工具嘗試將 SOPS 和 SealedSecrets 的概念結(jié)合起來,它使用 SealedSecrets 工作流程,但沒使用其特有的加密方案,而是使用 SOPS 進(jìn)行加密。
隨著團(tuán)隊(duì)和個人數(shù)量的增加,SOPS 方法無法很好地?cái)U(kuò)展,用戶必須執(zhí)行加密并管理 PGP 密鑰或?qū)ζ渌用軅鬏斚到y(tǒng)的身份進(jìn)行認(rèn)證,這是該方法的一個弱點(diǎn)。最后當(dāng)然用戶會成為安全鏈中較弱的環(huán)節(jié),比起直接使用 PGP 密鑰,與密鑰管理系統(tǒng)的整合更為可取,雖然這并沒有消除人為因素和直接與 Secret 交互的需求,但至少它消除了必須管理密鑰的負(fù)擔(dān)。
存儲在 Git 中的 Secret 引用
在這種方法中,我們會在 Git 中存儲一個清單,該清單表示對位于密鑰管理系統(tǒng)中的 Secret 的引用。然后,GitOps Operator 將該清單部署到 Kubernetes 集群,操作人員獲取數(shù)據(jù)并將其作為 Kubernetes Secret 對象應(yīng)用到集群。
有兩個主要項(xiàng)目實(shí)現(xiàn)了這種方式:ExternalSecrets[8] 和 Kubernetes Secret Store CSI Driver[9]。
這兩種方法都需要有一個密鑰管理系統(tǒng),這通常是一個企業(yè)級的系統(tǒng),用于管理整個企業(yè)的私密數(shù)據(jù),其功能不僅僅是分發(fā)加密密鑰。
ExternalSecrets
ExternalSecrets 項(xiàng)目最初由 GoDaddy 開發(fā),目的是在 Kubernetes 中安全使用外部 secret 管理系統(tǒng),如 HashiCorp 的 Vault、AWS Secrets Manager、Azure Key Vault、阿里巴巴 KMS 和 GCP Secret Manager 等。通過社區(qū)的發(fā)展,項(xiàng)目支持的密鑰管理系統(tǒng)也在不斷增加。
ExternalSecrets 是一個 Kubernetes 控制器,它可以將來自自定義資源 (ExternalSecrets) 的 Secret 應(yīng)用到集群中,其中包括對外部密鑰管理系統(tǒng)中密鑰的引用。自定義資源指定包含機(jī)密數(shù)據(jù)的后端,以及如何通過定義模板將其轉(zhuǎn)換為 Secret,該模板可以包含動態(tài)元素(以 lodash 格式),并可用于向最終的 Secret 資源添加標(biāo)簽或注解,或者在從后端存儲加載后對某些數(shù)據(jù)進(jìn)行修改。
ExternalSecrets 資源本身可以安全地存儲在 Git 中,因?yàn)樗鼈儾话魏螜C(jī)密信息。ExternalSecrets 本身不執(zhí)行任何加密操作,完全依賴于 Secret Store 后端的安全性。

該 Operator 對多租戶的支持也比較成熟了,可以采用不同的方法來確保不同的租戶彼此隔離,可以使用具有命名空間級別本地憑證的 SecretStore 資源來進(jìn)行管理,這樣每個租戶將使用不同的憑據(jù)來對密鑰管理系統(tǒng)進(jìn)行身份驗(yàn)證。
Kubernetes Secret Store CSI Driver
Secrets Store CSI 驅(qū)動程序也是一個旨在將 Secret 從外部存儲帶入 Kubernetes 集群的項(xiàng)目,支持的后端包括 HashiCorp Vault、Azure Key Vault 和 GCP Secret Manager,其他后端可以從外部開發(fā),并按照插件模式作為"供應(yīng)商"進(jìn)行整合。與 ExternalSecrets 項(xiàng)目相反,Secrets Store CSI 驅(qū)動程序不是作為控制器將數(shù)據(jù)協(xié)調(diào)到 Secret 資源中,而是使用一個單獨(dú)的卷,該卷被掛載到 Kubernetes pod 中,以包含加密數(shù)據(jù),對于那些不被 pod 直接消費(fèi)的加密數(shù)據(jù)場景,這種方法會變得有些復(fù)雜,甚至無法使用。
盡管 Secrets Store CSI 驅(qū)動程序確實(shí)提供了將內(nèi)容同步到 Kubernetes 中的 Secret 資源的可選功能,但由于作為 CSI 實(shí)現(xiàn)的性質(zhì),驅(qū)動程序及其創(chuàng)建的密鑰最終將綁定到工作負(fù)載。此外,另一個限制因素是,如果刪除了工作負(fù)載,則可選創(chuàng)建的 Secret 也將被刪除。
目前,對輪轉(zhuǎn)密鑰的支持有限,該功能仍處于 alpha 狀態(tài),并非所有提供商都支持。
與 ExternalSecrets 類似,Secrets Store CSI 驅(qū)動程序本身不執(zhí)行任何加密操作,而是利用后端系統(tǒng)加密和存儲私密數(shù)據(jù)。

用于連接后端系統(tǒng)的提供程序由外部第三方開發(fā)和維護(hù),雖然這種方法有利于新提供商的開發(fā),但它也對代碼的安全性、成熟度和兼容性提出了問題。但是,Secrets Store CSI 驅(qū)動程序提供了一些在開發(fā)此類提供程序時要遵循的規(guī)則和模式,風(fēng)險(xiǎn)似乎可以忽略不計(jì)。在采用提供商時,了解如何管理多租戶非常重要,一些提供商使用一個共享憑證來訪問所有租戶的私密數(shù)據(jù)。
總體而言,Secret Store CSI 驅(qū)動程序項(xiàng)目的目標(biāo)并不完全清楚。一方面,它似乎贊同我們不應(yīng)使用 Kubernetes Secrets 的想法,而只是將臨時的內(nèi)存卷掛載到包含從密鑰管理系統(tǒng)獲取的 secret 的 pod 上。但是,與此同時,包括使用 Kubernetes Secrets 的能力,最終通過 etcd 暴露私密信息。
總結(jié)
我們已經(jīng)看到了兩種使用 GitOps 管理 secret 的架構(gòu)方法:存儲在 Git 中的加密 secret 和在 Git 中存儲對 secret 的引用。后一種方法似乎更有希望,因?yàn)樗梢钥鐖F(tuán)隊(duì)/人員和集群的維度進(jìn)行擴(kuò)展,同時不影響安全性。前一種方法也許適合在沒有企業(yè)密鑰管理系統(tǒng)的情況下,或者是在 GitOps 初期的時候。這種方法的挑戰(zhàn)在于它可能無法適當(dāng)?shù)財(cái)U(kuò)展以適應(yīng)企業(yè)的規(guī)模。
如果你的目的是有一個可靠的方法來創(chuàng)建 Kubernetes Secrets,那么 External Secret 項(xiàng)目是一個很好的選擇。如果你的目的是將 secret 信息提供給 pod,則最好使用 Secret Store CSI Driver,因?yàn)樗恍枰诩褐袆?chuàng)建 Kubernetes Secret 信息。在后一種情況下,還可以選擇使用 sidecar 從密鑰管理系統(tǒng)獲取和刷新secret 信息。
避免創(chuàng)建 Kubernetes Secret 可以減輕一些保護(hù) etcd 的需求,但是在 Kubernetes 中幾乎不可避免,因?yàn)樘嗟暮诵暮透郊庸δ芤蕾囉?Kubernetes Secret。因此我們必須在可以反映為 Kubernetes Secrets 的 Secrets 和那些被 Pod 直接使用的 Secrets 之間取得平衡。
此外,如果我們的目標(biāo)是使用 GitOps 方式管理我們所有的 IT 配置,那么這也應(yīng)該適用于密鑰管理系統(tǒng)。Operators,例如 External Secrets,并沒有解決與管理 Secrets 相關(guān)的問題,而是將它們提升一個級別,從 Kubernetes 中的 Secret 創(chuàng)建,到 Key Management System 中的 Secret 創(chuàng)建。
為了打破這種循環(huán),密鑰管理系統(tǒng)必須能夠與需要驗(yàn)證憑據(jù)的端點(diǎn)協(xié)調(diào),動態(tài)生成密鑰,從而消除將外部生成的密鑰輸入密鑰管理系統(tǒng)的要求,這些密鑰通常是由用戶來處理的。例如,密鑰管理系統(tǒng)可以與數(shù)據(jù)庫協(xié)調(diào),動態(tài)創(chuàng)建范圍更小、壽命較短的數(shù)據(jù)庫憑證。該功能的實(shí)現(xiàn)可以在帶有 secret 引擎的 Hashicorp Vault[10] 或帶有動態(tài) secret 的 Akeyless[11] 中找到。
總之,要實(shí)現(xiàn)基于 GitOps 的端到端 secret 管理方法,我們需要一個支持動態(tài) secret 的密鑰管理系統(tǒng),并能夠通過 GitOps 進(jìn)行配置。
原文鏈接:https://cloud.redhat.com/blog/a-guide-to-secrets-management-with-gitops-and-kubernetes
參考資料
Sealed Secrets: https://github.com/bitnami-labs/sealed-secrets
[2]SOPS: https://github.com/mozilla/sops
[3]kubeseal: https://github.com/bitnami-labs/sealed-secrets
[4]helm-secrets: https://github.com/jkroepke/helm-secrets
[5]請參閱此處的方法: https://github.com/jkroepke/helm-secrets/wiki/ArgoCD-Integration
[6]請查看此處的方法: https://github.com/viaduct-ai/kustomize-sops
[7]sops-secrets-operator: https://github.com/isindir/sops-secrets-operator
[8]ExternalSecrets: https://github.com/external-secrets/external-secrets
[9]Kubernetes Secret Store CSI Driver: https://secrets-store-csi-driver.sigs.k8s.io/
[10]Hashicorp Vault: https://www.vaultproject.io/docs/secrets
[11]Akeyless: https://www.akeyless.io/
