背景と必要性:GitOpsにおけるSecret管理のジレンマを解決する
GitOpsでは、Gitリポジトリをインフラの「信頼できる唯一の情報源(Single Source of Truth)」として扱います。ArgoCDやFluxなどの継続的デリバリー(CD)エンジンは、mainブランチにプッシュされたマニフェストとクラスターの状態を常に同期(リコンサイル)します。しかし、標準的なKubernetesのSecretマニフェストには重大な脆弱性があります。Base64エンコードは単なる難読化にすぎず、暗号化ではありません。未暗号化のSecretマニフェストをGitにプッシュすると、リポジトリをクローンできる人なら誰にでも本番データベースのパスワード、StripeのAPIキー、秘密TLS証明書などが漏洩してしまうリスクがあります。
サイドカーインジェクターを備えたHashiCorp VaultやExternal Secrets Operatorのような複雑なアーキテクチャは、大規模プラットフォームでは有効ですが、運用のオーバーヘッドが極めて大きくなります。Bitnami Sealed Secretsは、エレガントな公開鍵暗号(非対称暗号)モデルによってこの課題を解決します。
開発者は、クラスターコントローラーの4096ビットRSA公開鍵を使用して、ローカル環境で通常のKubernetes Secretを暗号化します。生成されるカスタムリソースであるSealedSecretは、パブリックまたはプライベートなGitHubリポジトリへコミットしても完全に安全です。クラスター内で稼働するコントローラーだけが秘密鍵を保持しており、これを復号(Unseal)して有効なKubernetes Secretへと復元できます。
本番のCI/CDパイプラインにおいて、このパターンを採用すれば、暗号化されたSecretをアプリケーションのデプロイマニフェストと同じ場所で管理できます。ロールバックのアトミック性(不可分性)も保たれ、リスクの高い帯域外(手動や個別スクリプト)でのSecret作成が不要になり、環境間の設定ドリフトを防ぐことができます。
インストール:コントローラーとクライアントの導入
Sealed Secretsは、クラスター側のオペレーターと、開発環境向けのCLIツール(kubeseal)という2つの軽量コンポーネントで構成されています。
1. コントローラーのデプロイ
初回起動時、コントローラーは既存の鍵ペアの有無を確認します。存在しない場合は新しい4096ビットRSA鍵ペアを自動生成し、リリース先ネームスペース内に通常のKubernetes Secretとして保存します。Helmを使用して、コントローラーをkube-systemなどのシステムレベルのネームスペースにデプロイします。
# Sealed SecretsのHelmリポジトリを追加
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm repo update
# コントローラーをkube-systemにインストール
helm install sealed-secrets-controller sealed-secrets/sealed-secrets \
--namespace kube-system \
--set-string ingress.enabled=false
Podが安定するまで約10秒待ち、コントローラーによって初期鍵ペアが生成されたことを確認します。
kubectl get pods -n kube-system -l app.kubernetes.io/name=sealed-secrets
kubectl logs -n kube-system -l app.kubernetes.io/name=sealed-secrets | grep -i "key"
2. kubeseal CLIのインストール
エンジニアはローカル環境やGitHub Actionsのランナー上でkubesealバイナリを実行し、稼働中のクラスターAPIへ直接アクセスすることなくペイロードを暗号化できます。
# Linux(AMD64)にkubesealをインストール
KUBESEAL_VERSION="0.28.0"
curl -L "https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION}/kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz" | tar -xvzf -
sudo install -m 755 kubeseal /usr/local/bin/kubeseal
# バイナリが利用可能か確認
kubeseal --version
プロのTips:設定ファイルを暗号化するためだけに、開発者に本番クラスターへのアクセス権を付与すべきではありません。公開証明書をあらかじめ一度取得してリポジトリにコミットしておくか、CIランナーのイメージに組み込んでおきましょう。
kubeseal --fetch-cert \
--controller-name=sealed-secrets-controller \
--controller-namespace=kube-system > pub-sealed-secrets.pem
設定:Secretの暗号化とスコープ設定
復号エラーの多くはスコープ設定のミスが原因です。Bitnami Sealed Secretsには、Secretをどの範囲で展開できるかを制御する3つのセキュリティスコープが用意されています。
- strict(デフォルト): Secret名と対象ネームスペースが完全に一致する場合のみ復号を許可します。Secret名を変更したり、マニフェストを別のネームスペースへ移動したりすると、即座に復号できなくなります。
- namespace-wide: Secret名の変更は許可されますが、指定されたネームスペース内に限定されます。
- cluster-wide: クラスター内のあらゆるネームスペースで、任意の名前として復号・適用できます。ワイルドカードSSL証明書などの共有設定に限定して、慎重に使用してください。
ステップ形式で学ぶ暗号化の実践例
まず、ローカルのクライアントサイドドライラン(--dry-run=client)を使って標準のSecretマニフェストを生成します。この生の未暗号化マニフェストをクラスターに適用してはいけません。
# APIサーバーにアクセスせずローカルで未暗号化マニフェストを生成
kubectl create secret generic db-credentials \
--from-literal=DB_USER=postgres \
--from-literal=DB_PASSWORD="4f8a9e2b!C7x#9Lq" \
--namespace=backend \
--dry-run=client -o yaml > raw-secret.yaml
次に、保存した公開証明書を使用して、ファイルをSealedSecretへと暗号化します。ここでは明示的に--scope=strictを指定します。
kubeseal --format=yaml \
--cert=pub-sealed-secrets.pem \
--scope=strict < raw-secret.yaml > sealed-db-credentials.yaml
# 未暗号化ファイルを即座に上書き・削除
shred -u raw-secret.yaml
出力されたsealed-db-credentials.yamlには、RSAエンベロープでカプセル化されたAES-GCM暗号文が含まれます。
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-credentials
namespace: backend
spec:
encryptedData:
DB_PASSWORD: AgB+x0aB8...[TRUNCATED_CIPHERTEXT]...
DB_USER: AgCv19Pz...[TRUNCATED_CIPHERTEXT]...
template:
metadata:
name: db-credentials
namespace: backend
既存のSealedSecretの更新
特定のパスワードだけをローテーションしたい場合でも、マニフェスト全体を一から再作成する必要はありません。--merge-intoを使用すれば、個々のキーをその場で更新できます。
kubectl create secret generic db-credentials \
--from-literal=DB_PASSWORD="UpdatedPass_2026_xYz" \
--namespace=backend \
--dry-run=client -o yaml | \
kubeseal --format=yaml --merge-into sealed-db-credentials.yaml --cert=pub-sealed-secrets.pem
検証とモニタリング:復号確認とローテーション
GitOpsツールによってマニフェストがクラスターへ適用されたら、コントローラーがペイロードを正しく復号し、下流のSecretオブジェクトを生成したか確認します。
1. コントローラーの動作確認
カスタムリソースの状態を確認します。
kubectl get sealedsecret db-credentials -n backend -o yaml
status.conditionsブロックを確認してください。正常に復号(Unseal)された場合、2〜5秒以内にType: SyncedおよびStatus: "True"と表示されます。復号に失敗した場合(strictスコープ適用時のネームスペースの誤字などが典型例)、エラーの詳細がこのコンディション内に明記されます。
対象のSecretが作成されていることを確認し、復号された内容を検査します。
kubectl get secret db-credentials -n backend
kubectl get secret db-credentials -n backend -o jsonpath="{.data.DB_USER}" | base64 --decode
2. 鍵のバックアップと運用Runbook
コントローラーの秘密鍵は、ディザスタリカバリ(DR)において最重要(Tier-1)資産として扱ってください。DRによるクラスター再構築時にアクティブな秘密鍵を紛失すると、Git履歴にあるすべてのSealedSecretマニフェストが復元不能なゴミとなってしまいます。
- 秘密鍵を直ちにバックアップする: アクティブな鍵のSecretをエクスポートし、オフラインのパスワードマネージャーやマネージドなSecret Vaultに保管します。
kubectl get secret -n kube-system \
-l sealedsecrets.bitnami.com/sealed-secret-key=active \
-o yaml > sealed-secrets-key-backup.yaml
- 鍵の自動ローテーション: Sealed Secretsはデフォルトで30日ごとにアクティブな署名鍵をローテーションします。コントローラーは過去のペイロードを復号できるよう、クラスター上に古い秘密鍵も保持し続けます。ベアメタルからクラスターを復元する際は、アプリケーションリポジトリを展開する前に、バックアップしておいたすべての秘密鍵Secretをリストアしてください。
- Prometheusによるモニタリング: コントローラーはポート
8080/metricsでメトリクスを公開しています。rate(sealed_secrets_controller_unseal_errors_total[5m]) > 0に対するアラートルールを設定しておきましょう。これにより、マニフェストの破損や復号鍵の欠落を、ユーザー向けワークロードに影響が出る前に早期検知できます。

