SOPSを用いたGitOpsレポジトリ上のsecretの暗号化
GitOpsではクラスタのリソースは全てファイルとしてレポジトリに載ってしまっています。SOPSを用いることで内容を暗号化しつつfluxcdにGitOpsを回してもらえますので、今回はそのセットアップについて書きます。
以下がfluxcd公式ドキュメントのリンクです。こちらではfluxcdでbootstrapしたレポジトリとは別のリポジトリをSOPS用に設けていますが、今回は同一レポジトリでセットアップします。そして最近ポストしているfluxcdでのGitOpsセットアップ記事の流れで、今回の記事でもレポジトリは"gitops/homelab-v3"、クラスタは"lab-hlv3"となっています。
https://fluxcd.io/flux/guides/mozilla-sops/
KubernetesクラスタのGitOps用レポジトリの紹介
流れ
- gpgとsopsのインストール
- GPGキーペアの生成
- フィンガープリントの確認
- GPGキーペアをクラスタのflux-system namespace上にsecretとしてエクスポート
- GPG公開鍵をGitOpsレポジトリにアップロード
- ホストからキーペアを削除
- 暗号化を実施するホストでGPG公開鍵をインポート
- SOPSコンフィグファイルの用意
- SOPS用のflux kustomizationを追加
- デモ
gpgとsopsのインストール
gpg
コマンドはどのディストロでもインストール済みかと思いますが、なければパッケージマネジャなどでインストールします。
SOPSのインストールについては次の通りです。
https://github.com/getsops/sops
# see the release page for the instructions
curl -LO https://github.com/getsops/sops/releases/download/v3.9.4/sops-v3.9.4.linux.amd64
# move the downloaded sops binary to wherever
mv sops-v3.9.4.linux.amd64 ~/.local/bin/sops
chmod u+x ~/.local/bin/sops
sops --version
GPGキーペアの生成
キーの名前とコメントを指定してGPGキーペアを生成します。
export KEY_NAME="lab-hlv3.lab.example.net"
export KEY_COMMENT="flux secrets"
gpg --batch --full-generate-key <<EOF
%no-protection
Key-Type: 1
Key-Length: 4096
Subkey-Type: 1
Subkey-Length: 4096
Expire-Date: 0
Name-Comment: ${KEY_COMMENT}
Name-Real: ${KEY_NAME}
EOF
フィンガープリントの確認
生成された公開鍵のフィンガープリントを確認します。
gpg --list-secret-keys "${KEY_NAME}"
GPGキーペアをクラスタのflux-system namespace上にsecretとしてエクスポート
フィンガープリントは"sec:"とある行の2行目です。以下はドキュメントからの例で、確認できたフィンガープリントの文字列をKEY_FPとしてエクスポートしましょう。
sec rsa4096 2020-09-06 [SC]
1F3D1CED2F865F5E59CA564553241F147E7C5FA4
export KEY_FP=1F3D1CED2F865F5E59CA564553241F147E7C5FA4
クラスタ上にsecretとしてキーペアをエクスポートしましょう。
# create gpg keypair secret on flux-system
gpg --export-secret-keys --armor "${KEY_FP}" |
kubectl create secret generic sops-gpg \
--namespace=flux-system \
--from-file=sops.asc=/dev/stdin
# confirm
kubectl get secret sops-gpg -n flux-system
GPG公開鍵をGitOpsレポジトリにアップロード
のちにどのホストでも暗号化に必要な公開鍵が取得できるよう、GitOpsレポジトリにアップロードしておきましょう。
# on gitops repo
mkdir -p sops/lab-hlv3
gpg --export --armor "${KEY_FP}" > sops/lab-hlv3/.sops.pub.asc
ホストからキーペアを削除
キーを生成したホストからはキーペアを削除しておきましょう。
gpg --delete-secret-keys "${KEY_FP}"
暗号化を実施するホストでGPG公開鍵をインポート
SOPSで暗号化を実施するホストでは、必要な公開鍵をインポートしておきます。
# on gitops repo
gpg --import sops/lab-hlv3/.sops.pub.asc
SOPSコンフィグファイルの用意
SOPSでの暗号化実行用のコンフィグファイルをGitOpsレポジトリ上に用意します。
フィンガープリントが必要ですので、もし再確認する場合は公開鍵をインポートしているホストでgpg --list-keys
を実行することで確認できます。
# on gitops repo
cat <<EOF > sops/lab-hlv3/.sops.yaml
creation_rules:
- path_regex: .*.yaml
encrypted_regex: ^(data|stringData)$
pgp: ${KEY_FP}
EOF
SOPS用のflux kustomizationを追加
これはGitOpsセットアップ時の記事でインフラ用、アプリ用と別々に設けたflux kustomizationとほぼ同じものを追加します。
sourceRef
としては元々bootstrapした本GitOpsレポジトリを指定し、path
には先の手順で用意したSOPS用ディレクトリを指定しています。
一番の違いは.spec.decryption
です。SOPSをプロバイダとして指定しており、用いるsecretは先の手順でGPGキーペアをエクスポートした"sops-gpg"を指定しています。
cat <<'EOF' > clusters/lab-hlv3/sops.yaml
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: sops
namespace: flux-system
spec:
decryption:
provider: sops
secretRef:
name: sops-gpg
interval: 1m0s
path: ./sops/lab-hlv3
prune: true
sourceRef:
kind: GitRepository
name: flux-system
EOF
commit/pushされたものがfluxによって同期されるのを待ち、flux kustomizationのリストに"sops"が追加されていることを確認します。
$ flux get ks
NAME REVISION SUSPENDED READY MESSAGE
apps main@sha1:1b76dba2 False False dependency 'flux-system/infra-configs' revision is not up to date
flux-system main@sha1:de9c4295 False True Applied revision: main@sha1:de9c4295
infra-configs main@sha1:1b76dba2 False False dependency 'flux-system/infra-controllers' is not ready
infra-controllers main@sha1:de9c4295 False True Applied revision: main@sha1:de9c4295
sops main@sha1:de9c4295 False True Applied revision: main@sha1:de9c4295
暗号化のデモ
デモとしてダミーのsecretをplaceholder namespace上に作ってみます。
# on gitops repo
mkdir sops/lab-hlv3/placeholder
# create a secret yaml file
kubectl -n placeholder create secret generic dummy \
--from-literal=user=username \
--from-literal=password=passwordforusername \
--dry-run=client \
-o yaml > sops/lab-hlv3/placeholder/dummy.yaml
# encrypt
# chdir where sops configuration file is placed at
cd sops/lab-hlv3
sops --encrypt --in-place placeholder/dummy.yaml
# commit and push
sops --encrypt
コマンド前後のdummy.yamlファイルの中身です。
# before
$ cat sops/lab-hlv3/placeholder/dummy.yaml
apiVersion: v1
data:
password: cGFzc3dvcmRmb3J1c2VybmFtZQ==
user: dXNlcm5hbWU=
kind: Secret
metadata:
creationTimestamp: null
name: basic-auth
namespace: default
# after the encryption
$ cat sops/lab-hlv3/placeholder/dummy.yaml
apiVersion: v1
data:
password: ENC[AES256_GCM,data:DWjyV2ooXZc2z+jBvzWfKrzz2oU8sSa/evt3rQ==,iv:m243VqNqgMgITXuygRuDIKDPDb8g1dbXT6+nkDL960E=,tag:ThXtGSft4AWcWvZrkFjaQA==,type:str]
user: ENC[AES256_GCM,data:yFbRgz3holhq1jZh,iv:T2KWIc84ntnbWF69AUsqlCNVh0iI8o0jdPy7Lwmy6oc=,tag:mi+n1lTCYiDpYIrdbC4Ntw==,type:str]
kind: Secret
metadata:
creationTimestamp: null
name: basic-auth
namespace: default
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age: []
lastmodified: "2025-03-27T08:14:27Z"
mac: ENC[AES256_GCM,data:npyzMmL71epRGsqTIDAyCMpiIYYR+qJPpC05Fd8XRxnaPGKbZ3z9rzRJIb/f0ei0J7qyi5f2SKmkYvGpN2DIdiV10Gake59LohUG909XTUuTdPS1Cr85/ygUN20AY3PaUJ82wTpIO1eM8rJfW7SJSrvlcXo81h/QTQtMMyF1w8U=,iv:d98j1wrTz6PVQrHDC5quS9ldGrRrIuYAqwu8KdAcp8Q=,tag:vVg3ceWRgzdtMPbPBaPE+Q==,type:str]
pgp:
- created_at: "2025-03-27T08:14:27Z"
enc: |-
-----BEGIN PGP MESSAGE-----
hQIMA1ZiUHDvVyFzARAAlb55Oj8tQw+/UoqQq+cPFOxn4Fkor3ffeFQgHyMUXiuc
sTSM1plrY6VqHBSszKr7qHdj8FKw/KdgSxlNvo8C8bYtgoo0SICLnUSnLwdP7co6
bEWJ9b+gFeqUgXbs+ugJbwQPy1QgyRjU9hS2YILg5WyOO4Yxs0K6ov6Aq8QEQxnM
6WLKr+7rAlsZvZiXz4ylQPw5motoHD5NJnc/S20nWKQXoNPQglH8nF2NK9ormQ1w
XD2cqzkF20CEHiMKe/9pdyuz9U3n53giu8y1UtCmEO6dZvuWLa8Vw/Pme+8RQ7s4
O4P55VQvCSCwrWByl9ZRDRUhOzz+gLAz9zSAyksokN49A43B7XUprmVZsUOokGa/
hZoDRnq5PBlWzVIUJ6iQgXrce/JlWKPXpTr/pGOp5LhJwn4PhBeQNrOKMzO/Il1W
/BfgwCGwdpdvNGBxNCjkHa2zCeqdjfgSQfdjggV5VxpPJbSd/BTOyHwlOj4rLdAS
4rghIGB+2fRxgzj7MWpZdYBqfz+UE1jdFQg9zcJGwuLKvnUZNmhIuuGFj7odWGI4
cHwyek9iW5x9WtPCnDV3XsLHevpuUqrm/9TmM/Vv13PvuL734bkw4Wn4m6F0FM3o
54s6SKQobGuTgJwtVhMYCbqvykGiq9b9CBtIX+ot/dpuB25OExmOPOfM2+GM8HfU
aAEJAhDeX2/rkXQMr12PWjj0rsTJ41p+6aZgcsiY0QjgRDH5z0wfA1jtK1suEz/z
w074qr7/2JEqpjJSrjwxum6MvG2ve04Rh5sWmV1f8tgyOKj6fjyj2xXGaCaSUA8c
yZznilTPD8zc
=7/av
-----END PGP MESSAGE-----
fp: FF44D91FBED62563A999B5E0A9697E97CDDD9973
encrypted_regex: ^(data|stringData)$
version: 3.9.4
sops flux ksの同期が完了すると、kubectl get secret dummy -n placeholder -o yaml
で複合化された状態のsecretの内容がクラスタ上にできていることが確認できます。
おわりに
以上です!
おうちラボのkubernetesクラスタ用のローカルでホストしているレポジトリですが、せっかくなので暗号化をセットアップしました。
Discussion