CentOS Stream 9でのHashiCorp Vault構築・設定ガイド:シークレットの一元管理

CentOS tutorial - IT technology blog
CentOS tutorial - IT technology blog

導入:現場の悪夢、認証情報のハードコード問題

プロジェクトの.envファイルを開いた瞬間、PostgreSQLのrootパスワードやAWSシークレットキー、TLS秘密鍵が生テキストで堂々と書かれているのを目にしたことはないでしょうか。システム管理者やDevOpsエンジニアにとって、決して他人事ではない光景のはずです。

サーバーが2〜3台程度であれば、SSHで入って手動修正するだけでも何とかなります。しかし、Kubernetes上で150個以上のコンテナを抱える40のマイクロサービス構成へとスケールした場合、この運用は即座に破綻します。開発者がうっかり公開GitHubリポジトリに設定ファイルをgit pushしてしまえば、わずか30秒で自動スキャンBotにすべての認証情報が抜き取られてしまいます。

CentOS 7からAlmaLinux 9へのインフラ移行を機に、私たちのチームはこの脆弱性を完全に塞ぐことを決断しました。CentOS Stream 9上にシークレット一元管理クラスタを構築したのです。そのデファクトスタンダードであり最も信頼されているソリューションが、HashiCorp Vaultです。

HashiCorp Vaultの基本動作メカニズム

コマンドを実行する前に、以下の4つの重要概念を把握しておくとデバッグ時に迷わなくなります:

  • Storage Backend: 暗号化されたデータを保管する場所です。5ノード未満のクラスタであれば、Vault内蔵のIntegrated Storage (Raft)が最もシンプルな選択肢となります。外部に複雑なConsulクラスタを別途構築する必要はありません。
  • Unseal Keys & Shamir’s Secret Sharing: 起動直後のVaultは、常にsealed(封印)状態にあります。この時点ではデータを一切読み取ることができません。デフォルトのShamirアルゴリズムはマスターキーを5つの断片に分割し、封印解除には最低3つの鍵を組み合わせる必要があります。これにより、単独の管理者が勝手にシークレットボックスを開ける事態を防ぎます。
  • Secrets Engines: データを処理するプラグインです。静的なAPIキーの保管にはkv (Key-Value)が最も一般的です。大規模システムでは、有効期限(TTL)付きで一時的なデータベースユーザーを自動生成するDynamic Secretsを活用できます。
  • Policies & Tokens: Vaultは最小権限の原則(Least Privilege)に基づいて権限を制御します。Vaultへ送信されるすべてのリクエストには、有効なTokenの添付が必須です。

実践:CentOS Stream 9でのVaultサーバー構築

以下の手順は、クリーンなCentOS Stream 9 VPS(構成: 2 vCPU、4GB RAM)上で実機検証を行っています。

ステップ1:HashiCorpリポジトリからのVaultインストール

CentOS Stream 9はDNFでパッケージを管理します。まず公式リポジトリを追加し、最新バージョンを取得できるようにします:

# リポジトリ管理ツールのインストール
sudo dnf install -y dnf-plugins-core

# HashiCorp公式リポジトリの追加
sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo

# Vaultのインストール
sudo dnf install -y vault

インストール完了後、バイナリを確認します:

vault --version

ステップ2:Raft StorageとWeb UIの設定

Raftデータ格納ディレクトリを作成し、vaultユーザーに適切な権限を付与します:

sudo mkdir -p /opt/vault/data
sudo chown -R vault:vault /opt/vault/data
sudo chmod 750 /opt/vault/data

/etc/vault.d/vault.hclファイルを以下の内容で上書きします:

storage "raft" {
  path    = "/opt/vault/data"
  node_id = "node1"
}

listener "tcp" {
  address     = "0.0.0.0:8200"
  tls_disable = 1 # 注意: 本番環境では必ず有効なTLS証明書・秘密鍵を指定してください
}

api_addr      = "http://127.0.0.1:8200"
cluster_addr  = "http://127.0.0.1:8201"
ui            = true
disable_mlock = true

