K8S實(shí)戰(zhàn)指南:結(jié)合ArgoCD使用SOPS管理 Kubernetes部署中的Secrets
在現(xiàn)代化的 Kubernetes GitOps 工作流中,如何安全、高效地管理敏感信息(如密碼、API 密鑰等)是一個(gè)核心挑戰(zhàn)。將明文 Secrets 存儲(chǔ)在 Git 倉(cāng)庫(kù)中是絕對(duì)不可取的,而傳統(tǒng)的 Secrets 管理方式又可能與自動(dòng)化的部署流程脫節(jié)。
本文將詳細(xì)介紹一種優(yōu)雅的解決方案:利用 SOPS (Secrets OPerationS) 對(duì) Helm Chart 中的 values 文件進(jìn)行加密,并結(jié)合一個(gè)自定義的 Init 容器,實(shí)現(xiàn)與 ArgoCD 的無縫集成,整個(gè)過程無需為 ArgoCD 安裝任何額外插件。
核心架構(gòu)概覽
該方案的核心思想是在應(yīng)用 Pod 啟動(dòng)前,通過一個(gè)臨時(shí)的 InitContainer動(dòng)態(tài)解密并創(chuàng)建 Kubernetes Secrets。
? SOPS 加密文件: 將包含敏感數(shù)據(jù)的 values.yaml文件(例如 secret-values.yaml)使用 SOPS 和 Age (或 PGP) 進(jìn)行加密。加密后的文件可以安全地提交到 Git 倉(cāng)庫(kù)。
? Helm Chart: 一個(gè)標(biāo)準(zhǔn)的 Helm Chart,但經(jīng)過特殊設(shè)計(jì),能夠接收加密文件和相關(guān)配置。
? 解密 Init 容器 (The "Magic Container"): 這是一個(gè)自定義的 Docker 鏡像,其中打包了 SOPS和 kubectl兩個(gè)關(guān)鍵工具以及一個(gè)解密腳本。它作為 InitContainer在主應(yīng)用容器之前運(yùn)行。
? 解密密鑰: 用于解密的私鑰(如 Age key)被預(yù)先存儲(chǔ)在目標(biāo)命名空間的一個(gè)標(biāo)準(zhǔn) Kubernetes Secret 中。
? RBAC 權(quán)限: Chart 中包含一個(gè) ServiceAccount、Role和 RoleBinding,授予 Init 容器足夠的權(quán)限(主要是創(chuàng)建和管理 Secret對(duì)象)來執(zhí)行解密和創(chuàng)建操作。
工作流程揭秘
1. 部署觸發(fā): 開發(fā)者通過 ArgoCD 發(fā)起部署。ArgoCD 從 Git 倉(cāng)庫(kù)拉取 Helm Chart 和相關(guān)的配置文件。
2. 傳遞加密文件: ArgoCD 使用 Helm 的 --set-file參數(shù),將 SOPS 加密后的文件內(nèi)容傳遞給 Helm Chart。這個(gè)加密內(nèi)容被臨時(shí)存儲(chǔ)在一個(gè)名為 <chart-name>-encrypted-secret的 Secret 中。
3. Init 容器啟動(dòng): 當(dāng)應(yīng)用的 Pod 開始創(chuàng)建時(shí),解密 InitContainer首先啟動(dòng)。
4. 解密與創(chuàng)建:
? InitContainer從 <chart-name>-age-keysSecret 中讀取解密私鑰。
? 它再讀取 <chart-name>-encrypted-secret中的加密數(shù)據(jù)。
? 利用內(nèi)置的 SOPS 工具和私鑰,在容器內(nèi)解密數(shù)據(jù)。
? 最后,使用內(nèi)置的 kubectl工具,將解密后的明文數(shù)據(jù)動(dòng)態(tài)創(chuàng)建一個(gè)新的、標(biāo)準(zhǔn)的 Kubernetes Secret,例如 <chart-name>-secret。
5. 主容器啟動(dòng): InitContainer成功退出后,主應(yīng)用容器啟動(dòng)。此時(shí),它可以像往常一樣掛載和使用剛剛由 Init 容器創(chuàng)建的 <chart-name>-secret。
實(shí)戰(zhàn)指南:具體步驟與代碼
以下我們將通過一個(gè)具體場(chǎng)景,演示如何從零開始配置和部署。
1. 前提準(zhǔn)備 (Prerequisites)
確保你已安裝并配置好以下工具:
? SOPS
? Helm
? kubectl
? Docker
? ArgoCD
2. 生成加密密鑰 (Age)
首先,我們使用 age生成一對(duì)公私鑰。私鑰將用于解密。
# 生成一個(gè)名為 age-key.txt 的密鑰文件
age-keygen -o age-key.txt
注意: age-key.txt包含了你的私鑰,絕對(duì)不能將其提交到 Git 倉(cāng)庫(kù)。請(qǐng)務(wù)必將其添加到 .gitignore文件中。
3. 準(zhǔn)備并加密敏感數(shù)據(jù)文件
創(chuàng)建一個(gè)用于存放敏感信息的文件,例如 secret-values.yaml:
# secret-values.yaml
data: "some secret you want to protect"
another_secret: "password123"
然后,使用 SOPS 和之前生成的公鑰(可以從 age-key.txt文件中查看,以 age1開頭)來加密此文件。
# 使用 -e (encrypt) 參數(shù)進(jìn)行加密
# --age 后面跟你的公鑰
sops --encrypt --age 'age1...' secret-values.yaml > secret-values.enc.yaml
現(xiàn)在你可以安全地將 secret-values.enc.yaml提交到 Git。
4. 在 Kubernetes 中創(chuàng)建解密密鑰 Secret
將 age-key.txt的全部?jī)?nèi)容存入一個(gè) Kubernetes Secret 中。這個(gè) Secret 需要和你的應(yīng)用部署在同一個(gè) namespace。
# age-key-secret.yaml
apiVersion:v1
kind:Secret
metadata:
# Secret 名稱需遵循 <chart-name>-age-keys 的格式
# 如果你的 chart 最終叫 myapp,這里就是 myapp-age-keys
name:myapp-age-keys
namespace:your-app-namespace# 替換成你的命名空間
type:Opaque
stringData:
# 鍵名必須是 age-key.txt
age-key.txt: |
# Created: 2024-08-09T10:00:00Z
# public key: age1...
AGE-SECRET-KEY-1... # 從 age-key.txt 文件粘貼全部?jī)?nèi)容
應(yīng)用它:kubectl apply -f age-key-secret.yaml
5. 配置 Helm Chart 和 ArgoCD
在你的 Helm Chart 的 values.yaml(或一個(gè)自定義的 custom-values.yaml) 文件中,配置 initContainer和其他參數(shù)。
# values.yaml
# 使用 nameOverride 來統(tǒng)一所有資源的名稱前綴
nameOverride:"myapp"
# 主應(yīng)用的配置...
image:
repository:nginx
tag:latest
# 解密 Init Container 的配置
initContainer:
image:mrupnikm/olm-chart-sops-decryption:latest# 使用作者提供的或自建的鏡像
k8s_args:"-n your-app-namespace"# 指定操作的命名空間
encrypted_secret:
# 如果使用 age,只需提供一個(gè)非空值即可
age:"x"
# 這個(gè)字段留空,ArgoCD 會(huì)通過 fileParameters 填充它
extraSecretFile: ""
在你的 ArgoCD Application argo.yaml中,你需要使用 helm.fileParameters來指定加密文件。
# argo.yaml
apiVersion:argoproj.io/v1alpha1
kind:Application
metadata:
name:my-web-app
namespace:argocd
spec:
project:default
source:
repoURL:'https://github.com/your/repo.git'# 你的 Git 倉(cāng)庫(kù)地址
targetRevision:HEAD
path:'path/to/your/helm-chart'# Helm Chart 所在的路徑
helm:
valueFiles:
-values.yaml
# 關(guān)鍵部分:使用 fileParameters 將加密文件內(nèi)容傳遞給 --set-file
fileParameters:
-name:extraSecretFile# 對(duì)應(yīng) values.yaml 中的 extraSecretFile
path:secret-values.enc.yaml# 加密文件在 Git 倉(cāng)庫(kù)中的路徑
destination:
server:'https://kubernetes.default.svc'
namespace:your-app-namespace# 應(yīng)用部署的目標(biāo)命名空間
syncPolicy:
automated:
prune:true
selfHeal: true
6. 部署應(yīng)用
最后,將 ArgoCD 應(yīng)用清單提交到集群:
argocd app create -f argo.yaml
ArgoCD 將會(huì)自動(dòng)同步應(yīng)用,執(zhí)行上述工作流,最終你的應(yīng)用將能成功掛載并使用解密后的 Secret。
總結(jié)與思考
本文介紹的方法巧妙地利用了 Kubernetes 的 InitContainer和 Helm 的 --set-file特性,構(gòu)建了一個(gè)無需 ArgoCD 插件即可自動(dòng)化處理 SOPS 加密文件的 GitOps 流程。
? 優(yōu)點(diǎn):
原生兼容: 無需為 ArgoCD 安裝或配置任何插件,降低了維護(hù)復(fù)雜性。
安全: 敏感信息以加密形式存儲(chǔ)在 Git 中,符合 GitOps 最佳實(shí)踐。
靈活: 可以通過修改 InitContainer中的腳本來適應(yīng)不同的 Secret 結(jié)構(gòu)或解密邏輯。
? 注意事項(xiàng):
部署完成后,手動(dòng)創(chuàng)建的解密密鑰 Secret (myapp-age-keys) 不受 ArgoCD 管理,如果項(xiàng)目清理,需要手動(dòng)刪除。
該方案引入了一個(gè)自定義的 InitContainer鏡像,需要確保該鏡像的來源可靠并進(jìn)行妥善維護(hù)。
總而言之,這并非管理 Kubernetes Secrets 的唯一方案(其他方案如 External Secrets Operator, HashiCorp Vault 等也十分優(yōu)秀),但它為特定場(chǎng)景——尤其是追求環(huán)境簡(jiǎn)潔、希望避免引入過多外部依賴的團(tuán)隊(duì)——提供了一個(gè)極具參考價(jià)值的、輕量級(jí)的實(shí)現(xiàn)范本。