Vault

HashiCorp Vault 完全ガイド:シークレット管理の最前線

目次

  1. イントロダクション
  2. Vault の基本概念
  3. アーキテクチャ概要
  4. 認証メカニズム
  5. シークレット管理
  6. 暗号化機能
  7. ポリシーとアクセス制御
  8. 実装パターン
  9. 運用とベストプラクティス
  10. まとめ

イントロダクション

Vault とは

HashiCorp Vault は、シークレット管理、暗号化、アクセス制御を統一的に提供するオープンソースのセキュリティプラットフォームです。データベース認証情報、API キー、TLS/SSL 証明書、パスワード、暗号化キーなど、あらゆるシークレット情報を安全に保管・管理・利用できます。

重要性

現代のクラウドネイティブアーキテクチャでは、マイクロサービス、コンテナ、サーバーレス環境など、数多くのアプリケーションやシステムがシークレット情報を必要とします。これらを以下の課題と直面します:

  • スケーラビリティ: 数千のアプリケーション、サービスが独立したシークレットを必要
  • セキュリティ: シークレットの漏洩防止とアクセス制御
  • 回転管理: 定期的なシークレット変更の自動化
  • 監査: すべてのアクセス、操作の記録
  • 複雑性: 複数のシークレット形式、バックエンドの統一管理

Vault はこれらすべての課題に対処する統合ソリューションです。

主要な利点

利点説明
集中管理すべてのシークレットを単一の信頼できるソースから管理
動的シークレット必要に応じてオンデマンドでシークレット生成
自動回転バックエンド連携によるシークレット自動更新
監査ログすべてのアクセスと変更の詳細記録
暗号化Transit エンジンによるデータ暗号化
複数認証LDAP、OIDC、AWS IAM など多数の認証方法対応

Vault の基本概念

1. シークレット(Secret)

シークレットは、保護が必要なデータの最小単位です。

種類:

  • スタティックシークレット: データベース認証情報、API キーなど変わらないもの
  • ダイナミックシークレット: Vault が生成し、TTL 後に失効するもの
  • 暗号化データ: Transit エンジンで暗号化されたデータ
# 例:スタティックシークレット
{
  "username": "app_user",
  "password": "super_secret_password_123",
  "host": "db.example.com"
}

# 例:ダイナミックシークレット(生成)
{
  "username": "vault_generated_user_a7f8d9e2",
  "password": "temporary_password_valid_1_hour",
  "ttl": "1h",
  "lease_id": "database/creds/app_role/8e8c3a2b"
}

2. Path(パス)

Vault 内のシークレットは、ファイルシステムのようなパス構造で組織されます。

secret/data/database/prod         # 本番環境のDB認証情報
secret/data/api/stripe           # Stripe API キー
aws/creds/deploy                 # AWS の一時認証情報
pki/issue/web-cert               # 証明書生成
transit/encrypt/payments         # 支払いデータ暗号化

3. Mount Point(マウントポイント)

Vault のシークレットエンジンは、マウントポイント経由でアクセスされます。

secret/    → KV シークレットエンジン
aws/       → AWS シークレットエンジン
database/  → データベースシークレットエンジン
pki/       → PKI(公開鍵基盤)エンジン
transit/   → Transit 暗号化エンジン

4. Token(トークン)

Vault へのアクセスは、必ずトークンで認証されます。トークンはポリシーをattachされ、アクセス権限を定義します。

s.qlBr8Z3jPgLXemCN6p9WNY4a    # トークン例
├─ TTL: 1 hour
├─ Renewable: true
├─ Policies: ["app-policy", "read-secrets"]
└─ Entity ID: 123e4567-e89b-12d3-a456-426614174000

5. Lease(リース)

Vault から発行されるシークレットやトークンは、有効期限を持ちます(リース期間)。

# リースの例
Lease ID: aws/creds/deploy/abc123def456
Lease Duration: 3600s (1 hour)
Lease Renewable: true

# 自動更新(Renew)
- クライアントが事前にリースを更新可能
- 管理者がリース時間をカスタマイズ可能
- TTL 終了後は自動的にシークレット失効

# リース管理
vault lease renew <lease_id>      # リース更新
vault lease revoke <lease_id>     # リース失効

6. Auth Method(認証方式)

Vault へアクセスするために、様々な認証方式が利用可能です。

┌─────────────────────────────────────┐
│    Vault 認証方式                    │
├──────────────┬──────────────────────┤
│ 認証タイプ   │ 例                   │
├──────────────┼──────────────────────┤
│ 基本認証     │ Username/Password    │
│ クラウド     │ AWS IAM, Azure, GCP  │
│ SSO/OIDC     │ Okta, Auth0, Google  │
│ ディレクトリ │ LDAP, Active Dir.    │
│ Kubernetes   │ ServiceAccount Token │
│ AppRole      │ Role ID + Secret ID  │
│ JWT          │ OIDC Token           │
│ TLS Cert     │ Client Certificate   │
└──────────────┴──────────────────────┘

アーキテクチャ概要

1. 全体構成図

┌─────────────────────────────────────────────────────────┐
│                   Client Applications                    │
│  (Web Apps, Microservices, Scripts, CI/CD Pipeline)    │
└──────────────┬──────────────────────────────────────────┘
               │ HTTP/HTTPS
┌──────────────▼──────────────────────────────────────────┐
│                  Vault API / CLI                         │
│              (RESTful HTTP Endpoint)                     │
└──────────────┬──────────────────────────────────────────┘
               │