システム起動時の自動起動を有効化し、サービスを開始します:

sudo systemctl enable --now vault
sudo systemctl status vault

ステップ3:ファイアウォールポートの開放

サーバーでfirewalldが有効になっている場合は、APIおよびダッシュボードアクセス用にポート8200を開放します:

sudo firewall-cmd --permanent --add-port=8200/tcp
sudo firewall-cmd --reload

ステップ4:クラスタの初期化とVaultのアンシール

Vault CLI用の環境変数を設定します:

export VAULT_ADDR='http://127.0.0.1:8200'

初期化コマンドを実行して鍵を生成します:

vault operator init

画面に5つのUnseal Keyと1つのInitial Root Tokenが出力されます:

Unseal Key 1: dE8jFn3q0... 
Unseal Key 2: 9kLm2xP1a... 
Unseal Key 3: xYz10uVb8... 
Unseal Key 4: aB7cdE3f4... 
Unseal Key 5: pQ4rtL9s2... 

Initial Root Token: hvs.CAESIJ...

重要な警告:これら5つの鍵は、直ちに安全なパスワードマネージャーに保管してください。これらの鍵を紛失すると、すべてのデータが永久に復旧できなくなります。

いずれか3つの鍵を使い、unsealコマンドを3回実行して封印を解除します:

vault operator unseal <Unseal_Key_1>
vault operator unseal <Unseal_Key_2>
vault operator unseal <Unseal_Key_3>

出力結果でSealed: falseと表示されれば、Vaultのデータ受け入れ準備は完了です。

ステップ5:シークレットの保存操作とJSON活用のTips

Root Tokenでログインします:

vault login <Initial_Root_Token>

Key-Valueバージョン2エンジン(バージョニングおよびロールバック対応)を有効化します:

vault secrets enable -version=2 -path=secret kv

アプリケーションのデータベース接続情報を書き込んでみます:

vault kv put secret/my-app/database username="db_admin" password="K8#mP$9vL2@xQ!7z" port=5432

保存したデータを取得して確認します:

vault kv get secret/my-app/database

システムアカウント作成時には、24〜32文字程度の強固なパスワードが求められます。適当な文字列を手打ちする代わりに、私はToolCraftのPassword Generatorを愛用しています。長さの調整や特殊文字の指定、エントロピー強度チェックが瞬時に行えます。

また、アプリケーションがREST API経由でVaultと通信する際、レスポンスは深くネストされたJSONになることがよくあります。curlでのデバッグ時にペイロード構造を素早く確認したい場合は、JSON Formatter & Validatorに貼り付けて整形しています。ToolCraftの最大の利点は、ブラウザのクライアントサイドJavaScriptで100%完全動作する点です。バックエンドサーバーへデータが1バイトも送信されないため、機密性の高いシークレットを扱う際も安心して利用できます。

ステップ6:ポリシー作成と安全なToken発行

アプリケーションやCI/CDパイプラインにRoot Tokenを渡すのは絶対に避けてください。該当するシークレットへの読み取り権限のみを持つポリシーを作成しましょう。app-policy.hclを作成します:

cat <<EOF > app-policy.hcl
path "secret/data/my-app/*" {
  capabilities = ["read"]
}
EOF

ポリシーを登録し、有効期限24時間のTokenを発行します:

vault policy write my-app-read app-policy.hcl
vault token create -policy="my-app-read" -ttl="24h"

このTokenはsecret/my-app/配下のデータ読み取りのみが許可されます。24時間経過後は自動的に失効するため、他のサービスに影響を与える心配もありません。

まとめ

HashiCorp Vaultを導入することで、テキストファイルやGitリポジトリにパスワードを平文で放置する悪習を根本から断ち切ることができます。アンシールライフサイクル、KV Engine、ポリシー設計に慣れてきたら、次はMySQL/PostgreSQL向けのDynamic Secretsや、Kubernetes Podへ直接シークレットを自動注入するVault Agentの活用にもステップアップしてみてください。

Share: