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