┌──────────────▼──────────────────────────────────────────┐
│               Authentication Layer                       │
│     (Auth Methods & Access Control Policies)            │
├──────────────────────────────────────────────────────────┤
│ AppRole │ OIDC │ LDAP │ AWS IAM │ Kubernetes │ RADIUS   │
└──────────────┬──────────────────────────────────────────┘
               │
┌──────────────▼──────────────────────────────────────────┐
│                   Policy Engine                          │
│           (Token Generation & Validation)               │
└──────────────┬──────────────────────────────────────────┘
               │
┌──────────────▼──────────────────────────────────────────┐
│              Secret Engines Layer                        │
├──────────────────────────────────────────────────────────┤
│ KV   │ Database │ AWS │ PKI │ Transit │ SSH │ LDAP Dir │
└──────────────┬──────────────────────────────────────────┘
               │
┌──────────────▼──────────────────────────────────────────┐
│                 Storage Backend                          │
│      (Encrypted Data Persisted Here)                    │
├──────────────────────────────────────────────────────────┤
│  File System │ Consul │ S3 │ DynamoDB │ PostgreSQL     │
│  Integrated Storage (Raft) │ etcd │ Azure │ GCS         │
└──────────────────────────────────────────────────────────┘
               │
┌──────────────▼──────────────────────────────────────────┐
│           Encryption & Key Management                    │
│         (Barrier Key & Master Key Hierarchy)            │
└──────────────────────────────────────────────────────────┘

2. コアコンポーネント

2.1 Vault Core(コア)

Vault サーバーの中心。すべての要求を処理します。

┌─────────────────────────────────────┐
│         Vault Core                   │
├─────────────────────────────────────┤
│ • HTTP API Handler                  │
│ • Request Routing                   │
│ • Token Management                  │
│ • Policy Enforcement                │
│ • Lease Management                  │
│ • Audit Logging                     │
└─────────────────────────────────────┘

2.2 Auth Methods(認証)

クライアントが Vault に対してアイデンティティを証明する方法。

例:AppRole 認証フロー
┌─────────────┐
│ Application │
└──────┬──────┘
       │ Send RoleID + SecretID
       ▼
┌──────────────────────────────────────┐
│ AppRole Auth Method                  │
│ ├─ RoleID (Public ID)                │
│ ├─ SecretID (Private Secret)         │
│ └─ Policies attached to role         │
└──────┬───────────────────────────────┘
       │ Validate credentials
       ▼
┌──────────────────────────────────────┐
│ Token Policy Engine                  │
└──────┬───────────────────────────────┘
       │ Return token with policies
       ▼
┌──────────────────────────────────────┐
│ Token = s.abc123... (with policy)    │
└──────────────────────────────────────┘

2.3 Secret Engines(シークレットエンジン)

実際のシークレットデータを管理・生成する機構。

KV Engine (Key-Value)
├─ Version 1: Simple key-value storage
└─ Version 2: Versioning + metadata

Database Engine
├─ PostgreSQL
├─ MySQL
├─ MongoDB
├─ MSSQL
└─ Oracle (with TTL-based credentials)

AWS Engine
├─ Access key generation
├─ STS temporary credentials
└─ EC2 instance authentication

PKI Engine
├─ CA management
├─ Certificate issuance
└─ CRL management

Transit Engine
├─ Data encryption/decryption
└─ Keyring management

2.4 Storage Backend(ストレージバックエンド)

Vault データを暗号化して保存する層。

Integrated Storage (推奨, Enterprise のみ)
├─ 内蔵 Raft コンセンサス
└─ マルチノード HA

External Storage
├─ Consul: 分散型キーバリューストア
├─ etcd: 分散トランザクションログ
├─ S3: AWS S3 バケット
├─ PostgreSQL: リレーショナルDB
├─ DynamoDB: AWS 管理型 NoSQL
├─ Azure Blob: Azure クラウドストレージ
└─ GCS: Google Cloud Storage

2.5 Audit Devices(監査)

すべての API リクエストと応答をログに記録します。

監査ログ形式
{
  "type": "request",
  "auth": {
    "display_name": "approle",
    "token_ttl": 3600,
    "policies": ["app-policy"]
  },
  "request": {
    "id": "req-123",
    "operation": "read",
    "path": "secret/data/database/prod",
    "data": "<redacted>"
  },
  "response": {
    "data": "<redacted>",
    "warnings": null
  },
  "time": "2026-04-07T10:30:45.123456789Z"
}

3. High Availability(HA)構成

┌────────────────────────────────────────────────────────┐
│              Load Balancer                              │
│           (Layer 7 HA Proxy)                           │
└─────────────┬────────────────┬───────────────────────┘
              │                │
    ┌─────────▼────┐  ┌────────▼─────────┐
    │ Vault Server │  │ Vault Server     │
    │   (Active)   │  │   (Standby)      │
    └─────────┬────┘  └────────┬─────────┘
              │                │
              └────────┬───────┘
                       │ Raft Consensus / etcd / Consul
              ┌────────▼──────────┐
              │  Storage Backend  │
              │   (Replicated)    │
              └───────────────────┘

認証メカニズム

1. AppRole 認証(最も一般的)

用途: アプリケーション、CI/CD パイプライン、自動化ツール

# Step 1: AppRole の定義(管理者が実施)
path "secret/data/app/*" {
  capabilities = ["read", "list"]
}

