HashiCorp Vault on Ubuntu — シークレット管理サーバーの本番構築

セキュリティ

データベースのパスワードや外部APIのキーを、いまも .env ファイルやソースコードに直書きしていないでしょうか。HashiCorp Vault は、シークレットを一元管理し、暗号化して保存しながらアプリには短命のトークンで渡すための専用サーバーです。漏れたら困る値をサーバーのあちこちに散らさず、1か所で厳重に管理できます。

本記事では Ubuntu 24.04 LTS 上に HashiCorp Vault v2.0.2 を file ストレージ構成(本番に近い形)で実際に構築し、初期化(init)・解錠(unseal)から、KVシークレットの保存、ポリシーによる権限制御、AppRole 認証でのアプリ連携までを Docker コンテナで動かして検証しました。コマンドの実出力と Web UI の実画面をそのまま掲載します。

この記事のポイント

  • Vault は起動直後は「封印(sealed)」状態で、生成された5本のアンシール鍵のうちしきい値の3本を入力するまでデータを復号できない
  • インストールは Docker イメージ(hashicorp/vault)でも公式 apt リポジトリでも可能。今回はどちらも v2.0.2 だった
  • KV v2 エンジンは値を上書きせずversionを増やす。古いパスワードにも戻せる
  • 本番アプリには root トークンを置かず、AppRole で TTL付きの短命トークンを発行して渡す
  • 本番では TLS 有効化・auto-unseal・監査ログを必ず併用する(検証では TLS を無効化)

目次

  1. HashiCorp Vault とは:なぜシークレット専用サーバーが必要か
  2. 前提環境
  3. Vault のインストール(Docker と apt)
  4. サーバーの起動・初期化・解錠
  5. Web UI にログインする
  6. KV v2 でシークレットを保存・取得する
  7. ポリシーで最小権限を設定する
  8. AppRole でアプリから安全にアクセスする
  9. 本番運用のチェックリスト
  10. よくあるエラーと解決策
  11. まとめ

HashiCorp Vault とは:なぜシークレット専用サーバーが必要か

Vault は HashiCorp(Terraform などで有名)が開発するシークレット管理ツールです。データベース認証情報・APIキー・証明書といった「漏れたら困る値」を暗号化して一元管理し、アクセスを認証・認可・監査します。

最大の特徴が「封印(seal)」という仕組みです。Vault はデータをすべて暗号化して保存しますが、その復号に使うキー自体も、起動しただけでは使えない状態(封印)になっています。封印を解く(unseal)には、初期化時に分割された複数の鍵をそろえる必要があります。

Vaultの封印モデル(概念図)
Vaultの封印モデル(概念図)

つまり、サーバーが盗まれてディスクごと持ち出されても、アンシール鍵がそろわなければ中身は開けません。鍵を複数人に分散して配ることが、そのままセキュリティになる設計です。

注意

本記事のコマンドは Ubuntu 24.04 LTS(Docker公式イメージ ubuntu:24.04 / arm64)と hashicorp/vault:latest(v2.0.2)で検証しています。検証の都合上 TLS を無効化していますが、本番では必ず TLS を有効化してください(後述)。

前提環境

  • OS: Ubuntu 24.04 LTS(ubuntu:24.04 Docker イメージ、2026-06-14 実測)
  • Vault: v2.0.2(hashicorp/vault:latest / 2026-06-04 ビルド)
  • ストレージ: file バックエンド(単一ノード。本番の最小構成に相当)
  • Docker がインストールされていること(VPS なら apt install docker.io

Vault のインストール(Docker と apt)

Vault の導入には2つの王道があります。コンテナで動かすなら公式 Docker イメージ、ホストに直接入れるなら HashiCorp 公式 apt リポジトリです。実際に両方を試したところ、どちらも同じ v2.0.2 でした。




ubuntu@linuxlab: ~
# ① Docker イメージ版でバージョン確認
$ docker run –rm hashicorp/vault:latest version
Vault v2.0.2 (a71d5add7ef4…), built 2026-06-04T13:18:11Z

# ② Ubuntu 24.04 に公式 apt リポジトリを追加
$ wget -qO- https://apt.releases.hashicorp.com/gpg \
| gpg –dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
$ echo “deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] \
https://apt.releases.hashicorp.com $(lsb_release -cs) main” \
| sudo tee /etc/apt/sources.list.d/hashicorp.list
$ sudo apt-get update && apt-cache policy vault
vault:
Installed: (none)
Candidate: 2.0.2-1
2.0.2-1 / 2.0.1-1 / 1.21.4-1 … 1.7.7(過去版もすべて提供)

