3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

HashiCorp Vault HA Cluster構築

Last updated at Posted at 2023-05-22

概要

HashiCorp Vault(OSS版)にて、HA Clusterを構成します

前提条件

  • HashiCorp Vault 1.13.2 を使って確認した内容です。
  • AWS にて、AmazonLinux 2023 3台を使用します。
  • Vaultは、Auto-Unseal を利用できるものとします。(未設定の場合は利用できる状態まで済ませてください)
  • AWSやEC2、ALB、DNSまわりの細かな設定は省略しますので、ご利用の環境にあわせて設定を行ってください。

上記以外の環境でも利用可能ですので、必要に応じて読み替えてください。

まだauto-unsealを利用していない場合は、こちらで切り替え方法のメモやリンクを載せてます
unseal - auto-unseal 切り替え
PC上で検証環境を作る場合でも、Transit Keyを使ったAuto-Unsealが利用可能です。 ※追加でvaultがもう1台必要
seal Stanza
Auto Unseal(Tutorial)

はじめに

全体の構成

大雑把にですが、下図のような構成を作ります。

image.png

  • Vault 3台 を 3つのAZに分散して配置し、1つのClusterを構成します(IPは説明の都合上、連番にしています)
    • vault-01 192.168.11.101
    • vault-02 192.168.11.102
    • vault-03 192.168.11.103
  • 外部からは、ALB経由でvault cluster にアクセスします
  • clusterの各node間通信、およびALBから各node宛ての通信はhttpsに対応します

ストレージタイプについて

Vault ver 1.13 においては、ストレージにRaftを利用することで、シンプルにCluster構成を組むことができます。
RaftはVaultに統合されているため、Vaultのインストールのみで利用でき、追加機能のインストールは不要です。

Raftを利用して初期構成を行うことで、あとはメンバー追加するのみで、容易にClusterを構成できます。

HA Cluster構成は、ストレージタイプ Fileでは利用できません。
Fileですでに稼働中の場合は、データの変換をする必要があります。
※本記事にて、FileからRaftへの移行手段も紹介しています。

Raftの利用時の注意点

  • Raftでは、最低3台での構成が必要です。(1台の障害まで許容)
    ※2台構成では、1台の障害のみでRaftストレージのRead/Writeが一切できなくなります。
  • Raft利用時は、バースト可能なCPUの利用は避けることを推奨されています。
    本記事ではt2.micro を利用しますが、負荷のかかる環境で利用する場合は、適切なインスタンスタイプを選択してください。
    Vault with Integrated Storage Reference Architecture

For predictable performance on cloud providers, it's recommended to avoid "burstable" CPU and storage options (such as AWS t2 and t3 instance types) whose performance may degrade rapidly under continuous load.

OSS版でのクラスタ構成の動作制限

  • OSS版のVaultでは、Clusterの台数を増やしても、性能の向上はありません。
    (Standby Node 宛ての処理は、すべてActive Node に転送される)
    ※HCP Vault や、Enterprise版では、Standby NodeでもRead処理を行う機能があるようです。
  • その他、機能の違いについては、下記をご参照ください。

事前準備

EC2の用意

EC2 を3台作成し、vaultをインストールします。
(本例では、AmazonLinux 2023、 InstanceType t2.micro を使用します)

sudo hostnamectl set-hostname vault-01
sudo timedatectl set-timezone Asia/Tokyo

# vault インストール ※この時点ではまだ起動しない
sudo dnf install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo
sudo dnf -y install vault
sudo systemctl enable vault

# vault auto complete と、必要環境変数追加
cat <<EOF | sudo tee /etc/profile.d/vault.sh
complete -C /usr/bin/vault vault
export VAULT_SKIP_VERIFY=true
EOF

source /etc/profile

# rsyslog が入ってないので追加
sudo dnf -y install rsyslog
sudo systemctl enable rsyslog
sudo systemctl start rsyslog

SecurityGroup

下記のようなSecurityGroupを定義し、EC2に割り当ててください。

  • 同一SecurityGroup内の通信を許可 (Cluster node間の通信用)
  • ALBからの tcp 8200 の通信を許可
  • その他管理に必要な任意の通信を許可

証明書の用意(https対応時のみ)

node間の通信をhttpsに対応させるために、SSL証明書を用意します。
本例では、証明書作成を簡単にするために、自己署名証明書を利用します。

証明書作成処理 ※要openssl 1.1.1 または 3.0