resource "vault_approle_auth_backend_role" "example" {
  backend            = "approle"
  role_name          = "my-app"
  token_ttl          = "1h"
  token_max_ttl      = "24h"
  secret_id_ttl      = "0h"      # 無制限
  secret_id_num_uses = "0"       # 無制限
}

# Step 2: Role ID と Secret ID 取得
resource "vault_approle_auth_backend_role_secret_id" "example" {
  backend   = "approle"
  role_name = vault_approle_auth_backend_role.example.role_name
}

output "role_id" {
  value = vault_approle_auth_backend_role.example.role_id
}

output "secret_id" {
  value = vault_approle_auth_backend_role_secret_id.example.secret_id
}

# Step 3: アプリケーションから認証
vault write auth/approle/login \
  role_id=<ROLE_ID> \
  secret_id=<SECRET_ID>

# 応答:
# Key                Value
# token              s.abc123...
# token_duration     1h
# token_policies     ["my-app"]

セキュリティ上の配置:

  • Role ID: 設定に埋め込むか、環境変数で配布
  • Secret ID: オペレーター管理、回転可能(可選)
  • 分離: Role ID と Secret ID を分けて管理

2. OIDC / JWT 認証

用途: Kubernetes, CI/CD(GitHub Actions など), クラウド環境

# Step 1: OIDC Auth Method 有効化
vault auth enable oidc

# Step 2: OIDC 設定
vault write auth/oidc/config \
  oidc_discovery_url="https://accounts.google.com" \
  oidc_client_id="<CLIENT_ID>" \
  oidc_client_secret="<CLIENT_SECRET>"

# Step 3: Role 定義(Kubernetes ServiceAccount)
vault write auth/oidc/role/my-app \
  bound_audiences="my-app" \
  user_claim="sub" \
  policies="app-policy"

# Step 4: ポッドから認証(Kubernetes)
KUBE_SA_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

vault write -method=post auth/oidc/login \
  role=my-app \
  jwt="$KUBE_SA_TOKEN"

3. AWS IAM 認証

用途: EC2、Lambda、その他 AWS リソース

# Step 1: AWS Auth Method 有効化
vault auth enable aws

# Step 2: AWS IAM セットアップ
vault write auth/aws/config/client \
  access_key=<AWS_ACCESS_KEY> \
  secret_key=<AWS_SECRET_KEY>

# Step 3: Role 定義
vault write auth/aws/role/ec2-role \
  auth_type=ec2 \
  bound_account_id="123456789012" \
  bound_vpc_id="vpc-12345678" \
  policies="app-policy" \
  ttl=1h

# Step 4: EC2 インスタンスから認証
TOKEN=$(curl http://169.254.169.254/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
IDENTITY=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document -H "X-aws-ec2-metadata-token: $TOKEN")
SIGNATURE=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/signature -H "X-aws-ec2-metadata-token: $TOKEN")

vault write -method=post auth/aws/login \
  iam_http_request_method=POST \
  iam_request_body="$IDENTITY" \
  iam_request_signature="$SIGNATURE"

4. Kubernetes ServiceAccount 認証

用途: Kubernetes ポッド

# Step 1: Kubernetes Auth Method 有効化
vault auth enable kubernetes

# Step 2: Kubernetes クラスタ設定
vault write auth/kubernetes/config \
  kubernetes_host="https://kubernetes.default.svc.cluster.local:443" \
  kubernetes_ca_cert="@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" \
  token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"

# Step 3: Role 定義
vault write auth/kubernetes/role/my-app \
  bound_service_account_names=my-app \
  bound_service_account_namespaces=default \
  policies="app-policy" \
  ttl=1h

# Step 4: ポッドから認証
vault write auth/kubernetes/login \
  role=my-app \
  jwt="@/var/run/secrets/kubernetes.io/serviceaccount/token"

シークレット管理

1. KV Secret Engine

用途: スタティックシークレット(パスワード、API キーなど)

# KV v2(推奨、バージョン管理対応)有効化
vault secrets enable -version=2 kv

# シークレット書き込み
vault kv put secret/database/prod \
  username="admin" \
  password="super_secret_123" \
  host="db.production.internal"

# シークレット読み取り
vault kv get secret/database/prod

# 出力:
# ======== Secret Path ========
# secret/data/database/prod
# 
# ======== Data ========
# Key        Value
# host       db.production.internal
# password   super_secret_123
# username   admin

# バージョン管理
vault kv list secret/database/prod        # すべてのバージョン
vault kv get -version=1 secret/database/prod  # 特定バージョン取得

# メタデータ
vault kv metadata get secret/database/prod
vault kv metadata delete secret/database/prod

# JSON 形式で全データ取得
vault kv get -format=json secret/database/prod | jq '.data.data'

2. Database Secret Engine

用途: ダイナミックなデータベース認証情報

# Database Engine 有効化
vault secrets enable database

# PostgreSQL 接続設定
vault write database/config/my-postgres \
  plugin_name=postgresql-database-plugin \
  allowed_roles="readonly","readwrite" \
  connection_url="postgresql://{{username}}:{{password}}@postgres.prod.internal:5432/mydb" \
  username="vault" \
  password="vault_password_123"

# Role 定義(Read-only)
vault write database/roles/readonly \
  db_name=my-postgres \
  creation_statements="CREATE USER \"{{name}}\" WITH PASSWORD '{{password}}' IN ROLE readonly; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
  default_ttl="1h" \
  max_ttl="24h"