$ sudo apt-get install -y vault && vault version
Vault TLS key and self-signed certificate have been generated in ‘/opt/vault/tls’.
Vault v2.0.2 (a71d5add7ef4…), built 2026-06-04T13:18:11Z
Vault v2.0.2 のインストール(Docker版とapt版・実測)
Vault v2.0.2 のインストール(Docker版とapt版・実測)

正直、ここで少し驚いたのが apt リポジトリのバージョンの豊富さです。1.7.7 から最新の 2.0.2 まで全バージョンが並んでおり、バージョン固定もしやすくなっています。apt 版はインストール時に /opt/vault/tls へ自己署名証明書まで自動生成してくれるのも親切です。

サーバーの起動・初期化・解錠

ここが Vault でいちばん独特な部分です。サーバーを起動しても、すぐには使えません。初期化(init)して解錠(unseal)するまでは封印状態です。まず file ストレージを使う最小の本番設定を書きます。




/vault/config/vault.hcl
ui = true
disable_mlock = true
storage “file” {
path = “/vault/file”
}
listener “tcp” {
address = “0.0.0.0:8200”
tls_disable = 1 # ← 検証用。本番では必ず TLS を有効に
}
api_addr = “http://127.0.0.1:8200”

この設定でサーバーを起動し(vault server -config=/vault/config/vault.hcl)、別のターミナルから初期化と解錠を行います。