# 自己署名でSANに vault-cluster を含めた証明書を生成
openssl req -x509 -days 7300 -nodes -newkey rsa:2048 -out vault-cluster.crt -keyout vault-cluster.key -config - <<EOF
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no

[req_distinguished_name]
CN = vault-cluster

[v3_req]
keyUsage = digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = vault-cluster
EOF

説明の都合上、CA証明書も同一ファイルをコピーして使用します。
※ファイル名が同じだとサーバー証明書とCA証明書の設定を混同しそうなので・・・
(みなさんは同じファイル名のままでも構いません)

cp vault-cluster.crt ca.crt

用意した証明書、秘密鍵、CA証明書を、vault の設定に合わせて配置します
※証明書ファイルは、3台ともに同一ファイルを使用してください。

# 証明書の配置
sudo cp vault-cluster.crt vault-cluster.key ca.crt /opt/vault/tls/
sudo chown -R vault: /opt/vault/tls

もし、ご自身でCA証明を利用している場合は、そちらを使って作成してください。
ただし、SANの登録は必須ですので、適切な値(本例では vault-cluster )を登録してください。

Tagの割り当て(auto-join に対応する場合のみ)

3台のEC2に、Vault-Clusterのメンバーであることを示すタグを設定します。
本記事では、 Tag名 Projectvault-dev-cluster とします。
※タグ名、値は任意です

IAM Roleの設定(auto-join に対応する場合のみ)

IAM Roleを作成し、必要なPolicyを追加します。(auto-join に対応する場合のみ)

vault_autojoin
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "ec2:DescribeInstances",
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}

このIAM Roleを、上記で作成した3台のEC2に割り当ててください。

【参考】Vault関連のPolicy

aws 上でvaultを稼働させる場合、ほとんどの場合、aws auth や、aws secret は利用するかと思います。
その場合に必要となるPolicyの一例です。※必要に応じて修正してください。

vault_aws_auth
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "iam:ListRoles",
        "iam:GetUser",
        "iam:GetRole",
        "iam:GetInstanceProfile",
        "ec2:DescribeInstances"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}
vault_aws_secret
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "iam:RemoveUserFromGroup",
        "iam:PutUserPolicy",
        "iam:ListUserPolicies",
        "iam:ListGroupsForUser",
        "iam:ListAttachedUserPolicies",
        "iam:ListAccessKeys",
        "iam:GetUser",
        "iam:DetachUserPolicy",
        "iam:DeleteUserPolicy",
        "iam:DeleteUser",
        "iam:DeleteAccessKey",
        "iam:CreateUser",
        "iam:CreateAccessKey",
        "iam:AttachUserPolicy",
        "iam:AddUserToGroup"
      ],
      "Effect": "Allow",
      "Resource": [
        "arn:aws:iam::*:user/*",
        "arn:aws:iam::*:group/*"
      ]
    },
    {
      "Action": "iam:ListUsers",
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}
vault_aws_secret_assumerole
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Effect": "Allow",
      "Resource": "arn:aws:iam::*:role/*"
    }
  ]
}

構成ファイル指定によるCluster構成

おおよそ下記の順番で進めます

  • vault-01 にて、Raft構成、Auto-Unseal の設定を行う。
  • vault-01 で、vault サービスを起動。vault operator initで初期化を行う
  • vault-02, 03 も、Raft構成を行ったうえでサービス起動
  • 3台ともRaftのメンバーとなったことを確認

vaultの設定 (設定項目の説明)

vault-01, 02, 03 それぞれで、構成ファイル /etc/vault.d/vault.hclに設定を追加します。
※設定が終わっても、この時点ではまだ起動しないでください。

/etc/vault.d/vault.hcl
ui = true

api_addr      = "https://192.168.11.101:8200"
cluster_addr  = "https://192.168.11.101:8201"
disable_mlock = true

enable_response_header_hostname = true
enable_response_header_raft_node_id = true

storage "raft" {
  path    = "/opt/vault/raft"
  node_id = "vault-01"

  retry_join {
    leader_tls_servername   = "vault-cluster"
    leader_api_addr         = "https://192.168.11.101:8200"
    leader_ca_cert_file     = "/opt/vault/tls/ca.crt"
    leader_client_cert_file = "/opt/vault/tls/vault-cluster.crt"
    leader_client_key_file  = "/opt/vault/tls/vault-cluster.key"
  }
  retry_join {
    leader_tls_servername   = "vault-cluster"
    leader_api_addr         = "https://192.168.11.102:8200"
    leader_ca_cert_file     = "/opt/vault/tls/ca.crt"
    leader_client_cert_file = "/opt/vault/tls/vault-cluster.crt"
    leader_client_key_file  = "/opt/vault/tls/vault-cluster.key"
  }
  retry_join {
    leader_tls_servername   = "vault-cluster"
    leader_api_addr         = "https://192.168.11.103:8200"
    leader_ca_cert_file     = "/opt/vault/tls/ca.crt"
    leader_client_cert_file = "/opt/vault/tls/vault-cluster.crt"
    leader_client_key_file  = "/opt/vault/tls/vault-cluster.key"
  }
}