# シークレット要求
vault read database/creds/readonly

# 出力:
# Key                Value
# lease_id           database/creds/readonly/abc123...
# lease_duration     1h
# lease_renewable    true
# password           xxxxx
# username           v_readonly_a1b2c3d4_xyz

# 自動回転設定
vault write -f database/rotate-root/my-postgres

マルチデータベース対応:

# MySQL
vault write database/config/my-mysql \
  plugin_name=mysql-database-plugin \
  connection_url="{{username}}:{{password}}@tcp(mysql.prod.internal:3306)/" \
  username="vault" \
  password="vault_pass"

# MongoDB
vault write database/config/my-mongo \
  plugin_name=mongodb-database-plugin \
  connection_url="mongodb://{{username}}:{{password}}@mongo.prod.internal:27017" \
  username="vault" \
  password="vault_pass"

# MSSQL
vault write database/config/my-mssql \
  plugin_name=mssql-database-plugin \
  connection_url="server={{username}}:{{password}}@mssql.prod.internal;port=1433;" \
  username="vault" \
  password="vault_pass"

3. AWS Secret Engine

用途: AWS アクセスキー、STS 認証情報の動的生成

# AWS Engine 有効化
vault secrets enable aws

# AWS 認証情報設定
vault write aws/config/root \
  access_key=<AWS_ACCESS_KEY> \
  secret_key=<AWS_SECRET_KEY> \
  region=us-east-1

# IAM Policy 定義
vault write aws/roles/s3-readonly \
  credential_type=iam_user \
  policy_document=@policy.json \
  ttl=1h \
  max_ttl=24h

# ポリシー内容(policy.json)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-bucket",
        "arn:aws:s3:::my-bucket/*"
      ]
    }
  ]
}

# シークレット要求
vault read aws/creds/s3-readonly

# 出力:
# Key                Value
# lease_id           aws/creds/s3-readonly/abc123
# access_key         AKIAJXYZ123...
# secret_key         xyz123abc...
# ttl                1h

# STS 一時認証(外部アカウント向け)
vault write aws/roles/external-sts \
  credential_type=assumed_role \
  role_arn=arn:aws:iam::ACCOUNT_ID:role/VaultRole \
  ttl=1h \
  max_ttl=12h

vault read aws/creds/external-sts

4. PKI Secret Engine

用途: TLS/SSL 証明書の動的生成

# PKI Engine 有効化
vault secrets enable pki

# キーペアサイズと期間設定
vault secrets tune -max-lease-ttl=87600h pki

# Root CA 生成
vault write -field=certificate pki/root/generate/internal \
  common_name="example.com" \
  ttl=87600h > ca.crt

# 中間 CA 生成
vault secrets enable -path=pki_int pki
vault secrets tune -max-lease-ttl=43800h pki_int

vault write -format=json pki_int/intermediate/generate/internal \
  common_name="example.com Intermediate Authority" \
  | jq -r '.data.csr' > pki_intermediate.csr

# Root CA で署名
vault write -format=json pki/root/sign-intermediate \
  csr=@pki_intermediate.csr \
  format=pem_bundle \
  ttl=43800h | jq -r '.data.certificate' > intermediate.cert.pem

# 中間 CA を設定
vault write pki_int/intermediate/set-signed certificate=@intermediate.cert.pem

# ロール定義(サーバー証明書)
vault write pki_int/roles/example-dot-com \
  allowed_domains="example.com" \
  allow_subdomains=true \
  max_ttl="720h" \
  generate_lease=true

# 証明書発行
vault write pki_int/issue/example-dot-com \
  common_name="api.example.com" \
  ttl="168h"

# 出力:
# Key                Value
# certificate        -----BEGIN CERTIFICATE-----
# issuing_ca         -----BEGIN CERTIFICATE-----
# private_key        -----BEGIN RSA PRIVATE KEY-----
# serial_number      ab:cd:ef:01:23:45:...

5. Transit Secret Engine

用途: アプリケーションレベルのデータ暗号化・復号化

# Transit Engine 有効化
vault secrets enable transit

# 暗号化キー作成
vault write -f transit/keys/payments

# データ暗号化
vault write transit/encrypt/payments \
  plaintext=$(base64 <<< "4111111111111111")

# 出力:
# Key            Value
# ciphertext     vault:v1:abc123def456...

# データ復号化
vault write transit/decrypt/payments \
  ciphertext="vault:v1:abc123def456..."

# 出力:
# Key              Value
# plaintext        NDExMTExMTExMTExMTExMTE=  (base64)

# キー回転(既存データに透過的)
vault write -f transit/keys/payments/rotate

# バッチ処理(複数データ一度に暗号化)
vault write transit/encrypt/payments \
  batch_input='[
    {"plaintext": "NDExMTEx..."},
    {"plaintext": "NDIyMjIy..."}
  ]'

# HMAC 署名生成
vault write transit/hmac/payments \
  input="sensitive_data"

# 署名検証
vault write transit/verify/payments \
  input="sensitive_data" \
  hmac="vault:v1:xxx..."

暗号化機能

1. Encryption at Rest

Vault が保存するすべてのデータは、デフォルトで暗号化されます。

