ストーリー
HashiCorp Vault のアーキテクチャ
コンポーネント
Vault アーキテクチャ:
┌─────────────────────────────────────────┐
│ Vault Server │
│ │
│ ┌─────────────┐ ┌──────────────────┐ │
│ │ Secret │ │ Auth Methods │ │
│ │ Engines │ │ │ │
│ │ ├── KV v2 │ │ ├── Token │ │
│ │ ├── Database │ │ ├── AppRole │ │
│ │ ├── AWS │ │ ├── Kubernetes │ │
│ │ ├── PKI │ │ ├── OIDC/JWT │ │
│ │ └── Transit │ │ └── AWS IAM │ │
│ └─────────────┘ └──────────────────┘ │
│ │
│ ┌─────────────┐ ┌──────────────────┐ │
│ │ Policies │ │ Audit Devices │ │
│ │ (HCL/JSON) │ │ ├── File │ │
│ │ │ │ ├── Syslog │ │
│ │ │ │ └── Socket │ │
│ └─────────────┘ └──────────────────┘ │
│ │
│ ┌──────────────────────────────────┐ │
│ │ Storage Backend │ │
│ │ ├── Raft (推奨、内蔵) │ │
│ │ ├── Consul │ │
│ │ └── DynamoDB │ │
│ └──────────────────────────────────┘ │
└─────────────────────────────────────────┘
Secret Engines の種類
| Secret Engine | 用途 | 出力例 |
|---|---|---|
| KV v2 | 静的シークレットの保存(バージョン管理付き) | APIキー、設定値 |
| Database | 動的DBユーザー/パスワードの生成 | 一時的なPostgreSQL認証情報 |
| AWS | 動的AWSアクセスキーの生成 | 一時的なIAMアクセスキー |
| PKI | X.509証明書の発行 | TLS証明書 + 秘密鍵 |
| Transit | 暗号化/復号化(Encryption as a Service) | 暗号化されたデータ |
| SSH | 動的SSH認証情報 | 一時的なSSH証明書 |
| TOTP | ワンタイムパスワード生成 | TOTP コード |
動的シークレットの設計
PostgreSQL動的シークレットの例
# Vault設定: PostgreSQLバックエンドの設定
resource "vault_database_secret_backend_connection" "postgres" {
backend = "database"
name = "payconnect-db"
allowed_roles = ["readonly", "readwrite"]
postgresql {
connection_url = "postgres://vault_admin:{{password}}@db.example.com:5432/payconnect"
}
}
# 読み取り専用ロール
resource "vault_database_secret_backend_role" "readonly" {
backend = "database"
name = "readonly"
db_name = vault_database_secret_backend_connection.postgres.name
default_ttl = 3600 # 1時間
max_ttl = 86400 # 24時間
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 PRIVILEGES ON ALL TABLES IN SCHEMA public FROM \"{{name}}\";",
"DROP ROLE IF EXISTS \"{{name}}\";"
]
}
// アプリケーションからの動的シークレット取得(TypeScript)
import Vault from "node-vault";
const vault = Vault({
endpoint: process.env.VAULT_ADDR,
token: process.env.VAULT_TOKEN, // AppRole等で取得
});
async function getDatabaseCredentials(): Promise<{
username: string;
password: string;
leaseId: string;
}> {
const result = await vault.read("database/creds/readonly");
return {
username: result.data.username,
password: result.data.password,
leaseId: result.lease_id,
};
// TTL(1時間)後に自動失効
// アプリケーションは定期的に新しい認証情報を取得
}
動的シークレットのライフサイクル
動的シークレットのライフサイクル:
[アプリ] ──→ Vault: "database/creds/readonly" をリクエスト
│
Vault ──→ PostgreSQL: 一時ユーザーを作成(TTL: 1h)
│
Vault ──→ [アプリ]: username + password + lease_id を返却
│
[アプリ] ──→ PostgreSQL: 一時ユーザーでクエリ実行
│
... 1時間後 ...
│
Vault ──→ PostgreSQL: 一時ユーザーを自動削除(Revoke)
│
[アプリ] ──→ Vault: 新しい認証情報をリクエスト(繰り返し)
Vaultのポリシー設計
パス別のアクセス制御
# PayConnect API サービス用のポリシー
path "secret/data/payconnect/*" {
capabilities = ["read", "list"]
}
# データベース動的シークレット(読み取り専用ロール)
path "database/creds/readonly" {
capabilities = ["read"]
}
# Transit暗号化(PII暗号化用)
path "transit/encrypt/payconnect-pii" {
capabilities = ["update"]
}
path "transit/decrypt/payconnect-pii" {
capabilities = ["update"]
}
# 自分のトークン情報の確認のみ許可
path "auth/token/lookup-self" {
capabilities = ["read"]
}
環境別のパス設計
Vault パス構造:
secret/
├── data/
│ ├── production/
│ │ ├── payconnect/
│ │ │ ├── stripe-api-key
│ │ │ ├── jwt-signing-key
│ │ │ └── s3-encryption-key
│ │ └── other-service/
│ ├── staging/
│ │ └── payconnect/
│ │ ├── stripe-api-key(テスト用)
│ │ └── ...
│ └── development/
│ └── payconnect/
│ └── ...
database/
├── creds/
│ ├── payconnect-readonly
│ └── payconnect-readwrite
transit/
├── keys/
│ ├── payconnect-pii(PII暗号化用)
│ └── payconnect-audit(監査ログ暗号化用)
Kubernetes統合パターン
Vault Agent Injectorパターン
# Kubernetes Deployment with Vault Agent Injector
apiVersion: apps/v1
kind: Deployment
metadata:
name: payconnect-api
spec:
template:
metadata:
annotations:
# Vault Agent Injector のアノテーション
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "payconnect-api"
vault.hashicorp.com/agent-inject-secret-db-creds: "database/creds/readonly"
vault.hashicorp.com/agent-inject-template-db-creds: |
{{- with secret "database/creds/readonly" -}}
postgresql://{{ .Data.username }}:{{ .Data.password }}@db.example.com:5432/payconnect
{{- end }}
spec:
serviceAccountName: payconnect-api
containers:
- name: payconnect-api
image: payconnect-api:latest
env:
- name: DATABASE_URL_FILE
value: /vault/secrets/db-creds
「Vault Agent Injectorを使えば、アプリケーションコードをVaultに依存させずにシークレットを注入できる。既存のアプリケーションへの導入が容易だ」 — 田中VPoE
まとめ
| ポイント | 内容 |
|---|---|
| Vaultの3つの役割 | シークレット保存、動的シークレット生成、暗号化サービス |
| 動的シークレット | 一時的な認証情報を生成し、TTL後に自動失効。漏洩リスクを根本的に低減 |
| ポリシー設計 | パス別のアクセス制御で最小権限原則を実現 |
| Kubernetes統合 | Vault Agent Injectorでアプリケーション非依存のシークレット注入 |
チェックリスト
- VaultのSecret Engineの種類と用途を説明できる
- 動的シークレットのライフサイクルを説明できる
- Vaultのポリシー設計パターンを理解した
- Kubernetes統合のパターンを理解した
次のステップへ
次は「ローテーション戦略」を学びます。シークレットの自動ローテーションを実現する設計パターンを身につけましょう。
推定読了時間: 30分