GitOpsにおける「マットの下の鍵」問題
GitOpsは、Kubernetesへのデプロイ方法を根本から変えました。ArgoCDやFluxのいずれを使用する場合でも、クラスターの状態をGitリポジトリと同期させるのは魔法のように感じられます。しかし、Secret(機密情報)の扱いは依然として大きな課題です。標準のKubernetes Secretは単にBase64でエンコードされているだけで、暗号化はされていません。これらをGitにコミットすることは、家の鍵を玄関マットの下に隠して誰も見ないことを祈るのと同じくらい、セキュリティ上のリスクがあります。
私は、HashiCorp Vaultのような重厚すぎるソリューションと、危険な手動の「kubectl apply」メソッドの中間に位置する「ちょうど良い」ソリューションを数ヶ月間探し続けました。Bitnami Sealed Secretsを本番環境で8ヶ月間運用し、12のネームスペースにわたる150以上のSecretを管理した結果、これがほとんどのエンジニアリングチームにとって最適なバランスであると確信しています。
このアプローチにより、機密データをコードと全く同じように扱うことができます。つまり、バージョン管理、監査、そして自動デプロイが可能になります。
仕組み:暗号化のプロセス
Sealed Secretsは、Git内でのSecret管理のジレンマを解決するために公開鍵暗号(非対称暗号)を使用します。システムは主に2つのコンポーネントで構成されています。
- コントローラー (Controller): クラスター内で動作するPodで、秘密鍵を保持します。暗号化されたデータを元のSecretに戻せる唯一の存在です。
- CLI (kubeseal): ローカルマシンで使用するツールで、コントローラーの公開鍵を使用してデータを暗号化します。
その結果、SealedSecretというカスタムリソースが生成されます。このファイルは、パブリックなGitHubリポジトリに安全にコミットできます。悪意のあるユーザーがリポジトリにアクセスしたとしても、クラスターのメモリ内に保存されている秘密鍵がなければ、値を復号することはできません。
ステップ1:デプロイとセットアップ
インストールにはHelmを使用するのが最も簡単です。コントローラーを標準のアプリケーションワークロードから分離するため、kube-systemネームスペースへのデプロイを推奨します。
# Bitnamiリポジトリを追加
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
# コントローラーをインストール
helm install sealed-secrets-controller sealed-secrets/sealed-secrets -n kube-system
ローカル環境用のkubesealバイナリを取得します。Linuxの場合、このワンライナーでGitHubから最新バージョンを直接取得できます。
KUBESEAL_VERSION=$(curl -s https://api.github.com/repos/bitnami-labs/sealed-secrets/releases/latest | grep tag_name | cut -d '"' -f 4 | sed 's/v//')
wget "https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION}/kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz"
tar -xvzf kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz kubeseal
sudo install -m 755 kubeseal /usr/local/bin/kubeseal
ステップ2:実際のワークフロー
プロセスは非常にシンプルです。一時的なローカルSecretを作成し、それを「シール(暗号化)」して、元のファイルを削除します。データベースの認証情報の例を見てみましょう。
YAMLの生成
まず、クラスターに適用せずに標準のSecretを作成します。--dry-run=clientを使用して、出力をファイルに書き出します。
kubectl create secret generic db-credentials \
--from-literal=password='super-secret-password' \
--dry-run=client -o yaml > db-secret.yaml
データのシール(暗号化)
次に暗号化を行います。kubesealツールはクラスターと通信して公開鍵を取得し、SecretをSealedSecretに変換します。
kubeseal < db-secret.yaml --format yaml > db-sealed-secret.yaml
ここで、db-secret.yamlを直ちに削除してください。残ったdb-sealed-secret.yamlファイルが、GitOpsパイプラインにおける「信頼できる唯一の情報源(Source of Truth)」となります。
ステップ3:重要な詳細:スコープの理解
私が初期段階で躓いたニュアンスの一つが「スコープ(Scope)」です。Sealed Secretsには3つのセキュリティ境界が用意されています。
- strict(デフォルト): Secretが特定の名前とネームスペースに紐付けられます。移動させると復号できません。
- namespace-wide: Secretの名前は変更可能ですが、同じネームスペース内にある必要があります。
- cluster-wide: クラスター内のどこでも復号可能です。
本番環境ではstrictを使用してください。これにより、開発者が誤って(あるいは意図的に)開発用ネームスペースで本番用データベースのパスワードを復号してしまうのを防ぐことができます。
検証とトラブルシューティング
クラスターにファイルを適用した後、コントローラーがバックグラウンドで動作し、標準のSecretを作成します。同期状態は次のコマンドで確認できます。
kubectl get sealedsecret db-credentials
# 実行結果を確認: Condition: Synced
トラブルシューティングは、通常コントローラーのログを確認するだけで済みます。Secretが表示されない場合、kube-system内のログを見れば、スコープの不一致や復号エラーが発生しているかどうかがわかります。
kubectl logs -n kube-system -l app.kubernetes.io/name=sealed-secrets
災害復旧:「鍵を失くさない」というルール
秘密鍵は暗号化の要です。クラスターが停止し、マスターキーのバックアップがない場合、Gitリポジトリは役に立たない暗号化ファイルの墓場となります。私は常に、マスターキーのコピーを安全な社内用Vaultに保管しています。
kubectl get secret -n kube-system -l sealedsecrets.bitnami.com/sealed-secrets-key -o yaml > master-key-backup.yaml
最終的な評価
Sealed Secretsへの移行により、「誰が本番環境のSecretファイルを持っているか?」というSlackでの不毛なやり取りがなくなり、チームの大きなボトルネックが解消されました。すべての変更が監査可能で可視化されるプルリクエストのワークフローに完璧にフィットします。大規模なエンタープライズ企業にはHashiCorp Vaultの機能が必要かもしれませんが、成長中の開発チームにとって、Sealed Secretsはセキュリティとスピードの最高のバランスを提供してくれます。

