ロードバランサー経由の Vault クライアントのアクセス制御
Vaultを本番環境で利用する場合、リファレンスアーキテクチャーに沿った構成にして頂く事が推奨されています。
その場合、VaultクラスタのフロントエンドにL4のロードバランサーを置いて頂いて、Vaultクライアントからのリクエストをロードバランサー経由でVaultクラスタを構成する各ノードに振り分けて頂く構成にするのが一般的です。
ただ、ロードバランサーを経由したアクセスの場合、Vaultの設定を正しく行わないと、ロードバランサーからのリクエストとしてVaultに処理されてしまうため、実際のクライアントからのリクエストを正しく制御出来ない場合があります。
その部分をうまく制御するパラメータがVaultの設定ファイルのtcp
listenerスタンザで利用可能であるため、検証してみました。
Configuration
検証で利用したClient -> LoadBalancer -> Vaultの環境は、以下の様な構成にしています。
Client-LB-Vault Enterprise検証構成
Hostname | IP address |
---|---|
client |
10.0.101.5 |
traefik (vault.lab.demo ) |
10.0.1.70 |
vault1 |
10.0.1.10 |
vault2 |
10.0.2.10 |
vault3 |
10.0.3.10 |
client
からtraefik
, 3台のvault
サーバに対しては、SSHでアクセスできる様にしています。一方、client
からは3台のvault
サーバに対して、8200/tcp
で直接VaultのAPIエンドポイントにはアクセス出来ない様にしています。
また、traefik
から3台のvault
サーバには8200/tcp
でVaultのAPIエンドポイントにアクセスできる様にしており、client
からhttps://vault.lab.demo:8200
で来たリクエストをバックエンドのVaultサーバにロードバランスできる様にしています。
Vault configuration
vault1
のVaultの構成ファイルは以下の様に定義しています。
api_addr = "https://10.0.1.10:8200"
cluster_addr = "https://10.0.1.10:8201"
ui = true
license_path = "/etc/vault.d/vault.hclic"
listener "tcp" {
address = "0.0.0.0:8200"
cluster_address = "0.0.0.0:8201"
tls_client_ca_file = "/etc/vault.d/ca.pem"
tls_cert_file = "/etc/vault.d/vault-cert.pem"
tls_key_file = "/etc/vault.d/vault-private-key.pem"
x_forwarded_for_authorized_addrs = ["0.0.0.0/0"]
telemetry {
unauthenticated_metrics_access = "true"
}
}
storage "raft" {
path = "/var/lib/vault"
node_id = "vault1"
retry_join {
leader_api_addr = "https://10.0.1.10:8200"
leader_ca_cert_file = "/etc/vault.d/ca.pem"
leader_client_cert_file = "/etc/vault.d/vault-cert.pem"
leader_client_key_file = "/etc/vault.d/vault-private-key.pem"
leader_tls_servername = "vault.lab.demo"
}
retry_join {
leader_api_addr = "https://10.0.2.10:8200"
leader_ca_cert_file = "/etc/vault.d/ca.pem"
leader_client_cert_file = "/etc/vault.d/vault-cert.pem"
leader_client_key_file = "/etc/vault.d/vault-private-key.pem"
leader_tls_servername = "vault.lab.demo"
}
retry_join {
leader_api_addr = "https://10.0.3.10:8200"
leader_ca_cert_file = "/etc/vault.d/ca.pem"
leader_client_cert_file = "/etc/vault.d/vault-cert.pem"
leader_client_key_file = "/etc/vault.d/vault-private-key.pem"
leader_tls_servername = "vault.lab.demo"
}
}
telemetry {
disable_hostname = true
prometheus_retention_time = "12h"
}
今回の構成の場合、x_forwarded_for_authorized_addrs
が、Vaultクライアント側のIPアドレスを利用し、アクセス制御を行う上で必要なパラメータになります。
x_forwarded_for_authorized_addrs
にX-Forwarded-For
ヘッダーが信頼される送信元IP CIDRのリストを指定します。上の設定では0.0.0.0/0
としていますが、本来ロードバランサーのIP CIDR等適切な値を指定します。カンマ区切りのリスト、またはJSON配列で指定する事が出来ます。
x_forwarded_for_authorized_addrs
を指定する事で、指定したVaultのtcp
listenerでX-Forwarded-Forのサポートが有効になります。この設定を入れる事で、Vaultの監査ログのremote_address
フィールドに、ロードバランサー経由でアクセスしてきたVaultクライアントのIPアドレスが書き込まれ、Vaultクライアントが持つIPアドレスレンジを用いたアクセス制御が正しく行える様になります。
vault2
、vault3
も同じ様なVault設定ファイルを作成し、Vaultクラスタを構成しています。
$ vault operator raft list-peers
Node Address State Voter
---- ------- ----- -----
vault1 10.0.1.10:8201 leader true
vault2 10.0.2.10:8201 follower true
vault3 10.0.3.10:8201 follower true
デフォルトだと監査ログの設定がされていないため、leaderであるvault1
に対して、以下のコマンドを実行し、監査ログを有効にします。
vault audit enable syslog tag="vault" facility="AUTH"
この設定を行う事で、この環境においては、/var/log/syslog
と/var/log/auth.log
にVaultのログが出力される様になります。
クライアントからのAPIリクエストに関するログは、/var/log/auth.log
に出力されます。
grep -o '{.*}' /var/log/auth.log > json_auth_log.txt
cat json_auth_log.txt | jq -r .
{
"auth": {
"policy_results": {
"allowed": true
},
"token_type": "default"
},
"request": {
"data": {
"role_id": "hmac-sha256:567cdfc1f10ec09004c6982876c9c97e4eb950a40e9833bf3ca132d4758d6ec4",
"secret_id": "hmac-sha256:61ad43bc793b2e25371f5e29e55cfa86421e5c235f7336d0f1d12f69d43bd5fa"
},
"id": "127f0c69-0af1-1a1e-d844-fd3be055b90a",
"mount_accessor": "auth_approle_2430c318",
"mount_class": "auth",
"mount_point": "auth/test/",
"mount_running_version": "v1.17.2+builtin.vault",
"mount_type": "approle",
"namespace": {
"id": "root"
},
"operation": "update",
"path": "auth/test/login",
"remote_address": "10.0.1.70",
"remote_port": 53934
},
"time": "2024-08-01T00:50:27.007693637Z",
"type": "request"
}
Vault側の事前準備は以上です。
ロードバランサーが接続クライアントのIPアドレスをX-Forwarded-For
ヘッダーに送る必要がありますので、ロードバランサー側の設定も見てみます。
Traefik configuration
ロードバランサーとしてTraefikを利用しています。OSレベルでは今回利用する自己署名証明書を信頼する設定を入れていますが、コンテナレベルでは設定をしていないため、dynamic.toml
ファイルでinsecureSkipVerify = true
の設定をしています。
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.https]
address = ":443"
[entryPoints.vault]
address = ":8200"
[log]
level = "INFO"
[api]
dashboard = true
insecure = true
[accessLog]
filePath = "/var/log/traefik/access.log"
format = "json"
[providers.file]
filename = "/etc/traefik/dynamic.toml"
[entryPoints.vault.forwardedHeaders]
trustedIPs = ["0.0.0.0/0"]
http.services]
[http.services.vault.loadBalancer]
passHostHeader = true
serversTransport = "insecureTransport"
[[http.services.vault.loadBalancer.servers]]
url = "https://10.0.1.10:8200"
[[http.services.vault.loadBalancer.servers]]
url = "https://10.0.2.10:8200"
[[http.services.vault.loadBalancer.servers]]
url = "https://10.0.3.10:8200"
[http.routers]
[http.routers.vault]
rule = "Host(`vault.lab.demo`)"
entryPoints = ["vault"]
service = "vault"
[http.routers.vault.tls]
[tls]
[tls.stores]
[tls.stores.default]
[tls.stores.default.defaultCertificate]
certFile = "/cert/vault-cert.pem"
keyFile = "/cert/vault-private-key.pem"
[http.serversTransports]
[http.serversTransports.insecureTransport]
insecureSkipVerify = true
これら設定ファイルを利用して、DockerコンテナでTraefikをtraefik
サーバで動かしています。
docker run -d \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /home/ubuntu/lb/traefik.toml:/etc/traefik/traefik.toml \
-v /home/ubuntu/lb/dynamic.toml:/etc/traefik/dynamic.toml \
-v /home/ubuntu/certs/:/cert/ \
-v /var/log/traefik:/var/log/traefik \
-p 80:80 \
-p 443:443 \
-p 8080:8080 \
-p 8200:8200 \
--restart always \
--name traefik \
traefik:v2.5
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ecfbe5c6a31f traefik:v2.5 "/entrypoint.sh trae…" 3 seconds ago Up 2 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp, 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 0.0.0.0:8200->8200/tcp, :::8200->8200/tcp traefik
Test
テストを行う準備が整ったので、client
からtraefik
経由でVaultクラスタにリクエストを送ってみます。
export VAULT_ADDR=https://vault.lab.demo:8200
Vaultのステータスを確認するエンドポイントにリクエストをしてみます。
$ vault status
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 1
Threshold 1
Version 1.17.2+ent
Build Date 2024-07-05T15:19:27Z
Storage Type raft
Cluster Name vault-cluster-2d146662
Cluster ID 7570c5d1-3f52-08b6-f52a-3aaaecee55ef
HA Enabled true
HA Cluster https://10.0.1.10:8201
HA Mode active
Active Since 2024-07-31T09:44:49.315910551Z
Raft Committed Index 379878
Raft Applied Index 379878
Last WAL 145460
続いて、クライアントトークンが必要なリクエストを送ってみます。client
ノードには有効なクライアントトークンがVAULT_TOKEN
に設定されていないため、リクエストは拒否されます。
$ vault auth list
Error listing enabled authentications: Error making API request.
URL: GET https://vault.lab.demo:8200/v1/sys/auth
Code: 403. Errors:
* permission denied
Vaultクラスタのノードにログインし、監査ログを確認して、client
のIPアドレスがremote_access
として記録されている事を確認してみます。
{
"auth": {
"token_type": "default"
},
"error": "permission denied",
"request": {
"id": "b64462f2-eb99-8c09-6412-6eab0e37f7b5",
"mount_class": "secret",
"mount_point": "sys/",
"mount_running_version": "v1.17.2+builtin.vault",
"mount_type": "system",
"namespace": {
"id": "root"
},
"operation": "read",
"path": "sys/auth",
"remote_address": "10.0.101.5",
"remote_port": 58284
},
"time": "2024-08-02T02:29:49.710022647Z",
"type": "request"
}
{
"auth": {
"token_type": "default"
},
"error": "1 error occurred:\n\t* permission denied\n\n",
"request": {
"id": "b64462f2-eb99-8c09-6412-6eab0e37f7b5",
"mount_class": "secret",
"mount_point": "sys/",
"mount_running_version": "v1.17.2+builtin.vault",
"mount_type": "system",
"namespace": {
"id": "root"
},
"operation": "read",
"path": "sys/auth",
"remote_address": "10.0.101.5",
"remote_port": 58284
},
"response": {
"data": {
"error": "hmac-sha256:18e81b291988ad095b6e3672ca8ff5ba7f76c1af2cb22117ad60a7df58d5ab4b"
},
"mount_class": "secret",
"mount_point": "sys/",
"mount_running_plugin_version": "v1.17.2+builtin.vault",
"mount_type": "system"
},
"time": "2024-08-02T02:29:49.712106752Z",
"type": "response"
}
監査ログのremote_address
が10.0.101.5
となっている事が分かります。Vaultクラスタへのリクエストはロードバランサー(vault.lab.demo
)を経由していますが、その先にいるVaultクライアントのIPアドレスが監査ログにログされている事が分かります。
続いて、AppRole認証メソッドのIPアドレス制限を利用し、正しくアクセス制御出来るか確認してみます。
Configure AppRole auth method
AppRole認証メソッドに関しては、設定したロールに紐づくSecretIDの利用を特定のCIDRと紐付ける事が出来ます。client
が所属するIP CIDRを指定し、正しくコントロール出来るか確認してみます。
テスト用のKVストアとポリシーをVault providerを利用して以下の様に設定しています。
provider "vault" {}
resource "vault_mount" "kvv2" {
path = "test"
type = "kv"
options = { version = "2" }
description = "kv-v2 secrets engine for test"
}
resource "vault_kv_secret_v2" "fruits" {
mount = vault_mount.kvv2.path
name = "fruits"
cas = 1
delete_all_versions = true
data_json = jsonencode(
{
member1 = "apple",
member2 = "banana"
}
)
depends_on = [
vault_mount.kvv2
]
}
resource "vault_kv_secret_v2" "vegetables" {
mount = vault_mount.kvv2.path
name = "vegetables"
cas = 1
delete_all_versions = true
data_json = jsonencode(
{
member1 = "asparagus",
member2 = "broccoli",
member3 = "cabbage"
}
)
depends_on = [
vault_mount.kvv2
]
}
resource "vault_policy" "read_fruits" {
name = "read-fruits"
policy = <<EOT
path "test/data/fruits" {
capabilities = ["read"]
}
EOT
}
resource "vault_policy" "all_vegetables" {
name = "all-vegetables"
policy = <<EOT
path "test/data/vegetables" {
capabilities = ["sudo","read","create","update","delete","list","patch"]
}
EOT
}
また、AppRole認証メソッドに関しては、上記KVストアとポリシー設定とはステートファイルを分けた形で以下の様に設定しています。secret_id_bound_cidrs
とtoken_bound_cidrs
パラメーターでclient
サーバにアサインされているIPレンジを指定しています。
provider "vault" {}
variable "approle_path" {
description = "path name for approle auth method"
default = "test"
}
resource "vault_auth_backend" "approle" {
path = var.approle_path
type = "approle"
description = "approle auth method"
}
resource "vault_approle_auth_backend_role" "r1" {
backend = vault_auth_backend.approle.path
role_name = "tokyo"
secret_id_bound_cidrs = ["10.0.101.0/24"]
secret_id_num_uses = 5
secret_id_ttl = 300
token_policies = ["default", "read-fruits"]
token_ttl = 300
token_max_ttl = 600
token_bound_cidrs = ["10.0.101.0/24"]
}
data "vault_approle_auth_backend_role_id" "r1" {
backend = vault_auth_backend.approle.path
role_name = vault_approle_auth_backend_role.r1.role_name
}
output "tokyo_roleid" {
description = "The RoleID of the role: tokyo"
value = data.vault_approle_auth_backend_role_id.r1.role_id
}
それぞれ、Terraformのワークフローに則って、設定をVaultクラスタに反映させます。
反映させたら、AppRole認証メソッドのロールtokyo
の設定内容を確認します。
$ vault read auth/test/role/tokyo
Key Value
--- -----
bind_secret_id true
local_secret_ids false
secret_id_bound_cidrs [10.0.101.0/24]
secret_id_num_uses 5
secret_id_ttl 5m
token_bound_cidrs [10.0.101.0/24]
token_explicit_max_ttl 0s
token_max_ttl 10m
token_no_default_policy false
token_num_uses 0
token_period 0s
token_policies [default read-fruits]
token_ttl 5m
token_type default
次に、生成したtokyo
ロールのRoleIDをファイルに出力しておき、RoleIDを確認します。
terraform output -json tokyo_roleid | jq -r . > role-id/tokyo
$ cat role-id/tokyo
0aa31721-e697-e9eb-960e-4e7d407a70c9
Vaultクラスタが初期化される際に出力される初期ルートトークンを利用して、tokyo
ロールのSecretIDを動的に生成します。
$ vault write -f auth/test/role/tokyo/secret-id
Key Value
--- -----
secret_id 9bca8775-cfbe-40d4-cdce-8fde5ef7c359
secret_id_accessor 3b4c7b85-940c-0d4e-f96a-2f00f22fbbc8
secret_id_num_uses 5
secret_id_ttl 5m
AppRole認証メソッドを利用して、Vaultにログインするための情報が揃ったので、tokyo
ロールに設定したIP CIDRの制約が上手く機能するか確認していきます。
Test with AppRole auth method
client
サーバから、RoleIDとSecretIDを利用して、Vaultにログインし、問題なくオペレーション出来るか確認していきます。先ほどvault1
サーバで生成しておいた、RoleIDとSecretIDを環境変数に設定します。
export ROLE_ID_T="0aa31721-e697-e9eb-960e-4e7d407a70c9"
export SECRET_ID_T="9bca8775-cfbe-40d4-cdce-8fde5ef7c359"
AppRole認証メソッドtokyo
ロールを利用して、Vaultへログインします。
$ vault write auth/test/login role_id=$ROLE_ID_T secret_id=$SECRET_ID_T
Key Value
--- -----
token hvs.CAESIBwCiqiwCfjvHQ0WniyKwOiO6pnHgpstTIWTJsQRe_5XGiIKHGh2cy5rRkRjRk54bFlZTEg5YkVVcEtWWEVmQzgQuJ0J
token_accessor ZBkOpqaKQfbMNwEL2tOJgyz0
token_duration 5m
token_renewable true
token_policies ["default" "read-fruits"]
identity_policies []
policies ["default" "read-fruits"]
token_meta_role_name tokyo
無事にログインする事が出来ました。続いて、生成されたクライアントトークンを利用し、Vaultに対してオペレーションを行います。
export VAULT_TOKEN=hvs.CAESIBwCiqiwCfjvHQ0WniyKwOiO6pnHgpstTIWTJsQRe_5XGiIKHGh2cy5rRkRjRk54bFlZTEg5YkVVcEtWWEVmQzgQuJ0J
クライアントトークンに付与されたポリシーread-fruits
で定義されている通り、KVストアtest/fruits
に対するRead処理は成功し、test/vegetables
に対しては権限がないため、リクエストが通らない事が確認出来ます。
$ vault kv get test/fruits
== Secret Path ==
test/data/fruits
======= Metadata =======
Key Value
--- -----
created_time 2024-08-01T00:44:52.659101222Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1
===== Data =====
Key Value
--- -----
member1 apple
member2 banana
$ vault kv get test/vegetables
Error reading test/data/vegetables: Error making API request.
URL: GET https://vault.lab.demo:8200/v1/test/data/vegetables
Code: 403. Errors:
* 1 error occurred:
* permission denied
次に、traefik
サーバに移動して、同様にVaultに対してオペレーションを実施してみます。
Vaultサーバへリクエストが通る事を確認しておきます。
export VAULT_ADDR=https://10.0.1.10:8200
$ vault status
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 1
Threshold 1
Version 1.17.2+ent
Build Date 2024-07-05T15:19:27Z
Storage Type raft
Cluster Name vault-cluster-2d146662
Cluster ID 7570c5d1-3f52-08b6-f52a-3aaaecee55ef
HA Enabled true
HA Cluster https://10.0.1.10:8201
HA Mode active
Active Since 2024-07-31T09:44:49.315910551Z
Raft Committed Index 395080
Raft Applied Index 395080
Last WAL 151280
client
サーバで実施したのと同様に、RoleIDとSecretIDを環境変数に設定します。
export ROLE_ID_T="0aa31721-e697-e9eb-960e-4e7d407a70c9"
export SECRET_ID_T="9bca8775-cfbe-40d4-cdce-8fde5ef7c359"
tokyo
ロールでVaultへログインを試みてみます。
$ vault write auth/test/login role_id=$ROLE_ID_T secret_id=$SECRET_ID_T
Error writing data to auth/test/login: Error making API request.
URL: PUT https://10.0.1.10:8200/v1/auth/test/login
Code: 400. Errors:
* source address "10.0.1.70" unauthorized by CIDR restrictions on the role: %!w(<nil>)
認可されていないCIDR(traefik
サーバのIPアドレスは、10.0.1.70
)からのログインリクエストと言う事で、tokyo
ロールを用いたVaultへのログインが失敗します。
client
サーバ、traefik
サーバでのVaultクラスタへのログイン時の挙動から、tokyo
ロールで設定したsecret_id_bound_cidrs
が効いている事が分かります。
続いて、client
サーバで生成したクライアントトークン使って、traefik
サーバからオペレーションが出来るか確認してみます。
export VAULT_TOKEN=hvs.CAESIBwCiqiwCfjvHQ0WniyKwOiO6pnHgpstTIWTJsQRe_5XGiIKHGh2cy5rRkRjRk54bFlZTEg5YkVVcEtWWEVmQzgQuJ0J
$ vault kv get test/fruits
Error making API request.
URL: GET https://10.0.1.10:8200/v1/sys/internal/ui/mounts/test/fruits
Code: 403. Errors:
* permission denied
VAULT_TOKEN
に設定したクライアントトークンには、KVストアtest/fruits
に対するRead処理を行う権限が付与されていますが、クライアントトークン利用時の制約であるtoken_bound_cidrs
が効いて、traefik
サーバからは利用出来ない事が分かります。
Check vault audit log
それぞれのサーバからの確認が出来たので、vault
サーバで監査ログを確認してみます。client
サーバに関しては以下の通りです。remote_address
として、"remote_address": "10.0.101.5"
とログにも記録され、設定通りに動いている事が監査ログからも読み取れます。
clientからログイン時の監査ログ
{
"auth": {
"token_type": "default"
},
"forwarded_from": "10.0.3.10:8200",
"request": {
"data": {
"role_id": "hmac-sha256:567cdfc1f10ec09004c6982876c9c97e4eb950a40e9833bf3ca132d4758d6ec4",
"secret_id": "hmac-sha256:b6e0343cc897a30bec1abf0abeb00e320c58312d2ce583ca7193a622dc10fe00"
},
"id": "e8917848-53db-b2d7-4007-9370f214905a",
"mount_accessor": "auth_approle_2430c318",
"mount_point": "auth/test/",
"mount_type": "approle",
"namespace": {
"id": "root"
},
"operation": "update",
"path": "auth/test/login",
"remote_address": "10.0.101.5",
"remote_port": 35278,
"replication_cluster": "855a7ea2-2367-2449-345c-a96e8fcd0bdb"
},
"time": "2024-08-02T04:06:36.273137645Z",
"type": "request"
}
{
"auth": {
"token_type": "default"
},
"forwarded": true,
"request": {
"data": {
"role_id": "hmac-sha256:567cdfc1f10ec09004c6982876c9c97e4eb950a40e9833bf3ca132d4758d6ec4",
"secret_id": "hmac-sha256:b6e0343cc897a30bec1abf0abeb00e320c58312d2ce583ca7193a622dc10fe00"
},
"id": "e8917848-53db-b2d7-4007-9370f214905a",
"mount_accessor": "auth_approle_2430c318",
"mount_class": "auth",
"mount_point": "auth/test/",
"mount_running_version": "v1.17.2+builtin.vault",
"mount_type": "approle",
"namespace": {
"id": "root"
},
"operation": "update",
"path": "auth/test/login",
"remote_address": "10.0.101.5",
"remote_port": 35278,
"replication_cluster": "855a7ea2-2367-2449-345c-a96e8fcd0bdb"
},
"response": {
"auth": {
"metadata": {
"role_name": "tokyo"
},
"policies": [
"default",
"read-fruits"
],
"token_ttl": 300,
"token_type": "service"
},
"mount_accessor": "auth_approle_2430c318",
"mount_class": "auth",
"mount_point": "auth/test/",
"mount_running_plugin_version": "v1.17.2+builtin.vault",
"mount_type": "approle"
},
"time": "2024-08-02T04:06:36.282362164Z",
"type": "response"
}
clientからtest/fruitsへreadリクエスト時の監査ログ
{
"auth": {
"accessor": "hmac-sha256:cb0b4fd5a155dea744491bd9467b0c1a5bd474f3c62836e9625583d402576776",
"client_token": "hmac-sha256:8647a21b17af1104f4be9c16abf5762f4f23845841f73fa956ed50a18a4bb25d",
"display_name": "test",
"entity_id": "6ffffcd0-6720-9bf4-1709-a8b8facfd111",
"metadata": {
"role_name": "tokyo"
},
"policies": [
"default",
"read-fruits"
],
"policy_results": {
"allowed": true
},
"token_policies": [
"default",
"read-fruits"
],
"token_issue_time": "2024-08-02T04:06:36Z",
"token_ttl": 300,
"token_type": "service"
},
"request": {
"client_id": "6ffffcd0-6720-9bf4-1709-a8b8facfd111",
"client_token": "hmac-sha256:01c08ece7fbde3a76019f2c67fa79a93e28c7b782b28626a0467ca76609f4452",
"client_token_accessor": "hmac-sha256:cb0b4fd5a155dea744491bd9467b0c1a5bd474f3c62836e9625583d402576776",
"id": "896b92c8-0aa8-4db2-81ab-886afa5d1a5b",
"mount_class": "secret",
"mount_point": "sys/",
"mount_running_version": "v1.17.2+builtin.vault",
"mount_type": "system",
"namespace": {
"id": "root"
},
"operation": "read",
"path": "sys/internal/ui/mounts/test/fruits",
"remote_address": "10.0.101.5",
"remote_port": 33702
},
"time": "2024-08-02T04:06:55.449515847Z",
"type": "request"
}
{
"auth": {
"accessor": "hmac-sha256:cb0b4fd5a155dea744491bd9467b0c1a5bd474f3c62836e9625583d402576776",
"client_token": "hmac-sha256:8647a21b17af1104f4be9c16abf5762f4f23845841f73fa956ed50a18a4bb25d",
"display_name": "test",
"entity_id": "6ffffcd0-6720-9bf4-1709-a8b8facfd111",
"metadata": {
"role_name": "tokyo"
},
"policies": [
"default",
"read-fruits"
],
"policy_results": {
"allowed": true
},
"token_policies": [
"default",
"read-fruits"
],
"token_issue_time": "2024-08-02T04:06:36Z",
"token_ttl": 300,
"token_type": "service"
},
"request": {
"client_id": "6ffffcd0-6720-9bf4-1709-a8b8facfd111",
"client_token": "hmac-sha256:01c08ece7fbde3a76019f2c67fa79a93e28c7b782b28626a0467ca76609f4452",
"client_token_accessor": "hmac-sha256:cb0b4fd5a155dea744491bd9467b0c1a5bd474f3c62836e9625583d402576776",
"id": "896b92c8-0aa8-4db2-81ab-886afa5d1a5b",
"mount_accessor": "system_dcfeebaa",
"mount_class": "secret",
"mount_point": "sys/",
"mount_running_version": "v1.17.2+builtin.vault",
"mount_type": "system",
"namespace": {
"id": "root"
},
"operation": "read",
"path": "sys/internal/ui/mounts/test/fruits",
"remote_address": "10.0.101.5",
"remote_port": 33702
},
"response": {
"data": {
"accessor": "hmac-sha256:16f4feca2fc475e4467ae9579e8ee5641a6cb844294e21c8ade9bb42bc40c87c",
"config": {
"default_lease_ttl": 2764800,
"force_no_cache": false,
"max_lease_ttl": 2764800
},
"deprecation_status": "hmac-sha256:88bf0ee43f76a41a1be75710639a2ce879f2ac594d39e52ea90e7aaf22e77a5e",
"description": "hmac-sha256:2e12fc4df650fd7175f0f09c6f7c47d5f3c3ad716af024ae49dba00384c31722",
"external_entropy_access": false,
"local": false,
"options": {
"version": "hmac-sha256:255b6fbcea9b2af09ebc8eb7ae30718462d5f1dbd85025efcf651950a436ae0b"
},
"path": "hmac-sha256:2b177b0c4c0a8b1fb69da9a92a9e3e345226665acb22b32530e06a44ea446f53",
"plugin_version": "hmac-sha256:69154b9e70228ed55173426b2de2522c5bb3c8bb5352a45df3d93cd1b5700b34",
"running_plugin_version": "hmac-sha256:594008bac65bc3f095ac5c2fd929a78a4e18a213b9d3cfd78e84e7a51ce4a6d9",
"running_sha256": "hmac-sha256:69154b9e70228ed55173426b2de2522c5bb3c8bb5352a45df3d93cd1b5700b34",
"seal_wrap": false,
"type": "hmac-sha256:78ad9f0d136001883f45858260115818ecd7c49477162fc160d02a4a44c9170e",
"uuid": "hmac-sha256:513cf1b91eb032dfb75854272c4384cf662b46c55fd7a433bd3fb3d8b1926c10"
},
"mount_accessor": "system_dcfeebaa",
"mount_class": "secret",
"mount_point": "sys/",
"mount_running_plugin_version": "v1.17.2+builtin.vault",
"mount_type": "system"
},
"time": "2024-08-02T04:06:55.452080207Z",
"type": "response"
}
clientからtest/vegetablesへreadリクエスト時の監査ログ
{
"auth": {
"accessor": "hmac-sha256:cb0b4fd5a155dea744491bd9467b0c1a5bd474f3c62836e9625583d402576776",
"client_token": "hmac-sha256:8647a21b17af1104f4be9c16abf5762f4f23845841f73fa956ed50a18a4bb25d",
"display_name": "test",
"entity_id": "6ffffcd0-6720-9bf4-1709-a8b8facfd111",
"metadata": {
"role_name": "tokyo"
},
"policies": [
"default",
"read-fruits"
],
"policy_results": {
"allowed": false
},
"token_policies": [
"default",
"read-fruits"
],
"token_issue_time": "2024-08-02T04:06:36Z",
"token_ttl": 300,
"token_type": "service"
},
"error": "1 error occurred:\n\t* permission denied\n\n",
"request": {
"client_id": "6ffffcd0-6720-9bf4-1709-a8b8facfd111",
"client_token": "hmac-sha256:01c08ece7fbde3a76019f2c67fa79a93e28c7b782b28626a0467ca76609f4452",
"client_token_accessor": "hmac-sha256:cb0b4fd5a155dea744491bd9467b0c1a5bd474f3c62836e9625583d402576776",
"id": "11bcc248-532e-d382-cbde-7fba2222c422",
"mount_class": "secret",
"mount_point": "test/",
"mount_running_version": "v0.19.0+builtin",
"mount_type": "kv",
"namespace": {
"id": "root"
},
"operation": "read",
"path": "test/data/vegetables",
"remote_address": "10.0.101.5",
"remote_port": 33702
},
"time": "2024-08-02T04:07:00.402109044Z",
"type": "request"
}
{
"auth": {
"accessor": "hmac-sha256:cb0b4fd5a155dea744491bd9467b0c1a5bd474f3c62836e9625583d402576776",
"client_token": "hmac-sha256:8647a21b17af1104f4be9c16abf5762f4f23845841f73fa956ed50a18a4bb25d",
"display_name": "test",
"entity_id": "6ffffcd0-6720-9bf4-1709-a8b8facfd111",
"metadata": {
"role_name": "tokyo"
},
"policies": [
"default",
"read-fruits"
],
"policy_results": {
"allowed": false
},
"token_policies": [
"default",
"read-fruits"
],
"token_issue_time": "2024-08-02T04:06:36Z",
"token_ttl": 300,
"token_type": "service"
},
"error": "1 error occurred:\n\t* permission denied\n\n",
"request": {
"client_id": "6ffffcd0-6720-9bf4-1709-a8b8facfd111",
"client_token": "hmac-sha256:01c08ece7fbde3a76019f2c67fa79a93e28c7b782b28626a0467ca76609f4452",
"client_token_accessor": "hmac-sha256:cb0b4fd5a155dea744491bd9467b0c1a5bd474f3c62836e9625583d402576776",
"id": "11bcc248-532e-d382-cbde-7fba2222c422",
"mount_class": "secret",
"mount_point": "test/",
"mount_running_version": "v0.19.0+builtin",
"mount_type": "kv",
"namespace": {
"id": "root"
},
"operation": "read",
"path": "test/data/vegetables",
"remote_address": "10.0.101.5",
"remote_port": 33702
},
"response": {
"data": {
"error": "hmac-sha256:4251ad8d96c56eb038ff67d8a104bd6b83ab7e4744352f280bef4acfa40bae35"
},
"mount_class": "secret",
"mount_point": "test/",
"mount_running_plugin_version": "v0.19.0+builtin",
"mount_type": "kv"
},
"time": "2024-08-02T04:07:00.402523849Z",
"type": "response"
}
traefik
サーバに関しては以下の通りです。remote_address
として、"remote_address": "10.0.1.70"
とログに記録され、AppRole認証メソッドのtokyo
ロールに設定したIP CIDRの制約のため、エラーがレスポンスされている事が読み取れます。
traefikからログイン時の監査ログ
{
"auth": {
"policy_results": {
"allowed": true
},
"token_type": "default"
},
"request": {
"data": {
"role_id": "hmac-sha256:567cdfc1f10ec09004c6982876c9c97e4eb950a40e9833bf3ca132d4758d6ec4",
"secret_id": "hmac-sha256:b6e0343cc897a30bec1abf0abeb00e320c58312d2ce583ca7193a622dc10fe00"
},
"id": "1417d98c-8729-cc12-9a63-ea210221c91b",
"mount_accessor": "auth_approle_2430c318",
"mount_class": "auth",
"mount_point": "auth/test/",
"mount_running_version": "v1.17.2+builtin.vault",
"mount_type": "approle",
"namespace": {
"id": "root"
},
"operation": "update",
"path": "auth/test/login",
"remote_address": "10.0.1.70",
"remote_port": 38114
},
"time": "2024-08-02T04:07:54.931654443Z",
"type": "request"
}
{
"auth": {
"policy_results": {
"allowed": true
},
"token_type": "default"
},
"request": {
"data": {
"role_id": "hmac-sha256:567cdfc1f10ec09004c6982876c9c97e4eb950a40e9833bf3ca132d4758d6ec4",
"secret_id": "hmac-sha256:b6e0343cc897a30bec1abf0abeb00e320c58312d2ce583ca7193a622dc10fe00"
},
"id": "1417d98c-8729-cc12-9a63-ea210221c91b",
"mount_accessor": "auth_approle_2430c318",
"mount_class": "auth",
"mount_point": "auth/test/",
"mount_running_version": "v1.17.2+builtin.vault",
"mount_type": "approle",
"namespace": {
"id": "root"
},
"operation": "update",
"path": "auth/test/login",
"remote_address": "10.0.1.70",
"remote_port": 38114
},
"response": {
"data": {
"error": "hmac-sha256:3487ea3db5b8f4c90438de840c8bd209411f32d421a8f7ad8da83912fd38dba7"
},
"mount_accessor": "auth_approle_2430c318",
"mount_class": "auth",
"mount_point": "auth/test/",
"mount_running_plugin_version": "v1.17.2+builtin.vault",
"mount_type": "approle"
},
"time": "2024-08-02T04:07:54.941065121Z",
"type": "response"
}
traefikからclientで生成したクライアントトークンを用いたオペレーション時の監査ログ
{
"auth": {
"accessor": "hmac-sha256:cb0b4fd5a155dea744491bd9467b0c1a5bd474f3c62836e9625583d402576776",
"client_token": "hmac-sha256:8647a21b17af1104f4be9c16abf5762f4f23845841f73fa956ed50a18a4bb25d",
"policy_results": {
"allowed": true
},
"token_type": "default"
},
"request": {
"client_token": "hmac-sha256:01c08ece7fbde3a76019f2c67fa79a93e28c7b782b28626a0467ca76609f4452",
"client_token_accessor": "hmac-sha256:cb0b4fd5a155dea744491bd9467b0c1a5bd474f3c62836e9625583d402576776",
"id": "47020eec-a386-c9e1-6675-313506bee18c",
"mount_class": "secret",
"mount_point": "sys/",
"mount_running_version": "v1.17.2+builtin.vault",
"mount_type": "system",
"namespace": {
"id": "root"
},
"operation": "read",
"path": "sys/internal/ui/mounts/test/fruits",
"remote_address": "10.0.1.70",
"remote_port": 38974
},
"time": "2024-08-02T04:08:10.63952107Z",
"type": "request"
}
{
"auth": {
"accessor": "hmac-sha256:cb0b4fd5a155dea744491bd9467b0c1a5bd474f3c62836e9625583d402576776",
"client_token": "hmac-sha256:8647a21b17af1104f4be9c16abf5762f4f23845841f73fa956ed50a18a4bb25d",
"policy_results": {
"allowed": true
},
"token_type": "default"
},
"error": "permission denied",
"request": {
"client_token": "hmac-sha256:01c08ece7fbde3a76019f2c67fa79a93e28c7b782b28626a0467ca76609f4452",
"client_token_accessor": "hmac-sha256:cb0b4fd5a155dea744491bd9467b0c1a5bd474f3c62836e9625583d402576776",
"id": "47020eec-a386-c9e1-6675-313506bee18c",
"mount_accessor": "system_dcfeebaa",
"mount_class": "secret",
"mount_point": "sys/",
"mount_running_version": "v1.17.2+builtin.vault",
"mount_type": "system",
"namespace": {
"id": "root"
},
"operation": "read",
"path": "sys/internal/ui/mounts/test/fruits",
"remote_address": "10.0.1.70",
"remote_port": 38974
},
"response": {
"mount_accessor": "system_dcfeebaa",
"mount_class": "secret",
"mount_point": "sys/",
"mount_running_plugin_version": "v1.17.2+builtin.vault",
"mount_type": "system"
},
"time": "2024-08-02T04:08:10.640096347Z",
"type": "response"
}
この様にロードバランサー越しにVaultクライアントからVaultクラスタにアクセスする場合においても、VaultクライアントのIPアドレスを用いて、アクセス制御を行える事が確認できました。
使えそうな部分があれば、参考にしてみてください!
x_forwarded_for_authorized_addrs
setting
(Optional) without 以下の様に、x_forwarded_for_authorized_addrs
をVaultの設定ファイルが削除した場合に、どのような挙動になるか確認しておきます。
api_addr = "https://10.0.1.10:8200"
cluster_addr = "https://10.0.1.10:8201"
ui = true
license_path = "/etc/vault.d/vault.hclic"
listener "tcp" {
address = "0.0.0.0:8200"
cluster_address = "0.0.0.0:8201"
tls_client_ca_file = "/etc/vault.d/ca.pem"
tls_cert_file = "/etc/vault.d/vault-cert.pem"
tls_key_file = "/etc/vault.d/vault-private-key.pem"
telemetry {
unauthenticated_metrics_access = "true"
}
}
storage "raft" {
path = "/var/lib/vault"
node_id = "vault1"
retry_join {
leader_api_addr = "https://10.0.1.10:8200"
leader_ca_cert_file = "/etc/vault.d/ca.pem"
leader_client_cert_file = "/etc/vault.d/vault-cert.pem"
leader_client_key_file = "/etc/vault.d/vault-private-key.pem"
leader_tls_servername = "vault.lab.demo"
}
retry_join {
leader_api_addr = "https://10.0.2.10:8200"
leader_ca_cert_file = "/etc/vault.d/ca.pem"
leader_client_cert_file = "/etc/vault.d/vault-cert.pem"
leader_client_key_file = "/etc/vault.d/vault-private-key.pem"
leader_tls_servername = "vault.lab.demo"
}
retry_join {
leader_api_addr = "https://10.0.3.10:8200"
leader_ca_cert_file = "/etc/vault.d/ca.pem"
leader_client_cert_file = "/etc/vault.d/vault-cert.pem"
leader_client_key_file = "/etc/vault.d/vault-private-key.pem"
leader_tls_servername = "vault.lab.demo"
}
}
telemetry {
disable_hostname = true
prometheus_retention_time = "12h"
}
前述のテスト同様、AppRole認証メソッドのtokyo
ロールに紐づくRoleIDとSecretIDを準備して、それを用いてclient
サーバからログインしてみます。
export VAULT_ADDR=https://vault.lab.demo:8200
export ROLE_ID_T=0aa31721-e697-e9eb-960e-4e7d407a70c9
export SECRET_ID_T=dc23d22f-4e18-e170-2405-e333bcef1ebd
$ vault write auth/test/login role_id=$ROLE_ID_T secret_id=$SECRET_ID_T
Error writing data to auth/test/login: Error making API request.
URL: PUT https://vault.lab.demo:8200/v1/auth/test/login
Code: 400. Errors:
* source address "10.0.1.70" unauthorized by CIDR restrictions on the role: %!w(<nil>)
client
(10.0.101.5/24
)からログインしていますが、ロードバランサーであるtraefik
(10.0.1.70/24
)からのログインとして、Vaultには認識され、tokyo
ロールでログインする事が出来なくなっています。
vault
サーバで監査ログを確認してみます。client
サーバからログインを試みていますが、監査ログに記録されている"remote_address":
は、ロードバランサーの"10.0.1.70"
となっています。
clientからAppRoleでログインしようと際の監査ログ
{
"auth": {
"token_type": "default"
},
"forwarded_from": "10.0.2.10:8200",
"request": {
"data": {
"role_id": "hmac-sha256:567cdfc1f10ec09004c6982876c9c97e4eb950a40e9833bf3ca132d4758d6ec4",
"secret_id": "hmac-sha256:0633b0e27b11427df6f4697507db65e2276aca7266174fd65ff03e7c405796fe"
},
"id": "bc939b9e-9469-2737-a50f-fa72b7b2d509",
"mount_accessor": "auth_approle_2430c318",
"mount_point": "auth/test/",
"mount_type": "approle",
"namespace": {
"id": "root"
},
"operation": "update",
"path": "auth/test/login",
"remote_address": "10.0.1.70",
"remote_port": 35066,
"replication_cluster": "08a0256f-5b7a-e2f1-aa0e-86a181e42af9"
},
"time": "2024-08-02T06:45:24.639776869Z",
"type": "request"
}
{
"auth": {
"token_type": "default"
},
"forwarded": true,
"request": {
"data": {
"role_id": "hmac-sha256:567cdfc1f10ec09004c6982876c9c97e4eb950a40e9833bf3ca132d4758d6ec4",
"secret_id": "hmac-sha256:0633b0e27b11427df6f4697507db65e2276aca7266174fd65ff03e7c405796fe"
},
"id": "bc939b9e-9469-2737-a50f-fa72b7b2d509",
"mount_accessor": "auth_approle_2430c318",
"mount_class": "auth",
"mount_point": "auth/test/",
"mount_running_version": "v1.17.2+builtin.vault",
"mount_type": "approle",
"namespace": {
"id": "root"
},
"operation": "update",
"path": "auth/test/login",
"remote_address": "10.0.1.70",
"remote_port": 35066,
"replication_cluster": "08a0256f-5b7a-e2f1-aa0e-86a181e42af9"
},
"response": {
"data": {
"error": "hmac-sha256:3487ea3db5b8f4c90438de840c8bd209411f32d421a8f7ad8da83912fd38dba7"
},
"mount_accessor": "auth_approle_2430c318",
"mount_class": "auth",
"mount_point": "auth/test/",
"mount_running_plugin_version": "v1.17.2+builtin.vault",
"mount_type": "approle"
},
"time": "2024-08-02T06:45:24.64938285Z",
"type": "response"
}
ロードバランサーを経由して、Vaultクラスタにアクセスする場合、VaultクライアントのIPアドレスを利用して、アクセス制御等を行う場合は、x_forwarded_for_authorized_addrs
設定が必要となる事が分かります。
Discussion