暗号化ハイアラルキー:
┌─────────────────────────────────────┐
│      Master Key (外部管理)           │
│ (HSM, Seal Key, KMS など)           │
└────────────────┬────────────────────┘
                 │
                 ▼ (復号化)
         ┌──────────────┐
         │ Barrier Key  │
         │ (メモリ保持)  │
         └──────┬───────┘
                │ (暗号化)
                ▼
        ┌──────────────┐
        │ Stored Data  │
        │ (ディスク)   │
        └──────────────┘

暗号化方式:

  • AES-256-GCM: データ暗号化(デフォルト)
  • HMAC-SHA256: 整合性検証
  • SHA1: キー導出(Argon2 推奨)

2. Seal Mechanism(シール機構)

Vault は起動時に Unseal が必要です。これは Master Key を使用してシステムを復号化するプロセスです。

# Shamir Secret Sharing(デフォルト)
# Master Key を複数のシェア(キー分割)に分割
vault operator init \
  --key-shares=5 \
  --key-threshold=3

# 出力:
# Unseal Key 1: KqD...
# Unseal Key 2: NmA...
# Unseal Key 3: PlB...
# Unseal Key 4: QmC...
# Unseal Key 5: RnD...
# Root Token: s.xxxxxx

# Unseal(3つのキーが必要)
vault operator unseal <KEY_1>
vault operator unseal <KEY_2>
vault operator unseal <KEY_3>

# ステータス確認
vault status

# 出力:
# Key                    Value
# Sealed                 false
# Total Shares           5
# Threshold              3
# Unseal Progress        3/3

Auto Unseal 設定:

# AWS KMS を使用した自動 Unseal
seal "awskms" {
  region     = "us-east-1"
  kms_key_id = "arn:aws:kms:us-east-1:123456789012:key/abc123"
}

# Google Cloud KMS
seal "gcpkms" {
  project    = "my-project"
  region     = "us"
  key_ring   = "vault"
  crypto_key = "vault-seal"
}

# Azure Key Vault
seal "azurekeyvault" {
  client_id      = "xxxx"
  client_secret  = "xxxx"
  tenant_id      = "xxxx"
  vault_name     = "vault-kms"
  key_name       = "vault-seal"
}

# HSM(Hardware Security Module)
seal "pkcs11" {
  lib = "/usr/lib/softhsm/libsofthsm2.so"
  slot = 0
  pin = "1234"
  key_label = "vault-seal-key"
}

3. Transit によるアプリケーション暗号化

┌─────────────────────────────────────────────────────┐
│          Application Layer                          │
│   (暗号化されていないシークレット データ)             │
└────────────┬────────────────────────────────────────┘
             │ 1. plaintext + keyname
             ▼
┌──────────────────────────────────────────────────────┐
│          Vault Transit Engine                        │
│   ├─ Request: encrypt/payments?plaintext=...        │
│   ├─ Key Management: payments key v3                │
│   ├─ Cipher: AES-256-GCM                            │
│   └─ Response: ciphertext=vault:v1:abc...           │
└────────────┬──────────────────────────────────────────┘
             │ 2. ciphertext のみ保存
             ▼
┌──────────────────────────────────────────────────────┐
│          Application Data Store                     │
│   (暗号化されたデータのみ格納)                       │
│   {                                                 │
│     "user_id": 123,                                 │
│     "ssn": "vault:v1:abc123def456",                 │
│     "phone": "vault:v1:xyz789..."                   │
│   }                                                 │
└──────────────────────────────────────────────────────┘

ポリシーとアクセス制御

1. Policy 基礎

Policy は HCL(HashiCorp Configuration Language)で記述された ACL ルールです。

# 基本的なポリシー構文
path "<path>" {
  capabilities = ["<capability>"]
  # オプション制約
  parameters = {
    "key" = ["value"]
  }
}

# 利用可能な Capability
read          # 読み取り
create        # 作成
update        # 更新
delete        # 削除
list          # リスト
sudo          # 管理者操作
deny          # 明示的な拒否

2. 実践的なポリシー例

# 例1: アプリケーション用ポリシー
path "secret/data/app/myapp/*" {
  capabilities = ["read", "list"]
}

path "database/creds/app-role" {
  capabilities = ["read"]
}

# 例2: データベース管理者用ポリシー
path "database/config/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

path "database/roles/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

path "database/rotate-root/*" {
  capabilities = ["update"]
}

# 例3: Kubernetes ポッド用ポリシー
path "secret/data/k8s/*" {
  capabilities = ["read"]
}

path "pki_int/issue/web-cert" {
  capabilities = ["create", "update"]
}

# 例4: 監査ログアクセス
path "sys/audit" {
  capabilities = ["read", "list"]
}

path "sys/audit-hash" {
  capabilities = ["update"]
}

# ワイルドカード使用例
path "secret/data/prod/*" {
  capabilities = ["read"]
}

path "secret/data/+/database" {
  capabilities = ["read"]      # secret/data/app/database, secret/data/web/database
}

path "secret/data/*/database/**" {
  capabilities = ["read"]      # secret/data/app/database/mysql 等
}

3. ポリシーの作成と適用

# ポリシーファイル作成(app-policy.hcl)
path "secret/data/app/*" {
  capabilities = ["read", "list"]
}

path "database/creds/readonly" {
  capabilities = ["read"]
}

# ポリシー登録
vault policy write app-policy app-policy.hcl

# ポリシー確認
vault policy read app-policy
vault policy list

# トークンにポリシー付与
vault token create -policy=app-policy -ttl=1h

