SOPS 讓 Kubernetes Secret 的值以加密形式儲存在 Git,再由 GitOps controller 在同步時解密並套用 manifest。它適合小型叢集、機密資料不多而不值得維運 Vault 的環境、建立 Vault/External Secrets 前所需的 bootstrap 機密值,以及希望與 manifest 一併審閱版本的設定。
安全模型
Git 中可見的是 metadata、Secret key 與加密後的值;私鑰不應進入 Git。只有持有解密金鑰的人或 controller 才能還原內容,因此仍需限制 repository 存取、保護 GitOps controller、輪替金鑰,並避免將解密結果輸出至 CI 或 controller 日誌。
選擇 age 與設定 .sops.yaml
在本機 GitOps 環境中,我使用 age,因為金鑰格式簡單、部署負擔也低。將 recipient public key 寫入 .sops.yaml,僅比對 apps/*/secrets/ 下的檔案,並只加密 data 與 stringData:
creation_rules:
- path_regex: apps/.*/secrets/.*\.ya?ml$
encrypted_regex: '^(data|stringData)$'
age: age1examplepublicrecipient
將私鑰以 Kubernetes Secret 安裝在 GitOps controller 的 namespace,再按照 Flux 或 Argo CD 的方式掛載至負責解密的元件。私鑰必須透過安全的管理管道交付,不能出現在明文 manifest 中。
建立、套用與輪替
用 sops 建立或更新 Secret,確認 Git diff 只顯示加密值。Flux 原生支援 SOPS 解密;Argo CD 則需受控的 plugin 或 repository-server 設計。使用 Argo CD 時要隔離 repo-server、保護 Redis、固定並由平台團隊維護 plugin image、限制 RBAC,並禁止把解密 manifest 印到 log。
輪替機密值時,依序解密、更新、重新加密、提交,再驗證同步結果。輪替 age 金鑰時,則先新增 recipient,讓檔案可同時使用新舊金鑰解密;確認 controller 已改用新私鑰後,移除舊 recipient、再次加密,最後刪除舊私鑰。
Repository 防護與 Vault 的選擇
在 CI 中檢查不該被追蹤的檔名、掃描明文內容,並以 .gitignore 排除本機金鑰檔。SOPS 適合少量且需要版本控管的設定;Vault 則更適合大量動態機密資料、細緻權限、存取稽核與集中輪替。兩者也可以並用:SOPS 保存 bootstrap 值,Vault 則供應日常應用程式所需的機密資料。
