implementing-secrets-management-with-vault について
プラットフォームやDevOpsチーム向けのHashiCorp Vault導入手順書です。動的認証情報、Kubernetesワークロードへのシークレット提供、暗号化、PKI、アクセス制御、監査ログの設定を扱います。
主な機能
- 01高可用性Vaultのデプロイ
- 02人間とマシンの認証設定
- 03有効期限付き動的認証情報の生成
- 04Kubernetesワークロードへのシークレット提供
- 05Transit暗号化とPKI管理
- 06最小権限ポリシーと監査の設定
活用シーン
- 01CI/CDのハードコードされた認証情報の置き換え
- 02有効期限付きデータベース認証情報の発行
- 03Kubernetes Podへのシークレットファイル提供
- 04シークレットアクセスと管理操作の監査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill implementing-secrets-management-with-vaultインストール後、Claude Code で/implementing-secrets-management-with-vaultを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSEreferences/api-reference.mdscripts/agent.py詳細
Vault によるシークレット管理の実装
使用する場面
- アプリケーションがデータベースのパスワード、API キー、証明書を環境変数や設定ファイルに保存している場合
- 静的で有効期間の長い認証情報から、動的で有効期間の短いシークレットへ移行する場合
- Kubernetes ワークロードからデータベースの認証情報やクラウドプロバイダー API に安全にアクセスする必要がある場合
- コンプライアンス要件により、監査ログを備えた一元的な認証情報管理が求められる場合
- CI/CD パイプラインにハードコードされたシークレットがあり、サプライチェーンのリスクとなっている場合
マルチクラウド要件がなく AWS Secrets Manager で十分な AWS 専用環境、アプリケーションレベルの暗号化ロジック(ただし、Vault Transit が役立つ場合があります)、または ID フェデレーション(managing-cloud-identity-with-okta を参照)には使用しないでください。
前提条件
- HA モードでデプロイされた HashiCorp Vault サーバー(Consul または Raft ストレージバックエンド)
- Vault リスナーエンドポイント用の TLS 証明書
- 名前空間、Sentinel ポリシー、レプリケーション用の Vault Enterprise ライセンス(任意)
- ワークロード統合のための Vault Agent Injector または CSI プロバイダーを備えた Kubernetes クラスター
ワークフロー
ステップ 1:Vault を高可用性モードでデプロイする
外部依存なしで HA を実現するため、Integrated Storage(Raft)を使用して Vault をデプロイします。TLS、監査ログ、クラウド KMS を使用した自動シール解除を設定します。
# vault-config.hcl
storage "raft" {
path = "/opt/vault/data"
node_id = "vault-node-1"
retry_join {
leader_api_addr = "https://vault-node-2.internal:8200"
}
retry_join {
leader_api_addr = "https://vault-node-3.internal:8200"
}
}
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/opt/vault/tls/vault.crt"
tls_key_file = "/opt/vault/tls/vault.key"
}
seal "awskms" {
region = "us-east-1"
kms_key_id = "alias/vault-unseal-key"
}
api_addr = "https://vault-node-1.internal:8200"
cluster_addr = "https://vault-node-1.internal:8201"
telemetry {
prometheus_retention_time = "30s"
disable_hostname = true
}
# Initialize Vault
vault operator init -key-shares=5 -key-threshold=3
# Enable audit logging
vault audit enable file file_path=/var/log/vault/audit.log
# Enable syslog audit for SIEM integration
vault audit enable syslog tag="vault" facility="AUTH"
ステップ 2:認証方式を設定する
人間の運用担当者、アプリケーション、CI/CD パイプライン向けの認証バックエンドを有効にします。マシン認証には AppRole、人間のアクセスには OIDC を使用します。
# Enable OIDC auth for human users via Okta
vault auth enable oidc
vault write auth/oidc/config \
oidc_discovery_url="https://company.okta.com/oauth2/default" \
oidc_client_id="vault-client-id" \
oidc_client_secret="vault-client-secret" \
default_role="default"
# Enable AppRole for application authentication
vault auth enable approle
vault write auth/approle/role/web-app \
secret_id_ttl=10m \
token_num_uses=10 \
token_ttl=20m \
token_max_ttl=30m \
secret_id_num_uses=1 \
token_policies="web-app-policy"
# Enable Kubernetes auth for pod-based access
vault auth enable kubernetes
vault write auth/kubernetes/config \
kubernetes_host="https://kubernetes.default.svc:443" \
token_reviewer_jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token \
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
ステップ 3:動的シークレットエンジンを有効にする
データベースシークレットエンジンを設定し、要求に応じて有効期間の短い認証情報を生成します。各認証情報セットには TTL が設定され、有効期限が切れると自動的に失効します。
# Enable database secrets engine for PostgreSQL
vault secrets enable database
vault write database/config/production-db \
plugin_name=postgresql-database-plugin \
allowed_roles="readonly,readwrite" \
connection_url="postgresql://{{username}}:{{password}}@db.internal:5432/production?sslmode=require" \
username="vault_admin" \
password="initial-password"
# Rotate the root credentials so Vault manages them exclusively
vault write -force database/rotate-root/production-db
# Create a readonly role with 1-hour TTL
vault write database/roles/readonly \
db_name=production-db \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
revocation_statements="REVOKE ALL ON ALL TABLES IN SCHEMA public FROM \"{{name}}\"; DROP ROLE IF EXISTS \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
# Enable AWS secrets engine for dynamic IAM credentials
vault secrets enable aws
vault write aws/config/root \
access_key=AKIAEXAMPLE \
secret_key=secretkey \
region=us-east-1
vault write aws/roles/deploy-role \
credential_type=iam_user \
[email protected] \
default_sts_ttl=3600
ステップ 4:Kubernetes ワークロードと統合する
Vault Agent Injector または CSI Provider を使用して、アプリケーションコードを変更せずにシークレットをポッドに提供します。シークレットは共有ボリューム内のファイルとしてレンダリングされます。
# Kubernetes deployment with Vault Agent Injector annotations
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "web-app"
vault.hashicorp.com/agent-inject-secret-db-creds: "database/creds/readonly"
vault.hashicorp.com/agent-inject-template-db-creds: |
{{- with secret "database/creds/readonly" -}}
export DB_USERNAME="{{ .Data.username }}"
export DB_PASSWORD="{{ .Data.password }}"
{{- end }}
spec:
serviceAccountName: web-app
containers:
- name: web-app
image: company/web-app:v2.1
command: ["/bin/sh", "-c", "source /vault/secrets/db-creds && ./start.sh"]
ステップ 5:Transit 暗号化と PKI を実装する
Transit シークレットエンジンを使用すると、アプリケーションコードで鍵を管理せずにアプリケーションレベルの暗号化を行えます。PKI エンジンをデプロイして、TLS 証明書管理を自動化します。
# Enable Transit engine for encryption as a service
vault secrets enable transit
vault write -f transit/keys/payment-data type=aes256-gcm96
# Encrypt sensitive data
vault write transit/encrypt/payment-data \
plaintext=$(echo "card-number-4111-1111-1111-1111" | base64)
# Enable PKI for internal certificate management
vault secrets enable pki
vault secrets tune -max-lease-ttl=87600h pki
# Generate root CA
vault write pki/root/generate/internal \
common_name="Internal Root CA" \
ttl=87600h
# Configure intermediate CA for issuing certificates
vault secrets enable -path=pki_int pki
vault write pki_int/intermediate/generate/internal \
common_name="Internal Intermediate CA" \
ttl=43800h
# Create a role for issuing certificates
vault write pki_int/roles/internal-services \
allowed_domains="internal.company.com" \
allow_subdomains=true \
max_ttl=720h
ステップ 6:ポリシーと監査証跡を確立する
最小権限の原則に従って、きめ細かな ACL ポリシーを定義します。すべてのシークレットへのアクセスと管理操作について、包括的な監査ログを有効にします。
# web-app-policy.hcl
path "database/creds/readonly" {
capabilities = ["read"]
}
path "transit/encrypt/payment-data" {
capabilities = ["update"]
}
path "transit/decrypt/payment-data" {
capabilities = ["update"]
}
path "secret/data/web-app/*" {
capabilities = ["read", "list"]
}
# Deny access to admin paths
path "sys/*" {
capabilities = ["deny"]
}
# Apply the policy
vault policy write web-app-policy web-app-policy.hcl
# Verify audit log captures all operations
vault audit list -detailed
主な概念
| 用語 | 定義 |
|---|---|
| 動的シークレット | 要求に応じて生成され、有効期限が切れると自動的に失効する認証情報です。有効期間の長い静的な認証情報を不要にします |
| シークレットエンジン | データを保存、生成、または暗号化する Vault のコンポーネントです。KV、データベース、AWS、PKI、Transit の各エンジンが含まれます |
| 自動シール解除 | 手動で鍵を入力せずに、再起動時に Vault ノードのシールを自動解除する、クラウド KMS ベースの仕組みです |
| AppRole | アプリケーションや CI/CD パイプラインのアクセスに、Role ID と Secret ID を使用するマシン向けの認証方式です |
| Transit エンジン | アプリケーションに暗号化鍵を公開せずに暗号処理を行う、サービスとしての暗号化を提供するエンジンです |
| リース | TTL が設定された期間限定の認証情報です。更新されない限り、Vault が有効期限切れ時に自動的に失効させます |
| 名前空間 | 個別の認証、シークレット、ポリシー管理によるテナント分離を実現する Vault Enterprise の機能です |
| レスポンスのラッピング | シークレットの応答を一度だけ使用できるトークンでラップし、受け渡し時の中間者による情報露出を防ぐ手法です |
ツールとシステム
- HashiCorp Vault:動的シークレット、暗号化、ID ベースのアクセスを提供する中核的なシークレット管理プラットフォームです
- Vault Agent Injector:サイドカーコンテナを介して Vault シークレットを Pod ボリュームに自動的に注入する、Kubernetes のミューテーティング Webhook です
- Vault CSI Provider:サイドカーコンテナを使わずに Vault シークレットを Pod ボリュームに直接マウントする、Kubernetes CSI ドライバーです
- consul-template:Vault シークレットを監視し、シークレットの変更時に設定ファイルを再レンダリングするテンプレートレンダリングデーモンです
- Vault Radar:ソースコード、CI/CD パイプライン、クラウド設定にハードコードされた認証情報を検出するシークレットスキャンツールです
よくあるシナリオ
シナリオ:CI/CD パイプラインからハードコードされたデータベース認証情報を排除する
背景:DevOps チームは、PostgreSQL の認証情報を GitHub Actions のシークレットと Jenkins の認証情報ストアに保存しています。同じ認証情報がステージング環境と本番環境で共有されており、18 か月間ローテーションされていません。
アプローチ:
- CI/CD システム向けに AppRole 認証を有効にして Vault をデプロイします
- データベースシークレットエンジンを設定し、ステージング用(読み書き可能、TTL 2 時間)と本番用(読み取り専用、TTL 1 時間)に個別のロールを作成します
- 各パイプラインステージ用に個別の Vault ポリシーを作成し、適切なデータベースロールへのアクセスを制限します
- GitHub Actions ワークフローを更新し、AppRole で認証して各ジョブの開始時に動的認証情報を要求するようにします
- 静的な PostgreSQL 認証情報をローテーションし、root アクセスを Vault のみに渡します
- 監査ログを有効にして、パイプラインジョブのメタデータとともにすべての認証情報リクエストを追跡します
注意点:Vault への移行後に元の静的認証情報をローテーションしないと、古い認証情報が引き続き有効なままになります。TTL を短く設定しすぎると、実行時間の長いジョブではデプロイの途中で認証情報が期限切れになることがあります。
出力形式
Vault Secrets Management Audit Report
=======================================
Vault Cluster: vault.internal.company.com
Version: 1.18.1 Enterprise
HA Mode: Raft (3 nodes)
Seal Type: AWS KMS Auto-Unseal
Report Date: 2025-02-23
SECRET ENGINES:
database/ PostgreSQL dynamic creds Leases Active: 47
aws/ Dynamic IAM credentials Leases Active: 12
transit/ Encryption as a service Keys: 8
pki/ Root CA Certs Issued: 0
pki_int/ Intermediate CA Certs Issued: 234
secret/ KV v2 static secrets Versions: 1,892
AUTH METHODS:
oidc/ Okta SSO for humans Active Tokens: 23
approle/ CI/CD pipelines Active Tokens: 156
kubernetes/ Pod-based auth Active Tokens: 89
AUDIT FINDINGS:
[WARN] 3 AppRole secret_id_num_uses set to 0 (unlimited)
[WARN] 12 KV secrets not accessed in 90+ days (potential orphans)
[PASS] All dynamic secret TTLs under 24 hours
[PASS] Audit logging enabled on all nodes
[PASS] Root token revoked after initial setup
CREDENTIAL HYGIENE:
Static Secrets (KV): 234
Dynamic Secrets Active: 59
Average Lease TTL: 2.3 hours
Secrets Rotated This Month: 12,456