# AppRole にポリシー付与
vault write auth/approle/role/myapp \
  token_policies="app-policy" \
  secret_id_ttl=0 \
  token_ttl=1h \
  token_max_ttl=24h

4. Entity と Identity Group

OIDC、JWT などで認証されるユーザーを管理します。

# Entity 作成
vault write identity/entity \
  name="john.doe" \
  metadata="role=admin,department=engineering"

# Entity Alias 追加(外部 ID マッピング)
vault write identity/entity-alias \
  name="john.doe@example.com" \
  canonical_mount_accessor=<OIDC_ACCESSOR> \
  mount_accessor=<OIDC_ACCESSOR>

# Entity に Policy 割り当て
vault write identity/entity \
  name="john.doe" \
  policies="admin-policy,read-all-policy"

# Identity Group 作成
vault write identity/group \
  name="engineers" \
  policies="read-all-policy,deploy-policy"

# Entity をグループに追加
vault write identity/group \
  name="engineers" \
  member_entity_ids="<ENTITY_ID>"

# ID Group Policy 適用
vault write identity/group-policies \
  name="engineers" \
  policies="engineering-policy"

実装パターン

1. マイクロサービス向け実装

# docker-compose.yml
version: '3.8'

services:
  vault:
    image: vault:latest
    ports:
      - "8200:8200"
    environment:
      VAULT_DEV_ROOT_TOKEN_ID: "root-token"
      VAULT_DEV_LISTEN_ADDRESS: "0.0.0.0:8200"
    volumes:
      - vault-storage:/vault/file

  app-service-1:
    build: ./app-service-1
    ports:
      - "3001:3000"
    environment:
      VAULT_ADDR: "http://vault:8200"
      VAULT_ROLE_ID: "${SERVICE_1_ROLE_ID}"
      VAULT_SECRET_ID: "${SERVICE_1_SECRET_ID}"
    depends_on:
      - vault

  app-service-2:
    build: ./app-service-2
    ports:
      - "3002:3000"
    environment:
      VAULT_ADDR: "http://vault:8200"
      VAULT_ROLE_ID: "${SERVICE_2_ROLE_ID}"
      VAULT_SECRET_ID: "${SERVICE_2_SECRET_ID}"
    depends_on:
      - vault

volumes:
  vault-storage:

Python クライアント実装:

import hvac
import os
import logging

class VaultClient:
    def __init__(self):
        self.vault_addr = os.getenv('VAULT_ADDR', 'http://localhost:8200')
        self.role_id = os.getenv('VAULT_ROLE_ID')
        self.secret_id = os.getenv('VAULT_SECRET_ID')
        self.client = hvac.Client(url=self.vault_addr)
        self.logger = logging.getLogger(__name__)
        
    def authenticate(self):
        """AppRole 認証"""
        try:
            response = self.client.auth.approle.login(
                role_id=self.role_id,
                secret_id=self.secret_id
            )
            self.client.token = response['auth']['client_token']
            self.logger.info(f"✓ Vault 認証成功 (TTL: {response['auth']['token_ttl']})")
        except Exception as e:
            self.logger.error(f"✗ Vault 認証失敗: {e}")
            raise
    
    def get_database_credentials(self, role):
        """データベース認証情報取得"""
        try:
            secret = self.client.secrets.database.generate_database_credentials(
                name=role
            )
            return {
                'username': secret['data']['username'],
                'password': secret['data']['password'],
                'lease_id': secret['lease_id'],
                'ttl': secret['lease_duration']
            }
        except Exception as e:
            self.logger.error(f"✗ DB認証情報取得失敗: {e}")
            raise
    
    def get_secret(self, path):
        """シークレット取得"""
        try:
            secret = self.client.secrets.kv.read_secret_version(path=path)
            return secret['data']['data']
        except Exception as e:
            self.logger.error(f"✗ シークレット取得失敗 ({path}): {e}")
            raise
    
    def encrypt_data(self, transit_key, plaintext):
        """データ暗号化"""
        try:
            response = self.client.secrets.transit.encrypt_data(
                name=transit_key,
                plaintext=plaintext
            )
            return response['data']['ciphertext']
        except Exception as e:
            self.logger.error(f"✗ 暗号化失敗: {e}")
            raise
    
    def decrypt_data(self, transit_key, ciphertext):
        """データ復号化"""
        try:
            response = self.client.secrets.transit.decrypt_data(
                name=transit_key,
                ciphertext=ciphertext
            )
            return response['data']['plaintext']
        except Exception as e:
            self.logger.error(f"✗ 復号化失敗: {e}")
            raise

# 使用例
if __name__ == "__main__":
    vault = VaultClient()
    
    # 認証
    vault.authenticate()
    
    # DB 認証情報取得
    db_creds = vault.get_database_credentials('readonly')
    print(f"DB User: {db_creds['username']} (TTL: {db_creds['ttl']})")
    
    # API キー取得
    api_key = vault.get_secret('secret/api/stripe')
    print(f"API Key: {api_key['key'][:20]}...")
    
    # データ暗号化
    encrypted = vault.encrypt_data('payments', '4111111111111111')
    print(f"Encrypted: {encrypted}")

2. Kubernetes 統合実装

# vault-setup.yaml - Kubernetes 認証設定

apiVersion: v1
kind: ServiceAccount
metadata:
  name: vault-auth
  namespace: default

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: vault-auth-delegator
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
- kind: ServiceAccount
  name: vault-auth
  namespace: default