# HTTP listener
#listener "tcp" {
#  address = "0.0.0.0:8200"
#  cluster_address = "0.0.0.0:8201"
#  tls_disable = 1
#}

# HTTPS listener
listener "tcp" {
  address       = "0.0.0.0:8200"
  cluster_address = "0.0.0.0:8201"
  tls_cert_file = "/opt/vault/tls/vault-cluster.crt"
  tls_key_file  = "/opt/vault/tls/vault-cluster.key"
  tls_client_ca_file = "/opt/vault/tls/ca.crt"
}

# AWS KMS auto unseal
seal "awskms" {
  region = "ap-northeast-1"
  kms_key_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
項目 説明
api_addr Clusterのメンバーに対して、自身のアドレスとして通知する値
Private IPや、内部DNSの登録がある場合はDNS名で指定
cluster_addr Clusterのメンバーに対して、自身のアドレスとして通知する値
Private IPや、内部DNSの登録がある場合はDNS名で指定
disable_mlock Raft利用時は mlock 無効を推奨
enable_response_header_hostname httpヘッダーにhost名を出力(任意)
enable_response_header_raft_node_id httpヘッダーにnode_idを出力(任意)

※api_addr, cluster_addr は、サーバーごとに適切な値を指定

Vault Configuration

raft 設定項目 説明
path raft データを配置する場所
node_id node_id をわかりやすい名前で指定
省略時はUUIDとなる

※ node_id は、サーバーごとに適切な値を指定(省略でも可)

raft retry_join 設定項目 説明
leader_tls_servername 接続先ノードの証明書中のSANに登録されている値を指定
接続先が適切であるかチェックに使用
※この値は名前解決できる必要はありません
leader_api_addr 接続先 api_addr を指定
leader_ca_cert_file 接続先のサーバー証明書チェックに使うCA証明書
leader_client_cert_file 自身のクライアント証明書
leader_client_key_file クライアント証明書のキー

※retry_join 部分の設定は、3台とも共通設定で問題ありません

Integrated Storage (Raft) Backend

listener の設定

lister 設定項目 説明
address vault のAPIを受けるアドレス指定
cluster_address vault のCluste関連通信を受けるアドレス指定
(省略時は address のポートに1足した値)
tls_cert_file 自身のサーバー証明書
tls_key_file サーバー証明書のキー
tls_client_ca_file クライアント証明書のチェックに使うCA証明書

tcp Listener

vault-01 起動、raft 、HA の状態チェック

まず1台のホストで、Raftを使った構成で、vaultを起動し、初期化します

# Raftデータ用のディレクトリを作成
[ ! -d /opt/vault/raft ] && sudo mkdir /opt/vault/raft
sudo chown -R vault: /opt/vault/raft

# vault が raft ストレージで正常に起動することを確認
sudo systemctl start vault
vault status
Key                      Value
---                      -----
Recovery Seal Type       awskms
Initialized              false
Sealed                   true
(省略)
Storage Type             raft
HA Enabled               true

# 初期化を行う(発行された 情報は大切に保管)
vault operator init
vault login ******

# raft に、単一ノードで追加されていることを確認
vault operator raft list-peers
Node        Address                State       Voter
----        -------                -----       -----
vault-01    192.168.11.101:8201    leader      true


vault operator members
Host Name    API Address                  Cluster Address              Active Node    Version    Upgrade Version    Redundancy Zone    Last Echo
---------    -----------                  ---------------              -----------    -------    ---------------    ---------------    ---------
vault-01     https://192.168.11.101:8200  https://192.168.11.101:8201  true           1.13.2     1.13.2             n/a                n/a


# auto unseal で起動できることを確認
sudo systemctl restart vault
vault status

vault-02 起動、raft 、HA の状態チェック

2台目以降は、vaultを起動すれば、自動でClusterに参加できます。

# Raftデータ用のディレクトリを作成
[ ! -d /opt/vault/raft ] && sudo mkdir /opt/vault/raft
sudo chown -R vault: /opt/vault/raft

# vault を起動すると、自動的にRaftに追加され、すでに初期化済みとなる ※反映に数秒かかる
sudo systemctl start vault
vault status
Key                      Value
---                      -----
Recovery Seal Type       shamir
Initialized              true
Sealed                   false
(省略)
HA Enabled               true
HA Cluster               https://192.168.11.101:8201
HA Mode                  standby
Active Node Address      https://192.168.11.101:8200


# vault-01 で取得した root token でログイン
vault login ******

# raft 追加されていることを確認
vault operator raft list-peers
Node        Address                State       Voter
----        -------                -----       -----
vault-01    192.168.11.101:8201    leader      true
vault-02    192.168.11.102:8201    follower    false


vault operator members
Host Name    API Address                  Cluster Address              Active Node    Version    Upgrade Version    Redundancy Zone    Last Echo
---------    -----------                  ---------------              -----------    -------    ---------------    ---------------    ---------
vault-01     https://192.168.11.101:8200  https://192.168.11.101:8201  true           1.13.2     1.13.2             n/a                n/a
vault-02     https://192.168.11.102:8200  https://192.168.11.102:8201  false          1.13.2     1.13.2             n/a                2023-05-18T03:15:24+09:00

vault-03 起動、raft 、HA の状態チェック

# Raftデータ用のディレクトリを作成
[ ! -d /opt/vault/raft ] && sudo mkdir /opt/vault/raft
sudo chown -R vault: /opt/vault/raft

# vault を起動すると、自動的にRaftに追加される
sudo systemctl start vault
vault status

# vault-01 で取得した root token でログイン
vault login ******

# raft 追加されていることを確認
vault operator raft list-peers
Node        Address                State       Voter
----        -------                -----       -----
vault-01    192.168.11.101:8201    leader      true
vault-02    192.168.11.102:8201    follower    true
vault-03    192.168.11.103:8201    follower    false

vault operator members
Host Name    API Address                  Cluster Address              Active Node    Version    Upgrade Version    Redundancy Zone    Last Echo
---------    -----------                  ---------------              -----------    -------    ---------------    ---------------    ---------
vault-01     https://192.168.11.101:8200  https://192.168.11.101:8201  true           1.13.2     1.13.2             n/a                n/a
vault-02     https://192.168.11.102:8200  https://192.168.11.102:8201  false          1.13.2     1.13.2             n/a                2023-05-18T03:15:24+09:00
vault-03     https://192.168.11.103:8200  https://192.168.11.103:8201  false          1.13.2     1.13.2             n/a                2023-05-18T03:15:24+09:00

auto-joinを利用したCluster構成

上記の手段では、各メンバーホストのIPアドレス情報をあらかじめ入手する必要がありました。

別の手段として、auto-join 機能を用いることで、特定のタグの付いたメンバーホスト検出し、Raftに自動追加することも可能です。
例えば下記の設定ならば、各ホストとも完全に共通の設定とすることが可能です。

/etc/vault.d/vault.hcl
ui = true

api_addr      = "https://{{ GetPrivateIP }}:8200"
cluster_addr  = "https://{{ GetPrivateIP }}:8201"
disable_mlock = true

enable_response_header_hostname = true
enable_response_header_raft_node_id = true

storage "raft" {
  path    = "/opt/vault/raft"

  retry_join {
    auto_join = "provider=aws addr_type=private_v4 tag_key=Project tag_value=vault-dev_cluster region=ap-northeast-1"
    auto_join_scheme = "https"
    auto_join_port = 8200
    leader_ca_cert_file     = "/opt/vault/tls/ca.crt"
    leader_client_cert_file = "/opt/vault/tls/vault-cluster.crt"
    leader_client_key_file  = "/opt/vault/tls/vault-cluster.key"
  }
}

# HTTP listener
#listener "tcp" {
#  address = "0.0.0.0:8200"
#  cluster_address = "0.0.0.0:8201"
#  tls_disable = 1
#}

# HTTPS listener
listener "tcp" {
  address       = "0.0.0.0:8200"
  cluster_address = "0.0.0.0:8201"
  tls_cert_file = "/opt/vault/tls/vault-cluster.crt"
  tls_key_file  = "/opt/vault/tls/vault-cluster.key"
  tls_client_ca_file = "/opt/vault/tls/ca.crt"
}

# AWS KMS auto unseal
seal "awskms" {
  region = "ap-northeast-1"
  kms_key_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
項目 説明
api_addr
cluster_addr
{{ GetPrivateIP }}で、自身のPrivateIPを割り当てます
auto_join AWS利用時の設定例 EC2に割り当てたTagやregion を指定することで、該当EC2のPrivateIPを検出します
auto_join_scheme
auto_join_port
https, port 8200 での接続を試みます

Vault HA Cluster with Integrated Storage on AWS
go-sockaddr template
Integrated Storage (Raft) Backend
hashicorp/go-discover

auto-join を利用する場合も、マニュアル設定時と同様に、初回は1台のnodeのみ起動し、初期化を行った後、順次メンバーを追加してください。

ALBからのヘルスチェック

target group は下記のように設定してください。

  • Protocol: HTTPS
  • Port 8200
  • Health Check Path: /v1/sys/health

※ヘルスチェックは、Active Node の場合、200を返します。

【参考】FileからRaftへの移行

冒頭で説明したとおり、HA Cluster構成は、File ストレージでは利用できません。
既にFileストレージで利用中の場合は、File → Rafrへのデータ変換が必要です。

あらかじめ、上記マニュアル構成もしくはauto-join構成などを利用して、Raftの設定が追加済みであることを前提とします。
また、root token を使っての操作を前提とします。もしRoot Tokenを削除している場合は、RecoveryKeyから再生成してください。
OS側の操作は、rootユーザー で行います。(パーミッションがややこしいため)

# サービスは停止しておく
systemctl stop vault.service

# もしRaft path が存在しない場合はディレクトリ作成
[ ! -d /opt/vault/raft ] && mkdir /opt/vault/raft
chown -R vault: /opt/vault/raft

# もし既存のRaftデータが存在する場合は、削除しておく
rm -rf /opt/vault/raft/*

現在のディレクトリに、データ変換用ファイルmigrate.hclを作成します。

既存のFileストレージのデータが/opt/vault/data、Raft のデータが /opt/vault/raftの場合、下記のようになります。
(もし他サーバーで稼働していた場合は、Fileデータを、Clusterを構成するサーバーにコピーしてください)

migrate.hcl
# 変換元のFileデータのpath指定
storage_source "file" {
  path = "/opt/vault/data"
}

# 変換先のRaftデータのpath指定
storage_destination "raft" {
  path    = "/opt/vault/raft"
  node_id = "vault-01"
}

# 自ホストのcluster_addr
cluster_addr = "https://192.168.11.101:8201"

vaultコマンドで、上記ファイルを指定し、データ変換処理を行います。
※vault のサービスは停止状態のままにしてください。

vault operator migrate -config migrate.hcl
(省略)
Success! All of the keys have been migrated.

vaultのサービスではなく、コマンド実行ユーザー(本例ではrootユーザー)によるデータ変換操作です。
したがって、サービス停止状態で問題ありません

ファイルのownerが、root(変換コマンドを実行したユーザー)となっているため、ownerをvault(サービス起動ユーザー)に修正

# 変換されたデータを確認
ls -l /opt/vault/raft
# owner ユーザー/グループを vault に修正
chown -R vault: /opt/vault/raft

サービス起動、Raft で起動したことを確認

systemctl start vault.service

# 起動状態を確認
# データが格納されているため、Initialized true , Auto-unseal 設定により Sealed  false となっていることを確認
vault status

Key                      Value
---                      -----
Recovery Seal Type       shamir
Initialized              true
Sealed                   false
(省略)
Storage Type             raft
HA Enabled               true
HA Cluster               https://192.168.11.101:8201
HA Mode                  active
(省略)

もとのFileストレージのデータが移行されたことを確認

# もとのFileストレージで使用していた認証でログイン
vault login *****

# Raftが単一ノードで起動していることを確認
vault operator raft list-peers

# データ移行ができたことを確認
vault auth list
vault secrets list

あとは、Raft初期構成時と同様に、他のメンバーを追加してください。

# 他のノードも起動し、複数 node 構成となったことを確認
vault operator raft list-peers
Node        Address                State       Voter
----        -------                -----       -----
vault-01    192.168.11.101:8201    leader      true
vault-02    192.168.11.102:8201    follower    true
vault-03    192.168.11.103:8201    follower    true

root token や、recovery key も含め、まるごと移行できます

operator migrate

最後に

vault のclusterは、最初から必要機能がすべて備わっているため、設定のみで簡単に構築できます。
耐障害性を確保するためにも、Cluster構成を検討されてはいかがでしょうか?

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?