最近、HashiCorp 社が OSS として提供する Vault を触る機会があったので、忘れない内に、記事としてまとめることにしました。この記事は 'Vault 1.18.3' を使用しました。
Vault とは
たくさんの記事が投稿されていて、改めてまとまるまでも無い感じですが、簡単に何をするソフトなのかを要約します。
Vaultは、シークレットと暗号化の管理システムです。シークレットには、「トークン、APIキー、パスワード、暗号化キー、証明書」など、大切な情報や守り、IT資源へのアクセスをコントロールするための「文字列」です。これらのシークレットは、第三者に流出して悪用されないために、厳重に管理しなければなりません。この管理は、関わる人が多くなると難しくなっていきます。この問題を解決するのが Vault です。
厳重な管理の方法として、Vaultのワークフローには、4つの段階があります。
- 認証:クライアントが本人であることを認証すると、トークンが生成され、ポリシーに関連づける。認証方法は、パスフレーズなどから選択可能
- 検証:クライアントを検証するために、Github,LDAPなどと連携してクライアントを検証
- 承認:Vault内の特定のPathと操作(作成、読み取り、更新など)を許可または禁止
- アクセス:クライアントのIDと紐も付いたトークンを発行、これを用いて、シークレット、キー、暗号化機能へのアクセスを許可
Vaultは、安全なシークレットストレージ、動的なシークレット、データの暗号化、有効期限と更新、失効などの機能を提供します。
この様な役割を担うVaultは、複数のシステムから認証/認可のために使用されます。そのため、Vaultが、停止すると、システム全体が使用不能に陥る事が懸念されます。そのことから、Vaultはクラスタ化して可用性を高める必要があります。
Vaultのクラスタ構成
Vaultクラスタの構成は、「自動アンシールのためのVaultサーバー」、「Vaultクラスタのメンバーサーバたち」からなります。
「自動アンシールのためのVaultサーバー」は、どうして必要なのか?、についての説明から始めます。Vaultサーバーの起動するとシール(封印)された状態で起動します。利用者はシール(封印)を解かなければ、Vaultの機能や保存データにアクセスできません。シールを解くためのキーは、Vaultのサーバープロセスのメモリ上だけに保存されています。そのため、Vaultのサーバープロセスを再起動するだけで、封印されてしまい、利用できなくなってしまいます。
封印を解くキーを外部から与える構造は、セキュリティを高めるために役立つのですが、セキュリティが確保されたエリアで運用するには、手間がかかります。Vaultクラスタのメンバーの一つが再起動すると、人手で封印解除のキーをインプットするまでは、クラスタに復帰できません。その問題を解決するために、提供されているのが、自動アンシール(Auto Unseal)の機能です。このVaultサーバーは、予めトークンを持ったサーバーだけに、アンシールのキーを提供します。
Vaultクラスタのメンバーの間では、Raftアルゴリズムによってリーダーが決められます。データの更新は、リーダーだけが担当して、他のメンバーは更新をフォローして同期します。これによって3重化を実現して、可用性を確保します。VaultクラスタのメンバーのVaultサーバーは、起動過程で自動アンシールのサーバーから封印解除のキーを受け取り、自身で封印を解除して、クラスタのメンバーに参加する仕組みです。
Vaultクラスタのメンバーが、自動アンシールのサーバーから封印解除のキーを受け取るためには、起動時に自動アンシールのVaultサーバーが発行したトークンを持っていなければなりません。また、自動アンシールの役割のVaultサーバーは、起動時の自身の封印解除のキーが必要です。
Vaultのビルド済み実行形式のインストール
HashiCorpのWebサイトから、ビルド済みの実行形式をダウンロードします。
curl -OL https://releases.hashicorp.com/vault/1.18.3/vault_1.18.3_linux_amd64.zip
圧縮を解凍して、/usr/local/bin/vault
として配置します。このコマンド一つで、サーバーとクライアントを兼ねていますので、自動アンシールのサーバー、クラスタメンバーのサーバー、操作用ワークステーションに、それぞれ一つづ配置すればインストールは完了です。
自動アンシールのVaultサーバーの構築
このサーバーは、スタンドアロンのサーバーとして構築します。そして、初期化後、封印を解いた後、rootでログインして、自動アンシールの設定を実施します。
Configファイル
Vaultを起動するためのコンフィグファイルです。このファイルの中で、シークレットストレージを'/var/vault-data'に置いています。起動前に、予めディレクトリを作成する必要があります。
このサーバーのIPアドレスは172.16.0.9なので、api_addr にアドレスをセットします。また、listenerには、ローカルホスト(127.0.0.1)のアクセスを受けられる様に、0.0.0.0
をセットします。
TLSの暗号通信のため、認証局証明書(ca.pem)、サーバー証明書(vault-node1.pem)、証明書鍵(vault-node1-key.pem)をセットします。ここでは、証明書の作り方は扱いません。
UIを使用すると、メニュー構造からシステムのアーキテクチャなども伺い知れるので便利ですから、ui = true
にします。
storage "file" {
path = "/var/vault-data"
}
listener "tcp" {
address = "0.0.0.0:8200"
cluster_address = "0.0.0.0:8201"
tls_client_ca_file = "/etc/vault/ca.pem"
tls_cert_file = "/etc/vault/vault-node1.pem"
tls_key_file = "/etc/vault/vault-node1-key.pem"
}
ui = true
disable_mlock = true
cluster_name = "vault-cluster"
api_addr = "https://172.16.0.9:8200"
Systemdのユニットとして起動するためのファイル
次のvault.servie
を/lib/systemd/system
の下に配置することで、systemctl start vault
で起動できるようになります。ただし、初回起動時は、未初期化、封印(シール)された状態ですから、クライアントからコマンドを叩いて、利用可能な状態にする必要があります。
[Unit]
Description=vault - Manage Secrets & Protect Sensitive Data
Documentation=https://developer.hashicorp.com/vault
After=network.target
Wants=network-online.target
[Service]
Type=notify
User=root
PermissionsStartOnly=true
ExecStart=/usr/local/bin/vault server -config /etc/vault/config.hcl
Restart=on-abnormal
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
封印解除
systemctl start vault
を実行して、起動できるか確認してみます。無事に起動できたら、次へ進みます。もし失敗するようであれば、ターミナルのコマンドラインから vault server -config /etc/vault/config.hcl
を実行することで、エラーメッセージをコマンドの応答として確認できます。
Vaultサーバーの初期化と封印解除
Vaultサーバーの起動が完了したら、Vaultクライアントからサーバーを初期化します。'vault server'とすることでサーバーとして、単にvault
としてコマンドを実行すると、クライアンで起動します。
クライアントが、サーバーに接続するためには、TLS暗号通信、サーバーのアドレスなど、必要な環境変数をセット視します。
export VAULT_SKIP_VERIFY=1
export VAULT_CA_CERT=/etc/vault/ca.pem
export VAULT_CLIENT_CERT=/etc/vault/vault-node1.pem
export VAULT_CLIENT_KEY=/etc/vault/vault-node1-key.pem
export VAULT_ADDR=https://127.0.0.1:8200
環境変数が効いていれば、サーバーの状態を次のコマンドで確認できます。
vault status
Vaultサーバーを初期化します。
実行結果として、封印解除のキー、root トークンなどが表示されますので、大切に保管しておきます。
vault operator init -format=json -key-shares 1 -key-threshold 1
Vaultは封印された状態で起動します。その封印を解除するために、封印解除のキーが必要です。以下のコマンドで、封印を解除します。
vault operator unseal <UNSEAL KEY>
Auto unsealサーバーの設定
rootでログインすることで、Vaultサーバーのコマンドを受け付ける様になります。
vault login <ROOT TOKEN>
シークレットの転送機能を有効化して、転送キーをセットいます。
vault secrets enable transit
vault write -f transit/keys/autounseal
転送キー対して、ポリシーを設定します。ポリシーには、可能な操作を記載します。
vault policy write autounseal -<<EOF
path "transit/encrypt/autounseal" {
capabilities = [ "update" ]
}
path "transit/decrypt/autounseal" {
capabilities = [ "update" ]
}
EOF
自動封印解除対象のサーバーのために、トークンを生成します。
ここで作成したトークンは、厳重に保存します。
vault token create -orphan -policy="autounseal" \
-wrap-ttl=120 -period=24h \
-field=wrapping_token > wrapping-token.txt
vault unwrap -field=token $(cat wrapping-token.txt)
rm wrapping-token.txt
以上で、オートアンシールのためのサーバーの設定は完了です。
Vaultクラスタメンバーのサーバー設定
Vaultクラスタを構成するサーバーを、最初に1つだけ起動して、初期化します。
Systemdから起動するためのファイル
systemd から起動するためのファイルを /lib/systemd/systemの下に配置します。
ポイントは、Environment=VAULT_TOKEN='から始まる行で、前述で取得してた
hvs.CAESIB*****`から始まるトークンをセットします。
# /lib/systemd/system/vault.service
[Unit]
Description=vault - Manage Secrets & Protect Sensitive Data
Documentation=https://developer.hashicorp.com/vault
After=network.target
Wants=network-online.target
[Service]
Environment=VAULT_TOKEN=hvs.CAESIB******************************************
Type=notify
User=root
PermissionsStartOnly=true
ExecStart=/usr/local/bin/vault server -config /etc/vault/config.hcl
Restart=on-abnormal
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
VaultのConfigファイル
{{ ansible_facts.all_ipv4_addresses[0] }}
の部分は、サーバーのIPアドレスで置き換えます。また、{{ ansible_facts.hostname }}
はホスト名で置き換えます。
leader_api_addr
から始まる行には、それぞれ、VaultクラスタのメンバーとなるIPアドレスをセットします。
起動前に、Vaultが保存するデータのために、/var/vault-data
のディレクトリを作成しておきます。また、/etc/vault
の下に認証局証明書、サーバー証明書、鍵を配置しておきます。
api_addr = "https://{{ ansible_facts.all_ipv4_addresses[0] }}:8200"
cluster_addr = "https://{{ ansible_facts.all_ipv4_addresses[0] }}:8201"
cluster_name = "vault-cluster"
disable_mlock = true
ui = true
storage "raft" {
path = "/var/vault-data"
node_id = "{{ ansible_facts.hostname }}"
retry_join {
leader_tls_servername = "vault-cluster"
leader_api_addr = "https://172.16.31.11:8200"
leader_ca_cert_file = "/etc/vault/ca.pem"
leader_client_cert_file = "/etc/vault/vault-node1.pem"
leader_client_key_file = "/etc/vault/vault-node1-key.pem"
}
retry_join {
leader_tls_servername = "vault-cluster"
leader_api_addr = "https://172.16.31.12:8200"
leader_ca_cert_file = "/etc/vault/ca.pem"
leader_client_cert_file = "/etc/vault/vault-node1.pem"
leader_client_key_file = "/etc/vault/vault-node1-key.pem"
}
retry_join {
leader_tls_servername = "vault-cluster"
leader_api_addr = "https://172.16.31.13:8200"
leader_ca_cert_file = "/etc/vault/ca.pem"
leader_client_cert_file = "/etc/vault/vault-node1.pem"
leader_client_key_file = "/etc/vault/vault-node1-key.pem"
}
}
listener "tcp" {
address = "0.0.0.0:8200"
cluster_address = "0.0.0.0:8201"
tls_client_ca_file = "/etc/vault/ca.pem"
tls_cert_file = "/etc/vault/vault-node1.pem"
tls_key_file = "/etc/vault/vault-node1-key.pem"
}
seal "transit" {
address = "https://172.16.0.9:8200"
disable_renewal = "false"
key_name = "autounseal"
mount_path = "transit/"
tls_cert_file = "/etc/vault/vault-node1.pem"
tls_key_file = "/etc/vault/vault-node1-key.pem"
tls_client_ca_file = "/etc/vault/ca.pem"
tls_skip_verify = "true"
}
以上の設定が完了したら、systemctl start vault
で起動できるか確認しておきます。
クラスタメンバーの初期化
Vaultクライアントのための環境変数をセットしておきます。
export VAULT_SKIP_VERIFY=1
export VAULT_CA_CERT=/etc/vault/ca.pem
export VAULT_CLIENT_CERT=/etc/vault/vault-node1.pem
export VAULT_CLIENT_KEY=/etc/vault/vault-node1-key.pem
export VAULT_ADDR=https://172.16.31.11:8200
Vaultクライアントが、サーバーと接続できるか確認します。
vault status
Key Value
--- -----
Seal Type transit
Recovery Seal Type n/a
Initialized false
Sealed true
<以下省略>
サーバーを初期化します。
vault operator init
表示されたキーを大切に保存しておきます。
Recovery Key 1: vzdo7uWa/q2W2K6HZkN+LdzTWs05kRZn3QkjDImK8J6S
Recovery Key 2: 4hQTImabMwOL1+/JeysACP2UY4xqIZ6JX3F3Bghnm4cU
Recovery Key 3: 5TK3LrcsGtmPuXKzAyozwE6TmsCU7DaQ5SroasiSfdWn
Recovery Key 4: VJcHeofUZyftzdtFzkyLLRroVq59pA5zK/Y69ePSKBsb
Recovery Key 5: DMvsZvtxS3OCTCSz5uGZhD4xk7JniApfUP2x6C6Xwt5R
Initial Root Token: hvs.1x3NPGQV8BkfgIzkcCKVsvDG
Success! Vault is initialized
rootでログインして、クラスタ状態を確認します。
クラスタに参加するvaultサーバーにログインするには、前述の初期化で表示されたInitial Root Token:
を使います。
vault login <hvs.から始まるトークン>
クラスタのメンバーをリストできます。
$ vault operator raft list-peers
Node Address State Voter
---- ------- ----- -----
node1 172.16.31.11:8201 leader true
クラスタメンバーの追加
/etc/vault/config.hcl
,/var/vault-data
などの設定を実施して、systemctl start vault
を実行することで、参加するメンバーが増えていきます。
vault operator members
Host Name API Address Cluster Address Active Node Version Upgrade Version Redundancy Zone Last Echo
--------- ----------- --------------- ----------- ------- --------------- --------------- ---------
node1 https://172.16.31.11:8200 https://172.16.31.11:8201 true 1.18.3 1.18.3 n/a n/a
node2 https://172.16.31.12:8200 https://172.16.31.12:8201 false 1.18.3 1.18.3 n/a 2025-02-02T12:46:13Z
node3 https://172.16.31.13:8200 https://172.16.31.13:8201 false 1.18.3 1.18.3 n/a 2025-02-02T12:46:16Z
Vault Clusterの構築は以上です。パブリッククラウドでは、自動アンシールのサービスを実施していますので、使用するクラウドのドキュメントを参照ください。