ubuntu@linuxlab: ~ (VAULT_ADDR=http://127.0.0.1:8200)
$ vault operator init -key-shares=5 -key-threshold=3
Unseal Key 1: gPXCJWXkbjb9fGhX**********************
Unseal Key 2: v8fYpcVoEdgVLB+X**********************
Unseal Key 3 〜 5: …(合計5本が表示される)
Initial Root Token: hvs.Tf8s********************
Vault initialized with 5 key shares and a key threshold of 3.

$ vault status # 初期化直後は封印されている
Sealed true Total Shares 5 Threshold 3

$ vault operator unseal <Key 1> # Unseal Progress 1/3
$ vault operator unseal <Key 2> # Unseal Progress 2/3
$ vault operator unseal <Key 3> # しきい値3に到達 → 解錠

$ vault status
Sealed false
Storage Type file
Cluster Name vault-cluster-3ab9b456
Version 2.0.2
operator init と unseal の解錠フロー(実測)
operator init と unseal の解錠フロー(実測)

実際に試すと、鍵を1本・2本入れても Sealed: true のままで、3本目を入れた瞬間に解錠されました。1〜2本では絶対に開きません。これが Shamir の秘密分散で、鍵を別々の管理者に配れば1人では開けられない運用にできます。

注意:再起動するたびに unseal が必要

Vault はサーバーを再起動すると再び封印されます。アンシール鍵と Initial Root Token は init のときに一度しか表示されません。安全な場所(パスワードマネージャーや別管理者の手元)に必ず控えてください。失うと復旧できません。

Web UI にログインする

設定で ui = true にしているので、ブラウザから http://<サーバーIP>:8200 にアクセスすると Web UI が使えます。認証方式に「Token」を選び、先ほどの Root Token を貼り付けてサインインします。

Vault Web UI のログイン画面(スクリーンショット・実測)
Vault Web UI のログイン画面(スクリーンショット・実測)

ログインすると、ダッシュボードに Vault v2.0.2、有効なシークレットエンジン、そして Cluster information(Storage type が file、TLS が Disabled になっている点に注目)が表示されます。

Vault ダッシュボード画面(スクリーンショット・実測)
Vault ダッシュボード画面(スクリーンショット・実測)
著者アイコン
著者アイコン

root トークンでログインすると「このトークンはブラウザに保存されません」という警告が出ます。ページを更新すると再ログインが必要になります。これはわざとそういう仕様で、root を常用させないための親切設計です。

KV v2 でシークレットを保存・取得する

シークレットを保存する最も基本的な場所が KV(Key-Value)シークレットエンジンです。バージョン2を有効化すると、値の変更履歴が残ります。




ubuntu@linuxlab: ~
$ vault secrets enable -version=2 -path=secret kv
Success! Enabled the kv secrets engine at: secret/

$ vault kv put secret/myapp/db username=appuser password=S3cr3t-Pass-v1 host=10.0.0.5 port=5432
version 1
$ vault kv put secret/myapp/db username=appuser password=R0tated-Pass-v2 host=10.0.0.5 port=5432
version 2 ← 上書きではなく新バージョンとして追加

$ vault kv get -version=1 secret/myapp/db # 古い値も取り出せる
password S3cr3t-Pass-v1
$ vault kv get secret/myapp/db # 既定は最新(v2)
password R0tated-Pass-v2
KV v2 のバージョン管理の実挙動(実測)
KV v2 のバージョン管理の実挙動(実測)

同じパスに put しても上書きされず、version が 1 → 2 と増えました。パスワードをローテーションしたあとでも、-version=1 を付ければ以前の値(S3cr3t-Pass-v1)をそのまま取り出せます。誤って壊しても直前に戻せるのが KV v2 の安心感です。

Web UI からも同じシークレットを確認できます。secret/ エンジンを開くと myapp/ フォルダが見え、その中の db を開くと現在のバージョンや API パスが表示されます。

KVシークレットエンジンの一覧画面(スクリーンショット・実測)
KVシークレットエンジンの一覧画面(スクリーンショット・実測)
シークレット secret/myapp/db の詳細画面(スクリーンショット・実測)
シークレット secret/myapp/db の詳細画面(スクリーンショット・実測)

詳細画面では Current version: 2 と、CLI/API から参照するときのパス(/v1/secret/data/myapp/db)が一目で分かります。コマンドが苦手なメンバーでも、この画面なら値の確認やローテーションができます。

ポリシーで最小権限を設定する

Vault は「デフォルト拒否」のシステムです。明示的に許可していないパスは、たとえ存在しても読めません。アプリには「自分の領域だけ読める」最小権限のポリシーを与えます。




ubuntu@linuxlab: ~
$ vault policy write app-ro – <<EOF
path “secret/data/myapp/*” {
capabilities = [“read”, “list”]
}
EOF
Success! Uploaded policy: app-ro

# このポリシーを持つトークンの「能力」を確認
$ vault token create -policy=app-ro -ttl=1h # → トークン発行
$ vault token capabilities <token> secret/data/myapp/db
list, read ← 自分の領域は読める
$ vault token capabilities <token> secret/data/other/db
deny ← 許可していない他人の領域は拒否

myapp 配下は read, list が返り、許可していない other 配下は deny になりました。Web UI の「Access control → ACL policies」からも同じポリシーを GUI で管理できます。

ACLポリシー(Access control)画面(スクリーンショット・実測)
ACLポリシー(Access control)画面(スクリーンショット・実測)

AppRole でアプリから安全にアクセスする

ここが本番運用の肝です。アプリに root トークンを渡すのは絶対にNG。代わりに AppRole 認証を使い、アプリには「RoleID + SecretID」で TTL付きの短命トークンを発行します。




ubuntu@linuxlab: ~
$ vault auth enable approle
Success! Enabled approle auth method at: approle/
$ vault write auth/approle/role/myapp token_policies=app-ro token_ttl=1h token_max_ttl=4h

$ vault read auth/approle/role/myapp/role-id
role_id 2b41680f-cc1e-6f0c-8723-e2748a591334
$ vault write -f auth/approle/role/myapp/secret-id
secret_id 0d3101b6-39f3-afee-d94f-99ccd9466071

# アプリ起動時に role_id + secret_id でログイン
$ vault write auth/approle/login role_id=<rid> secret_id=<sid>
token hvs.CAESIHD0…(TTL 3600秒の短命トークン)
token_policies [app-ro default]
AppRole 認証でアプリが短命トークンを得る流れ(実測)
AppRole 認証でアプリが短命トークンを得る流れ(実測)

RoleID はアプリの設定に埋め込んでもよい公開値、SecretID だけを CI/CD や起動スクリプトから安全に注入します。発行されるトークンは TTL 1時間(3600秒)の短命トークンで、app-ro ポリシーが付くので許可されたパスしか読めません。万が一漏れても、影響範囲と有効時間が限定されます。

本番運用のチェックリスト

今回の構築で実測できた数値をまとめます。

本番構築の実測サマリ(実測)
本番構築の実測サマリ(実測)

そのうえで、検証構成から本番へ進むときに最低限おさえたいのが次の3点です。

本番化で必ずやること

  • tls_disable = 1 をやめ、TLS を有効化する(Let’s Encrypt や apt 版が生成する /opt/vault/tls を利用)
  • auto-unseal を設定する(クラウドKMSや Transit エンジンで、再起動のたびの手動 unseal を自動化)
  • 監査ログを有効化する(vault audit enable file file_path=/var/log/vault_audit.log

注意:単一ノードの file ストレージは学習・小規模向け

file バックエンドは構成が単純で個人VPSでの学習に向いていますが、冗長化(HA)はできません。可用性が必要な本番では Integrated Storage(Raft)構成や複数ノードを検討してください。

こうしたシークレット管理サーバーは、自分の VPS を1台借りて常時起動しておくと一気に実用的になります。東京リージョンがあり時間課金で試しやすい Vultr あたりが、Vault のような常駐サービスの検証には扱いやすいです。

よくあるエラーと解決策

① Vault is sealed

エラー

Error ... Vault is sealed

原因:サーバーは起動しているが解錠されていません。再起動後は必ず封印されます。アンシール鍵をしきい値ぶん(今回は3本)入力して解錠してください。




ubuntu@linuxlab: ~
$ vault status | grep Sealed
Sealed true
$ vault operator unseal # 対話で鍵を3回入力

② permission denied

原因:トークンの権限不足です。Vault はデフォルト拒否なので、アクセスしたいパスを許可するポリシーがトークンに付いているか vault token capabilities <token> <path> で確認してください。KV v2 のパスは secret/data/...(API上は data/ が入る)である点に注意します。

③ missing client token

原因VAULT_TOKEN 環境変数が未設定です。export VAULT_TOKEN=hvs.... でトークンを設定するか、vault login でログインしてください。同様に接続先は export VAULT_ADDR=http://127.0.0.1:8200 で指定します。

④ Web UI に root token を入れてもログイン後すぐ戻される

root トークンはセキュリティ上ブラウザに保存されません。画面を更新すると再ログインが必要です。日常運用では root を使わず、ユーザーごとの認証メソッド(userpass や OIDC)を有効化して個別アカウントでログインするのが正解です。

まとめ

Ubuntu 24.04 LTS 上に HashiCorp Vault v2.0.2 を file ストレージ構成で構築し、初期化から AppRole 連携までを実際に動かして確認しました。

  • Vault は起動直後は封印状態。5本中3本のアンシール鍵で解錠する(Shamirの秘密分散)
  • インストールは Docker でも apt でも可能で、今回はどちらも v2.0.2 だった
  • KV v2 は値を上書きせずバージョンを増やすので、古いシークレットにも戻せる
  • ポリシーはデフォルト拒否。アプリには最小権限の read/list だけを与える
  • 本番アプリには root を渡さず、AppRole で TTL 1時間の短命トークンを発行する
  • 本番化では TLS 有効化・auto-unseal・監査ログを必ず併用する

シークレット管理は、VPS で本番サーバーを運用し始めると必ずぶつかる課題です。最初は file ストレージの単一ノードで十分動くので、まずは自分の VPS に1台立てて、KV と AppRole の感覚をつかむところから始めるのがおすすめです。

VPS の選び方や Ubuntu サーバーの初期設定については、以下も参考にしてください。

[LINK_X_ARTICLE]

コメント

タイトルとURLをコピーしました