---
apiVersion: v1
kind: Pod
metadata:
  name: vault-auth-setup
  namespace: default
spec:
  serviceAccountName: vault-auth
  containers:
  - name: vault-setup
    image: vault:latest
    command:
    - /bin/sh
    - -c
    - |
      # Vault に接続
      export VAULT_ADDR=http://vault:8200
      
      # Kubernetes 認証有効化
      vault auth enable kubernetes || true
      
      # API 設定
      vault write auth/kubernetes/config \
        token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
        kubernetes_host="https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT" \
        kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
      
      # アプリケーション用ロール
      vault write auth/kubernetes/role/myapp \
        bound_service_account_names=default \
        bound_service_account_namespaces=default \
        policies="kv-reader" \
        ttl=24h
      
      echo "✓ Kubernetes 認証セットアップ完了"

---
# アプリケーション Pod
apiVersion: v1
kind: Pod
metadata:
  name: app-pod
  namespace: default
spec:
  serviceAccountName: default
  containers:
  - name: app
    image: myapp:latest
    env:
    - name: VAULT_ADDR
      value: "http://vault:8200"
    - name: VAULT_K8S_MOUNT_PATH
      value: "kubernetes"
    - name: VAULT_ROLE
      value: "myapp"
    volumeMounts:
    - name: k8s-token
      mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      readOnly: true
  volumes:
  - name: k8s-token
    projected:
      sources:
      - serviceAccountToken:
          path: token
          expirationSeconds: 3600

Go クライアント実装:

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"

	"github.com/hashicorp/vault/api"
)

func main() {
	// Vault クライアント初期化
	config := api.DefaultConfig()
	config.Address = os.Getenv("VAULT_ADDR")
	
	client, err := api.NewClient(config)
	if err != nil {
		log.Fatalf("Failed to create Vault client: %v", err)
	}

	// Kubernetes トークン読み込み
	token, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
	if err != nil {
		log.Fatalf("Failed to read token: %v", err)
	}

	// Kubernetes 認証
	auth := client.Auth().Kubernetes()
	secret, err := auth.Login(nil, map[string]interface{}{
		"role": os.Getenv("VAULT_ROLE"),
		"jwt":  string(token),
	})
	if err != nil {
		log.Fatalf("Kubernetes auth failed: %v", err)
	}

	// トークン設定
	client.SetToken(secret.Auth.ClientToken)
	fmt.Println("✓ Kubernetes 認証成功")

	// シークレット取得
	secret, err = client.Logical().Read("secret/data/database/prod")
	if err != nil {
		log.Fatalf("Failed to read secret: %v", err)
	}

	data := secret.Data["data"].(map[string]interface{})
	fmt.Printf("Database User: %s\n", data["username"])
}

3. CI/CD パイプライン統合

# .gitlab-ci.yml または .github/workflows/deploy.yml

deploy:
  image: alpine:latest
  before_script:
    - apk add curl jq
    - |
      # Vault へのログイン(CI/CD トークン)
      export VAULT_ADDR="https://vault.company.com"
      export VAULT_NAMESPACE="ci-cd"
      
      # AppRole 認証
      AUTH_RESPONSE=$(curl -s -X POST \
        $VAULT_ADDR/v1/auth/approle/login \
        -d "{
          \"role_id\": \"$CI_APPROLE_ROLE_ID\",
          \"secret_id\": \"$CI_APPROLE_SECRET_ID\"
        }")
      
      export VAULT_TOKEN=$(echo $AUTH_RESPONSE | jq -r '.auth.client_token')
      echo "✓ Vault 認証成功"
  
  script:
    # AWS 認証情報取得
    - |
      AWS_CREDS=$(curl -s -X GET \
        "$VAULT_ADDR/v1/aws/creds/deploy" \
        -H "X-Vault-Token: $VAULT_TOKEN")
      
      export AWS_ACCESS_KEY_ID=$(echo $AWS_CREDS | jq -r '.data.access_key')
      export AWS_SECRET_ACCESS_KEY=$(echo $AWS_CREDS | jq -r '.data.secret_key')
    
    # デプロイ実行
    - aws s3 sync ./build s3://my-app-bucket --delete
    - echo "✓ デプロイ完了"

運用とベストプラクティス

1. HA 構成(Consul バックエンド)

# vault.hcl 設定ファイル

listener "tcp" {
  address       = "0.0.0.0:8200"
  tls_cert_file = "/etc/vault/certs/vault.crt"
  tls_key_file  = "/etc/vault/certs/vault.key"
}

storage "consul" {
  address = "127.0.0.1:8500"
  path    = "vault/"
  scheme  = "http"
  
  # レプリケーション設定
  consistency_mode = "strong"
  disable_registration = false
}

seal "awskms" {
  region     = "us-east-1"
  kms_key_id = "arn:aws:kms:us-east-1:123456789012:key/xxx"
}

ui = true
log_level = "info"

# API アドレス設定(HA クラスタ向け)
api_addr = "https://vault.example.com"
cluster_addr = "https://vault-node.example.internal"

Terraform による HA 構成:

# terraform/main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# ロードバランサー
resource "aws_lb" "vault" {
  name               = "vault-lb"
  internal           = false
  load_balancer_type = "network"
  
  subnets = var.subnet_ids
  
  enable_deletion_protection = true
  
  tags = {
    Name = "vault-lb"
  }
}

