Kubernetesネイティブなシークレットの問題点
KubernetesネイティブのSecretsという名称は、少々誤解を招くかもしれません。これらは実際には暗号化されておらず、単にBase64エンコードされた文字列に過ぎないからです。もしこれらをGitリポジトリにコミットしてしまえば、読み取り権限を持つ人なら誰でも echo 'encoded-string' | base64 --decode を実行するだけで、わずか3秒で本番環境のデータベースを掌握できてしまいます。ジュニアデベロッパーが誤ってStripeのAPIキーを含む.yamlファイルをパブリックリポジトリにプッシュしてしまい、手痛い教訓を得るチームを私は何度も見てきました。
ほとんどの組織は、最終的にAWS Secrets ManagerやHashiCorp Vaultのような専用ツールへと移行します。しかし、それらの値をPodに取り込もうとすると摩擦が生じます。カスタムのPythonスクリプトを書いたり、重いinit-containersを使用したりすることもできますが、これらの手法はメンテナンスの負債を大きく増やします。外部VaultとKubernetesの架け橋をマスターすることは、安全でモダンなCI/CDパイプラインを構築する上で避けて通れないスキルです。
External Secrets Operator (ESO) は、このギャップを完璧に埋めてくれます。これは、プロバイダーからデータを取得し、アプリケーションが利用できるようにKubernetesネイティブのシークレットとして注入するバックグラウンドコントローラーとして機能します。
ESOのコアコンセプトを理解する
ESOは、主に2つのカスタムリソースを使用して重労働を処理します。
- SecretStore / ClusterSecretStore: これらは接続文字列の役割を果たします。AWS、Vault、またはGCPとどのように通信するかを定義します。単一のネームスペースで隔離する場合は
SecretStoreを、クラスター全体で1つの接続を共有する場合はClusterSecretStoreを使用します。 - ExternalSecret: これは何を取得するかを定義するマニフェストです。
prod/db/passwordのような特定のキーを特定し、それをローカルのKubernetesシークレットにマッピングするようオペレーターに指示します。
マニフェストのデバッグ中、ネストされたデータ構造が正しいか確認するために、YAMLとJSONの形式を頻繁に切り替えることがあります。このような時、YAML ↔ JSON 変換ツールを手元に置いておくと便利です。クライアント側で動作するツールを使用することで、変換プロセス中に機密性の高いアーキテクチャパターンがサードパーティのサーバーに漏洩するのを防ぐことができます。
ステップ1:External Secrets Operatorのインストール
インストールにはHelmを使用するのが標準的です。バージョン管理が容易になり、将来的に移行が必要になった際のクリーンアッププロセスも簡単になります。
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets \
external-secrets/external-secrets \
-n external-secrets \
--create-namespace
コントローラーPodのステータスを確認して、デプロイを検証します:
kubectl get pods -n external-secrets
ステップ2:AWS Secrets Managerへの接続
静的なAWSアクセスキーの使用は、何としても避けてください。代わりに、IAM Roles for Service Accounts (IRSA) を使用して、クラスターにシークレットの読み取り権限を付与します。対象のリソースに対して secretsmanager:GetSecretValue を具体的に許可するIAMポリシーが必要になります。
IAMロールをKubernetesのServiceAccountに紐付けたら、以下のようにClusterSecretStoreを適用します:
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: aws-secretsmgr
spec:
provider:
aws:
service: SecretsManager
region: us-east-1
auth:
jwt:
serviceAccountRef:
name: eso-service-account
namespace: external-secrets
ステップ3:HashiCorp Vaultへの接続
Vaultの設定には、通常もう少し多くの構成が必要です。トークンやAppRoleも使用できますが、クラスター内部のトラフィックにはKubernetes Auth Methodが最もクリーンです。以下は、Kubernetesネイティブな認証マウントの標準的な構成です:
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
namespace: my-app
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "my-role"
serviceAccountRef:
name: "my-app-sa"
初期設定時には認証エラーがよく発生します。オペレーターが同期に失敗した場合、私は JWTデコーダー を使用して、サービスアカウントトークンの有効期限やクレームを確認します。これにより、問題がトークンの期限切れなのか、ロール名の不一致なのかを素早く特定できます。
ステップ4:最初のシークレットを同期する
実際にやってみましょう。AWSに /production/api-key という名前のシークレットがあるとします。これを my-app ネームスペースで api-credentials という名前のシークレットとして利用できるようにします。
以下の ExternalSecret マニフェストを適用します:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: api-key-sync
namespace: my-app
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secretsmgr
kind: ClusterSecretStore
target:
name: api-credentials
creationPolicy: Owner
data:
- secretKey: API_TOKEN
remoteRef:
key: /production/api-key
property: api_token_value
これでESOが値をフェッチし、標準的なKubernetesシークレットを生成します。アプリケーションのPodは、これを環境変数やファイルボリュームとしてマウントできます。レガシーなシークレットを移行する場合は、Base64デコーダー を使用して、新しく同期された値を古い手動設定の値と比較してください。これにより、予期しないフォーマット変更によるアプリケーションのクラッシュを防ぐことができます。
現場で役立つ実践的なヒント
本番クラスターで数百のシークレットを管理してきた経験から、いくつか重要な教訓を学びました:
- APIコストに注意: AWS Secrets Managerの料金は、シークレット1つにつき月額0.40ドルに加え、APIコール10,000回ごとに0.05ドルかかります。
refreshIntervalを10秒に設定してはいけません。ほとんどのユースケースでは1時間間隔で十分であり、コストを抑えることができます。 - パスの標準化:
/env/namespace/app/keyのような階層構造を使用してください。これにより、あるチームが誤って別のチームの認証情報を閲覧することを防ぐ、きめ細かなIAMやVaultのポリシーを作成できます。 - ステータスの監視: 同期が失敗した場合、
kubectl describe externalsecret <name>が最強の味方になります。問題が403 Forbiddenエラーなのか、ネットワークタイムアウトなのかを示す明確なイベントログを確認できます。 - 最小権限の原則: ESOにVaultへのクラスター全体の管理者権限を絶対に与えないでください。特定のパスへの読み取り専用アクセス権のみを付与し、万が一侵害された場合の影響範囲を最小限に抑えます。
まとめ
手動の kubectl create secret コマンドを卒業することは、セキュリティと運用効率の両面で大きな勝利です。External Secrets Operatorを導入することで、GitOpsマニフェストをクリーンに保ちながら、堅牢なVaultで信頼できる唯一の情報源(Single Source of Truth)を維持できます。これにより、ローテーションが自動化され、ヒューマンエラーが減り、機密データがソース管理にプレーンテキストで触れることがなくなります。
初期設定には1時間ほどかかりますが、トラブルシューティングで節約できる時間とセキュリティ上の安心感は、それ以上の価値があります。複数の環境でシークレットが自動的に更新されるのを目にすれば、もう手動でのエンコード作業には戻れなくなるでしょう。