# Auto Scaling Group
resource "aws_autoscaling_group" "vault" {
  name                = "vault-asg"
  vpc_zone_identifier = var.subnet_ids
  min_size            = 3
  max_size            = 5
  desired_capacity    = 3
  
  launch_template {
    id      = aws_launch_template.vault.id
    version = "$Latest"
  }
  
  tag {
    key                 = "Name"
    value               = "vault-server"
    propagate_at_launch = true
  }
}

# Launch Template
resource "aws_launch_template" "vault" {
  name_prefix = "vault-"
  image_id    = data.aws_ami.ubuntu.id
  instance_type = "t3.large"
  
  user_data = base64encode(templatefile("${path.module}/user_data.sh", {
    vault_version = "1.15.0"
  }))
  
  tag_specifications {
    resource_type = "instance"
    tags = {
      Name = "vault-instance"
    }
  }
}

# Consul クラスタ(ストレージバックエンド)
resource "aws_autoscaling_group" "consul" {
  name                = "consul-asg"
  vpc_zone_identifier = var.subnet_ids
  min_size            = 3
  max_size            = 5
  desired_capacity    = 3
  
  launch_template {
    id      = aws_launch_template.consul.id
    version = "$Latest"
  }
}

2. バックアップとディザスタリカバリー

#!/bin/bash
# backup_vault.sh - Vault バックアップスクリプト

set -e

VAULT_ADDR="https://vault.example.com"
BACKUP_DIR="/backups/vault"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/vault_backup_$DATE.snap"

echo "📦 Vault バックアップ開始..."

# バックアップ作成
curl -X PUT \
  "$VAULT_ADDR/v1/sys/leases/lookup/database/creds/readonly" \
  -H "X-Vault-Token: $VAULT_TOKEN" \
  > "$BACKUP_FILE.tmp"

# ストレージバックエンドからの raft スナップショット
vault write -f sys/storage/raft/snapshot \
  > "$BACKUP_FILE"

# 圧縮
gzip "$BACKUP_FILE"

# S3 にアップロード
aws s3 cp "$BACKUP_FILE.gz" \
  "s3://vault-backups/vault_backup_$DATE.snap.gz"

# 古いバックアップ削除(30日以上前)
find "$BACKUP_DIR" -name "vault_backup_*.snap.gz" -mtime +30 -delete

echo "✅ バックアップ完了: $BACKUP_FILE.gz"

ディザスタリカバリー:

#!/bin/bash
# restore_vault.sh - Vault リストア スクリプト

set -e

BACKUP_FILE="$1"  # vault_backup_20260407_120000.snap.gz

if [ -z "$BACKUP_FILE" ]; then
  echo "使用方法: $0 <backup_file>"
  exit 1
fi

echo "🔄 Vault リストア開始..."

# Vault を Raft リーダーから削除
vault write -f sys/storage/raft/remove-peer node_id=<node_id>

# スナップショット復元
gunzip -c "$BACKUP_FILE" | vault write sys/storage/raft/restore-

# 再起動
systemctl restart vault

# ステータス確認
sleep 5
vault status

echo "✅ リストア完了"

3. 監査ログ管理

# 監査ログの有効化

# ファイルベースの監査ログ
vault audit enable file file_path=/var/log/vault/audit.log

# Syslog
vault audit enable syslog tag="vault"

# CloudWatch(AWS)
vault audit enable cloudwatch log_group_name="/vault/audit" log_stream_name="vault-audit"

# 監査ログのフォーマット確認
vault audit list

# JSON 形式の監査ログ解析
cat /var/log/vault/audit.log | jq '.request.path' | sort | uniq -c

# 特定ユーザーのアクション追跡
cat /var/log/vault/audit.log | jq 'select(.auth.display_name=="john.doe") | {time, request_path: .request.path, operation: .request.operation}'

4. ベストプラクティス

カテゴリベストプラクティス
セキュリティTLS を必須にする、Auto Unseal を使用、定期的な監査ログ確認
HA 構成最小3ノード、ロードバランサー経由、複数 AZ 配置
認証本番環境では AppRole、Kubernetes、OIDC を使用
シークレット回転30-90 日ごと、Database Engine の自動回転を活用
ポリシー最小権限原則、定期的なレビュー
バックアップ日次バックアップ、リストアテスト実施
監査すべての監査ログを外部ストレージに送信、1年以上保持
パフォーマンスキャッシング、接続プーリング、レート制限設定

まとめ

主要ポイント

  1. 統合管理: Vault はスタティック/ダイナミック シークレット、認証、暗号化を統一的に管理
  2. スケーラビリティ: マイクロサービス環境で数千のシークレットを安全に運用可能
  3. 自動化: Database Engine、PKI による自動生成・回転
  4. 監査: すべてのアクセスと操作を記録し、コンプライアンス対応
  5. 柔軟性: 複数の認証方式、ストレージバックエンド、暗号化オプション

次のステップ

  • ローカル開発: vault server -dev で試してみる
  • Docker 環境: 公式イメージで HA 構成を構築
  • クラウド統合: AWS、Azure、GCP のネイティブ認証を設定
  • 自動化: Terraform、Helm で IaC 化
  • 監視: Prometheus メトリクスの収集とアラート設定

HashiCorp Vault は、シークレット管理の複雑さを軽減し、セキュアで スケーラブルなインフラストラクチャを構築するための不可欠なツールです。