From 392412829bc95ec8707f8244ad69e51da2061401 Mon Sep 17 00:00:00 2001 From: Ryan Cragun Date: Mon, 9 Sep 2024 14:29:11 -0600 Subject: [PATCH] [VAULT-30189] enos: verify identity and OIDC tokens (#28274) * [VAULT-30189] enos: verify identity and OIDC tokens Expand our baseline API and data verification by including the identity and identity OIDC tokens secrets engines. We now create a test entity, entity-alias, identity group, various policies, and associate them with the entity. For the OIDC side, we now configure the OIDC issuer, create and rotate named keys, create and associate roles with the named key, and issue and introspect tokens. During a second phase we also verify that the those some entities, groups, keys, roles, config, etc all exist with the expected values. This is useful to test durability after upgrades, migrations, etc. This change also includes new updates our prior `auth/userpass` and `kv` verification. We had two modules that were loosely coupled and interdependent. This restructures those both into a singular module with child modules and fixes the assumed values by requiring the read module to verify against the created state. Going forward we can continue to extend this secrets engine verification module with additional create and read checks for new secrets engines. Signed-off-by: Ryan Cragun --- enos/Makefile | 4 +- enos/enos-descriptions.hcl | 28 +- enos/enos-dev-scenario-pr-replication.hcl | 2 +- enos/enos-modules.hcl | 42 +- enos/enos-qualities.hcl | 83 +++- enos/enos-scenario-agent.hcl | 45 ++- enos/enos-scenario-autopilot.hcl | 45 ++- enos/enos-scenario-dr-replication.hcl | 89 +++- enos/enos-scenario-pr-replication.hcl | 59 ++- enos/enos-scenario-proxy.hcl | 43 +- enos/enos-scenario-seal-ha.hcl | 61 ++- enos/enos-scenario-smoke.hcl | 43 +- enos/enos-scenario-upgrade.hcl | 48 ++- .../scripts/verify-data.sh | 38 -- enos/modules/vault_verify_write_data/main.tf | 87 ---- .../scripts/smoke-enable-secrets-kv.sh | 54 --- .../modules/create/auth.tf | 145 +++++++ .../modules/create/identity.tf | 380 ++++++++++++++++++ .../modules/create/kv.tf | 126 ++++++ .../modules/create/main.tf | 53 +++ .../modules/read/auth.tf | 24 ++ .../modules/read/identity.tf | 56 +++ .../verify_secrets_engines/modules/read/kv.tf | 24 ++ .../modules/read}/main.tf | 27 +- .../scripts/auth-enable.sh | 22 + .../scripts/auth-userpass-login.sh | 22 + .../scripts/auth-userpass-write.sh} | 29 +- .../scripts/identity-oidc-introspect-token.sh | 33 ++ .../scripts/identity-verify-entity.sh | 43 ++ .../scripts/identity-verify-oidc.sh | 63 +++ .../verify_secrets_engines/scripts/kv-put.sh | 25 ++ .../scripts/kv-verify-value.sh | 32 ++ .../scripts/policy-write.sh | 22 + .../verify_secrets_engines/scripts/read.sh | 21 + .../scripts/secrets-enable.sh | 22 + .../scripts/write-payload.sh | 26 ++ 36 files changed, 1615 insertions(+), 351 deletions(-) delete mode 100644 enos/modules/vault_verify_read_data/scripts/verify-data.sh delete mode 100644 enos/modules/vault_verify_write_data/main.tf delete mode 100644 enos/modules/vault_verify_write_data/scripts/smoke-enable-secrets-kv.sh create mode 100644 enos/modules/verify_secrets_engines/modules/create/auth.tf create mode 100644 enos/modules/verify_secrets_engines/modules/create/identity.tf create mode 100644 enos/modules/verify_secrets_engines/modules/create/kv.tf create mode 100644 enos/modules/verify_secrets_engines/modules/create/main.tf create mode 100644 enos/modules/verify_secrets_engines/modules/read/auth.tf create mode 100644 enos/modules/verify_secrets_engines/modules/read/identity.tf create mode 100644 enos/modules/verify_secrets_engines/modules/read/kv.tf rename enos/modules/{vault_verify_read_data => verify_secrets_engines/modules/read}/main.tf (68%) create mode 100644 enos/modules/verify_secrets_engines/scripts/auth-enable.sh create mode 100644 enos/modules/verify_secrets_engines/scripts/auth-userpass-login.sh rename enos/modules/{vault_verify_write_data/scripts/smoke-write-test-data.sh => verify_secrets_engines/scripts/auth-userpass-write.sh} (51%) create mode 100644 enos/modules/verify_secrets_engines/scripts/identity-oidc-introspect-token.sh create mode 100644 enos/modules/verify_secrets_engines/scripts/identity-verify-entity.sh create mode 100644 enos/modules/verify_secrets_engines/scripts/identity-verify-oidc.sh create mode 100644 enos/modules/verify_secrets_engines/scripts/kv-put.sh create mode 100644 enos/modules/verify_secrets_engines/scripts/kv-verify-value.sh create mode 100644 enos/modules/verify_secrets_engines/scripts/policy-write.sh create mode 100644 enos/modules/verify_secrets_engines/scripts/read.sh create mode 100644 enos/modules/verify_secrets_engines/scripts/secrets-enable.sh create mode 100644 enos/modules/verify_secrets_engines/scripts/write-payload.sh diff --git a/enos/Makefile b/enos/Makefile index 3afcf0efaf8c..4a5532b2174a 100644 --- a/enos/Makefile +++ b/enos/Makefile @@ -2,10 +2,10 @@ default: check-fmt shellcheck .PHONY: check-fmt -check-fmt: check-fmt-enos check-fmt-modules +check-fmt: check-fmt-enos check-fmt-modules check-shfmt .PHONY: fmt -fmt: fmt-enos fmt-modules +fmt: fmt-enos fmt-modules shfmt .PHONY: check-fmt-enos check-fmt-enos: diff --git a/enos/enos-descriptions.hcl b/enos/enos-descriptions.hcl index f0d5880fe458..6ec60150ecc8 100644 --- a/enos/enos-descriptions.hcl +++ b/enos/enos-descriptions.hcl @@ -126,12 +126,6 @@ globals { 'await-server-removal'. EOF - verify_read_test_data = <<-EOF - Verify that we are able to read test data we've written in prior steps. This includes: - - Auth user policies - - Kv data - EOF - verify_replication_status = <<-EOF Verify that the default replication status is correct depending on the edition of Vault that been deployed. When testing a Community Edition of Vault we'll ensure that replication is not @@ -163,12 +157,22 @@ globals { Vault's reported seal type matches our configuration. EOF - verify_write_test_data = <<-EOF - Verify that vault is capable mounting engines and writing data to them. These currently include: - - Mount the auth engine - - Mount the kv engine - - Write auth user policies - - Write kv data + verify_secrets_engines_create = <<-EOF + Verify that Vault is capable mounting, configuring, and using various secrets engines and auth + methods. These currently include: + - v1/auth/userpass/* + - v1/identity/* + - v1/kv/* + - v1/sys/policy/* + EOF + + verify_secrets_engines_read = <<-EOF + Verify that data that we've created previously is still valid, consistent, and duarable. + This includes: + - v1/auth/userpass/* + - v1/identity/* + - v1/kv/* + - v1/sys/policy/* EOF verify_ui = <<-EOF diff --git a/enos/enos-dev-scenario-pr-replication.hcl b/enos/enos-dev-scenario-pr-replication.hcl index 0a4f0494a18d..f184937876d1 100644 --- a/enos/enos-dev-scenario-pr-replication.hcl +++ b/enos/enos-dev-scenario-pr-replication.hcl @@ -722,7 +722,7 @@ scenario "dev_pr_replication" { description = <<-EOF Enable the auth userpass method and create a new user. EOF - module = module.vault_verify_write_data + module = module.vault_verify_secrets_engines_create depends_on = [step.get_primary_cluster_ips] diff --git a/enos/enos-modules.hcl b/enos/enos-modules.hcl index 137e1245967a..3c776c0f7dc5 100644 --- a/enos/enos-modules.hcl +++ b/enos/enos-modules.hcl @@ -281,13 +281,17 @@ module "vault_verify_dr_replication" { vault_install_dir = var.vault_install_dir } -module "vault_verify_raft_auto_join_voter" { - source = "./modules/vault_verify_raft_auto_join_voter" +module "vault_verify_secrets_engines_create" { + source = "./modules/verify_secrets_engines/modules/create" - vault_install_dir = var.vault_install_dir - vault_cluster_addr_port = global.ports["vault_cluster"]["port"] + vault_install_dir = var.vault_install_dir } +module "vault_verify_secrets_engines_read" { + source = "./modules/verify_secrets_engines/modules/read" + + vault_install_dir = var.vault_install_dir +} module "vault_verify_default_lcq" { source = "./modules/vault_verify_default_lcq" @@ -295,32 +299,21 @@ module "vault_verify_default_lcq" { vault_autopilot_default_max_leases = "300000" } -module "vault_verify_replication" { - source = "./modules/vault_verify_replication" -} - -module "vault_verify_read_data" { - source = "./modules/vault_verify_read_data" - - vault_install_dir = var.vault_install_dir -} - module "vault_verify_performance_replication" { source = "./modules/vault_verify_performance_replication" vault_install_dir = var.vault_install_dir } -module "vault_verify_version" { - source = "./modules/vault_verify_version" +module "vault_verify_raft_auto_join_voter" { + source = "./modules/vault_verify_raft_auto_join_voter" - vault_install_dir = var.vault_install_dir + vault_install_dir = var.vault_install_dir + vault_cluster_addr_port = global.ports["vault_cluster"]["port"] } -module "vault_verify_write_data" { - source = "./modules/vault_verify_write_data" - - vault_install_dir = var.vault_install_dir +module "vault_verify_replication" { + source = "./modules/vault_verify_replication" } module "vault_verify_ui" { @@ -339,6 +332,12 @@ module "vault_verify_unsealed" { vault_install_dir = var.vault_install_dir } +module "vault_verify_version" { + source = "./modules/vault_verify_version" + + vault_install_dir = var.vault_install_dir +} + module "vault_wait_for_leader" { source = "./modules/vault_wait_for_leader" @@ -364,3 +363,4 @@ module "vault_verify_billing_start_date" { vault_instance_count = var.vault_instance_count vault_cluster_addr_port = global.ports["vault_cluster"]["port"] } + diff --git a/enos/enos-qualities.hcl b/enos/enos-qualities.hcl index 529990f6fcdb..698ef6a57bc0 100644 --- a/enos/enos-qualities.hcl +++ b/enos/enos-qualities.hcl @@ -72,8 +72,79 @@ quality "vault_agent_log_template" { description = global.description.verify_agent_output } +quality "vault_api_auth_userpass_login_write" { + description = "The v1/auth/userpass/login/ Vault API creates a token for a user" +} + +quality "vault_api_auth_userpass_user_write" { + description = "The v1/auth/userpass/users/ Vault API associates a policy with a user" +} + +quality "vault_api_identity_entity_read" { + description = <<-EOF + The v1/identity/entity Vault API returns an identity entity, has the correct metadata, and is + associated with the expected entity-alias, groups, and policies + EOF +} + +quality "vault_api_identity_entity_write" { + description = "The v1/identity/entity Vault API creates an identity entity" +} + +quality "vault_api_identity_entity_alias_write" { + description = "The v1/identity/entity-alias Vault API creates an identity entity alias" +} + +quality "vault_api_identity_group_write" { + description = "The v1/identity/group/ Vault API creates an identity group" +} + +quality "vault_api_identity_oidc_config_read" { + description = <<-EOF + The v1/identity/oidc/config Vault API returns the built-in identity secrets engine configuration + EOF +} + +quality "vault_api_identity_oidc_config_write" { + description = "The v1/identity/oidc/config Vault API configures the built-in identity secrets engine" +} + +quality "vault_api_identity_oidc_introspect_write" { + description = "The v1/identity/oidc/introspect Vault API creates introspect verifies the active state of a signed OIDC token" +} + +quality "vault_api_identity_oidc_key_read" { + description = <<-EOF + The v1/identity/oidc/key Vault API returns the OIDC signing key and verifies the key's algorithm, + rotation_period, and verification_ttl are correct + EOF +} + +quality "vault_api_identity_oidc_key_write" { + description = "The v1/identity/oidc/key Vault API creates an OIDC signing key" +} + +quality "vault_api_identity_oidc_key_rotate_write" { + description = "The v1/identity/oidc/key//rotate Vault API rotates an OIDC signing key and applies a new verification TTL" +} + +quality "vault_api_identity_oidc_role_read" { + description = <<-EOF + The v1/identity/oidc/role Vault API returns the OIDC role and verifies that the roles key and + ttl are corect. + EOF +} + +quality "vault_api_identity_oidc_role_write" { + description = "The v1/identity/oidc/role Vault API creates an OIDC role associated with a key and clients" +} + +quality "vault_api_identity_oidc_token_read" { + description = "The v1/identity/oidc/token Vault API creates an OIDC token associated with a role" +} + quality "vault_api_sys_auth_userpass_user_write" { - description = "The v1/sys/auth/userpass/users/ Vault API associates a policy with a user" + description = "The v1/sys/auth/userpass/users/ Vault API associates a superuser policy with a user" } quality "vault_api_sys_config_read" { @@ -110,7 +181,7 @@ quality "vault_api_sys_metrics_vault_core_replication_write_undo_logs_enabled" { } quality "vault_api_sys_policy_write" { - description = "The v1/sys/policy Vault API writes a superuser policy" + description = "The v1/sys/policy Vault API writes a policy" } quality "vault_api_sys_quotas_lease_count_read_max_leases_default" { @@ -435,6 +506,10 @@ quality "vault_mount_auth" { description = "Vault mounts the auth engine" } +quality "vault_mount_identity" { + description = "Vault mounts the identity engine" +} + quality "vault_mount_kv" { description = "Vault mounts the kv engine" } @@ -487,10 +562,6 @@ quality "vault_seal_pkcs11" { description = "Vault auto-unseals with the pkcs11 seal" } -quality "vault_secrets_auth_user_policy_write" { - description = "Vault creates auth user policies with the root token" -} - quality "vault_secrets_kv_read" { description = "Vault kv secrets engine data is readable" } diff --git a/enos/enos-scenario-agent.hcl b/enos/enos-scenario-agent.hcl index d8c60ccdc24b..73a2b0f12cad 100644 --- a/enos/enos-scenario-agent.hcl +++ b/enos/enos-scenario-agent.hcl @@ -455,9 +455,9 @@ scenario "agent" { } } - step "verify_write_test_data" { - description = global.description.verify_write_test_data - module = module.vault_verify_write_data + step "verify_secrets_engines_create" { + description = global.description.verify_secrets_engines_create + module = module.vault_verify_secrets_engines_create depends_on = [step.verify_vault_unsealed] providers = { @@ -465,10 +465,22 @@ scenario "agent" { } verifies = [ - quality.vault_secrets_auth_user_policy_write, - quality.vault_secrets_kv_write, + quality.vault_api_auth_userpass_login_write, + quality.vault_api_auth_userpass_user_write, + quality.vault_api_identity_entity_write, + quality.vault_api_identity_entity_alias_write, + quality.vault_api_identity_group_write, + quality.vault_api_identity_oidc_config_write, + quality.vault_api_identity_oidc_introspect_write, + quality.vault_api_identity_oidc_key_write, + quality.vault_api_identity_oidc_key_rotate_write, + quality.vault_api_identity_oidc_role_write, + quality.vault_api_identity_oidc_token_read, + quality.vault_api_sys_auth_userpass_user_write, + quality.vault_api_sys_policy_write, quality.vault_mount_auth, quality.vault_mount_kv, + quality.vault_secrets_kv_write, ] variables { @@ -523,11 +535,11 @@ scenario "agent" { } } - step "verify_read_test_data" { - description = global.description.verify_read_test_data - module = module.vault_verify_read_data + step "verify_secrets_engines_read" { + description = global.description.verify_secrets_engines_read + module = module.vault_verify_secrets_engines_read depends_on = [ - step.verify_write_test_data, + step.verify_secrets_engines_create, step.verify_replication ] @@ -535,9 +547,17 @@ scenario "agent" { enos = local.enos_provider[matrix.distro] } - verifies = quality.vault_secrets_kv_read + verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_identity_entity_read, + quality.vault_api_identity_oidc_config_read, + quality.vault_api_identity_oidc_key_read, + quality.vault_api_identity_oidc_role_read, + quality.vault_secrets_kv_read + ] variables { + create_state = step.verify_secrets_engines_create.state hosts = step.get_vault_cluster_ips.follower_hosts vault_addr = step.create_vault_cluster.api_addr_localhost vault_install_dir = global.vault_install_dir[matrix.artifact_type] @@ -606,6 +626,11 @@ scenario "agent" { value = step.create_vault_cluster.recovery_keys_hex } + output "secrets_engines_state" { + description = "The state of configured secrets engines" + value = step.verify_secrets_engines_create.state + } + output "seal_attributes" { description = "The Vault cluster seal attributes" value = step.create_seal_key.attributes diff --git a/enos/enos-scenario-autopilot.hcl b/enos/enos-scenario-autopilot.hcl index b8ff550303d0..b03a102142ac 100644 --- a/enos/enos-scenario-autopilot.hcl +++ b/enos/enos-scenario-autopilot.hcl @@ -320,9 +320,9 @@ scenario "autopilot" { } } - step "verify_write_test_data" { - description = global.description.verify_write_test_data - module = module.vault_verify_write_data + step "verify_secrets_engines_create" { + description = global.description.verify_secrets_engines_create + module = module.vault_verify_secrets_engines_create depends_on = [ step.create_vault_cluster, step.get_vault_cluster_ips @@ -333,9 +333,21 @@ scenario "autopilot" { } verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_auth_userpass_user_write, + quality.vault_api_identity_entity_write, + quality.vault_api_identity_entity_alias_write, + quality.vault_api_identity_group_write, + quality.vault_api_identity_oidc_config_write, + quality.vault_api_identity_oidc_introspect_write, + quality.vault_api_identity_oidc_key_write, + quality.vault_api_identity_oidc_key_rotate_write, + quality.vault_api_identity_oidc_role_write, + quality.vault_api_identity_oidc_token_read, + quality.vault_api_sys_auth_userpass_user_write, + quality.vault_api_sys_policy_write, quality.vault_mount_auth, quality.vault_mount_kv, - quality.vault_secrets_auth_user_policy_write, quality.vault_secrets_kv_write, ] @@ -366,7 +378,7 @@ scenario "autopilot" { step.build_vault, step.create_vault_cluster, step.create_autopilot_upgrade_storageconfig, - step.verify_write_test_data + step.verify_secrets_engines_create ] providers = { @@ -535,12 +547,12 @@ scenario "autopilot" { } } - step "verify_read_test_data" { - description = global.description.verify_read_test_data - module = module.vault_verify_read_data + step "verify_secrets_engines_read" { + description = global.description.verify_secrets_engines_read + module = module.vault_verify_secrets_engines_read depends_on = [ step.get_updated_vault_cluster_ips, - step.verify_write_test_data, + step.verify_secrets_engines_create, step.upgrade_vault_cluster_with_autopilot, step.verify_raft_auto_join_voter ] @@ -549,9 +561,17 @@ scenario "autopilot" { enos = local.enos_provider[matrix.distro] } - verifies = quality.vault_secrets_kv_read + verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_identity_entity_read, + quality.vault_api_identity_oidc_config_read, + quality.vault_api_identity_oidc_key_read, + quality.vault_api_identity_oidc_role_read, + quality.vault_secrets_kv_read + ] variables { + create_state = step.verify_secrets_engines_create.state hosts = step.get_updated_vault_cluster_ips.follower_hosts vault_addr = step.upgrade_vault_cluster_with_autopilot.api_addr_localhost vault_install_dir = local.vault_install_dir @@ -842,6 +862,11 @@ scenario "autopilot" { value = step.create_vault_cluster.recovery_keys_hex } + output "secrets_engines_state" { + description = "The state of configured secrets engines" + value = step.verify_secrets_engines_create.state + } + output "seal_attributes" { description = "The Vault cluster seal attributes" value = step.create_seal_key.attributes diff --git a/enos/enos-scenario-dr-replication.hcl b/enos/enos-scenario-dr-replication.hcl index 452bdc31650a..210269e8e482 100644 --- a/enos/enos-scenario-dr-replication.hcl +++ b/enos/enos-scenario-dr-replication.hcl @@ -655,9 +655,9 @@ scenario "dr_replication" { } } - step "write_test_data_on_primary" { - description = global.description.verify_write_test_data - module = module.vault_verify_write_data + step "verify_secrets_engines_on_primary" { + description = global.description.verify_secrets_engines_create + module = module.vault_verify_secrets_engines_create depends_on = [step.get_primary_cluster_ips] providers = { @@ -665,9 +665,21 @@ scenario "dr_replication" { } verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_auth_userpass_user_write, + quality.vault_api_identity_entity_write, + quality.vault_api_identity_entity_alias_write, + quality.vault_api_identity_group_write, + quality.vault_api_identity_oidc_config_write, + quality.vault_api_identity_oidc_introspect_write, + quality.vault_api_identity_oidc_key_write, + quality.vault_api_identity_oidc_key_rotate_write, + quality.vault_api_identity_oidc_role_write, + quality.vault_api_identity_oidc_token_read, + quality.vault_api_sys_auth_userpass_user_write, + quality.vault_api_sys_policy_write, quality.vault_mount_auth, quality.vault_mount_kv, - quality.vault_secrets_auth_user_policy_write, quality.vault_secrets_kv_write, ] @@ -699,7 +711,7 @@ scenario "dr_replication" { depends_on = [ step.get_primary_cluster_ips, step.get_secondary_cluster_ips, - step.write_test_data_on_primary + step.verify_secrets_engines_on_primary ] providers = { @@ -978,20 +990,54 @@ scenario "dr_replication" { } } + step "verify_new_primary_cluster_unsealed" { + description = global.description.verify_vault_unsealed + module = module.vault_verify_unsealed + depends_on = [ + step.wait_for_demoted_cluster_leader, + ] + + providers = { + enos = local.enos_provider[matrix.distro] + } + + verifies = [ + quality.vault_auto_unseals_after_autopilot_upgrade, + quality.vault_seal_awskms, + quality.vault_seal_pkcs11, + quality.vault_seal_shamir, + ] + + variables { + hosts = step.get_secondary_cluster_ips.follower_hosts + vault_addr = step.create_secondary_cluster.api_addr_localhost + vault_install_dir = global.vault_install_dir[matrix.artifact_type] + } + } + step "verify_replicated_data_during_failover" { - description = global.description.verify_read_test_data - module = module.vault_verify_read_data + description = global.description.verify_secrets_engines_read + module = module.vault_verify_secrets_engines_read depends_on = [ - step.wait_for_demoted_cluster_leader + step.wait_for_demoted_cluster_leader, + step.verify_new_primary_cluster_unsealed, ] providers = { enos = local.enos_provider[matrix.distro] } - verifies = quality.vault_secrets_kv_read + verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_identity_entity_read, + quality.vault_api_identity_oidc_config_read, + quality.vault_api_identity_oidc_key_read, + quality.vault_api_identity_oidc_role_read, + quality.vault_secrets_kv_read + ] variables { + create_state = step.verify_secrets_engines_on_primary.state hosts = step.get_secondary_cluster_ips.follower_hosts vault_addr = step.create_secondary_cluster.api_addr_localhost vault_install_dir = global.vault_install_dir[matrix.artifact_type] @@ -1005,7 +1051,9 @@ scenario "dr_replication" { so that secondary clusters can utilize it. EOF module = module.generate_secondary_public_key - depends_on = [step.verify_replicated_data_during_failover] + depends_on = [ + step.verify_replicated_data_during_failover, + ] verifies = quality.vault_api_sys_replication_dr_primary_secondary_token_write @@ -1102,12 +1150,12 @@ scenario "dr_replication" { } step "verify_failover_replicated_data" { - description = global.description.verify_read_test_data - module = module.vault_verify_read_data + description = global.description.verify_secrets_engines_read + module = module.vault_verify_secrets_engines_read depends_on = [ step.verify_dr_replication, step.get_secondary_cluster_ips, - step.write_test_data_on_primary, + step.verify_secrets_engines_on_primary, step.verify_failover_dr_replication ] @@ -1115,9 +1163,17 @@ scenario "dr_replication" { enos = local.enos_provider[matrix.distro] } - verifies = quality.vault_secrets_kv_read + verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_identity_entity_read, + quality.vault_api_identity_oidc_config_read, + quality.vault_api_identity_oidc_key_read, + quality.vault_api_identity_oidc_role_read, + quality.vault_secrets_kv_read + ] variables { + create_state = step.verify_secrets_engines_on_primary.state hosts = step.get_secondary_cluster_ips.follower_hosts vault_addr = step.create_secondary_cluster.api_addr_localhost vault_install_dir = global.vault_install_dir[matrix.artifact_type] @@ -1176,6 +1232,11 @@ scenario "dr_replication" { value = step.create_secondary_cluster.root_token } + output "secrets_engines_state" { + description = "The state of configured secrets engines" + value = step.verify_secrets_engines_on_primary.state + } + output "dr_secondary_token" { description = "The dr secondary replication token" value = step.generate_secondary_token.secondary_token diff --git a/enos/enos-scenario-pr-replication.hcl b/enos/enos-scenario-pr-replication.hcl index 56456e1b683c..4d580c24f3b9 100644 --- a/enos/enos-scenario-pr-replication.hcl +++ b/enos/enos-scenario-pr-replication.hcl @@ -677,9 +677,9 @@ scenario "pr_replication" { } } - step "write_test_data_on_primary" { - description = global.description.verify_write_test_data - module = module.vault_verify_write_data + step "verify_secrets_engines_on_primary" { + description = global.description.verify_secrets_engines_create + module = module.vault_verify_secrets_engines_create depends_on = [step.get_primary_cluster_ips] providers = { @@ -687,9 +687,21 @@ scenario "pr_replication" { } verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_auth_userpass_user_write, + quality.vault_api_identity_entity_write, + quality.vault_api_identity_entity_alias_write, + quality.vault_api_identity_group_write, + quality.vault_api_identity_oidc_config_write, + quality.vault_api_identity_oidc_introspect_write, + quality.vault_api_identity_oidc_key_write, + quality.vault_api_identity_oidc_key_rotate_write, + quality.vault_api_identity_oidc_role_write, + quality.vault_api_identity_oidc_token_read, + quality.vault_api_sys_auth_userpass_user_write, + quality.vault_api_sys_policy_write, quality.vault_mount_auth, quality.vault_mount_kv, - quality.vault_secrets_auth_user_policy_write, quality.vault_secrets_kv_write, ] @@ -713,7 +725,7 @@ scenario "pr_replication" { // Wait for both clusters to be up and healthy... step.get_primary_cluster_ips, step.get_secondary_cluster_ips, - step.write_test_data_on_primary, + step.verify_secrets_engines_on_primary, // Wait base verification to complete... step.verify_vault_version, step.verify_ui, @@ -724,10 +736,22 @@ scenario "pr_replication" { } verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_auth_userpass_user_write, + quality.vault_api_identity_entity_write, + quality.vault_api_identity_entity_alias_write, + quality.vault_api_identity_group_write, + quality.vault_api_identity_oidc_config_write, + quality.vault_api_identity_oidc_introspect_write, + quality.vault_api_identity_oidc_key_write, + quality.vault_api_identity_oidc_key_rotate_write, + quality.vault_api_identity_oidc_role_write, + quality.vault_api_identity_oidc_token_read, quality.vault_api_sys_auth_userpass_user_write, quality.vault_api_sys_policy_write, - quality.vault_api_sys_replication_performance_primary_enable_write, - quality.vault_cli_policy_write, + quality.vault_mount_auth, + quality.vault_mount_kv, + quality.vault_secrets_kv_write, ] variables { @@ -872,21 +896,29 @@ scenario "pr_replication" { } step "verify_replicated_data" { - description = global.description.verify_read_test_data - module = module.vault_verify_read_data + description = global.description.verify_secrets_engines_read + module = module.vault_verify_secrets_engines_read depends_on = [ step.verify_performance_replication, step.get_secondary_cluster_ips, - step.write_test_data_on_primary + step.verify_secrets_engines_on_primary ] providers = { enos = local.enos_provider[matrix.distro] } - verifies = quality.vault_secrets_kv_read + verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_identity_entity_read, + quality.vault_api_identity_oidc_config_read, + quality.vault_api_identity_oidc_key_read, + quality.vault_api_identity_oidc_role_read, + quality.vault_secrets_kv_read + ] variables { + create_state = step.verify_secrets_engines_on_primary.state hosts = step.get_secondary_cluster_ips.follower_hosts vault_addr = step.create_secondary_cluster.api_addr_localhost vault_install_dir = global.vault_install_dir[matrix.artifact_type] @@ -1231,6 +1263,11 @@ scenario "pr_replication" { value = step.create_secondary_cluster.root_token } + output "secrets_engines_state" { + description = "The state of configured secrets engines" + value = step.verify_secrets_engines_on_primary.state + } + output "performance_secondary_token" { description = "The performance secondary replication token" value = step.generate_secondary_token.secondary_token diff --git a/enos/enos-scenario-proxy.hcl b/enos/enos-scenario-proxy.hcl index 8b47fa4e16f8..394de37bb9bb 100644 --- a/enos/enos-scenario-proxy.hcl +++ b/enos/enos-scenario-proxy.hcl @@ -432,9 +432,9 @@ scenario "proxy" { } } - step "verify_write_test_data" { - description = global.description.verify_write_test_data - module = module.vault_verify_write_data + step "verify_secrets_engines_create" { + description = global.description.verify_secrets_engines_create + module = module.vault_verify_secrets_engines_create depends_on = [step.verify_vault_unsealed] providers = { @@ -442,9 +442,21 @@ scenario "proxy" { } verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_auth_userpass_user_write, + quality.vault_api_identity_entity_write, + quality.vault_api_identity_entity_alias_write, + quality.vault_api_identity_group_write, + quality.vault_api_identity_oidc_config_write, + quality.vault_api_identity_oidc_introspect_write, + quality.vault_api_identity_oidc_key_write, + quality.vault_api_identity_oidc_key_rotate_write, + quality.vault_api_identity_oidc_role_write, + quality.vault_api_identity_oidc_token_read, + quality.vault_api_sys_auth_userpass_user_write, + quality.vault_api_sys_policy_write, quality.vault_mount_auth, quality.vault_mount_kv, - quality.vault_secrets_auth_user_policy_write, quality.vault_secrets_kv_write, ] @@ -500,11 +512,11 @@ scenario "proxy" { } } - step "verify_read_test_data" { - description = global.description.verify_read_test_data - module = module.vault_verify_read_data + step "verify_secrets_engines_read" { + description = global.description.verify_secrets_engines_read + module = module.vault_verify_secrets_engines_read depends_on = [ - step.verify_write_test_data, + step.verify_secrets_engines_create, step.verify_replication ] @@ -512,9 +524,17 @@ scenario "proxy" { enos = local.enos_provider[matrix.distro] } - verifies = quality.vault_secrets_kv_read + verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_identity_entity_read, + quality.vault_api_identity_oidc_config_read, + quality.vault_api_identity_oidc_key_read, + quality.vault_api_identity_oidc_role_read, + quality.vault_secrets_kv_read + ] variables { + create_state = step.verify_secrets_engines_create.state hosts = step.get_vault_cluster_ips.follower_hosts vault_addr = step.create_vault_cluster.api_addr_localhost vault_install_dir = global.vault_install_dir[matrix.artifact_type] @@ -583,6 +603,11 @@ scenario "proxy" { value = step.create_vault_cluster.recovery_keys_hex } + output "secrets_engines_state" { + description = "The state of configured secrets engines" + value = step.verify_secrets_engines_create.state + } + output "seal_attributes" { description = "The Vault cluster seal attributes" value = step.create_seal_key.attributes diff --git a/enos/enos-scenario-seal-ha.hcl b/enos/enos-scenario-seal-ha.hcl index 918ea2f0827a..8cc3a7494981 100644 --- a/enos/enos-scenario-seal-ha.hcl +++ b/enos/enos-scenario-seal-ha.hcl @@ -410,9 +410,9 @@ scenario "seal_ha" { } // Write some test data before we create the new seal - step "verify_write_test_data" { - description = global.description.verify_write_test_data - module = module.vault_verify_write_data + step "verify_secrets_engines_create" { + description = global.description.verify_secrets_engines_create + module = module.vault_verify_secrets_engines_create depends_on = [ step.create_vault_cluster, step.get_vault_cluster_ips, @@ -424,9 +424,21 @@ scenario "seal_ha" { } verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_auth_userpass_user_write, + quality.vault_api_identity_entity_write, + quality.vault_api_identity_entity_alias_write, + quality.vault_api_identity_group_write, + quality.vault_api_identity_oidc_config_write, + quality.vault_api_identity_oidc_introspect_write, + quality.vault_api_identity_oidc_key_write, + quality.vault_api_identity_oidc_key_rotate_write, + quality.vault_api_identity_oidc_role_write, + quality.vault_api_identity_oidc_token_read, + quality.vault_api_sys_auth_userpass_user_write, + quality.vault_api_sys_policy_write, quality.vault_mount_auth, quality.vault_mount_kv, - quality.vault_secrets_auth_user_policy_write, quality.vault_secrets_kv_write, ] @@ -444,7 +456,7 @@ scenario "seal_ha" { description = global.description.wait_for_seal_rewrap module = module.vault_wait_for_seal_rewrap depends_on = [ - step.verify_write_test_data, + step.verify_secrets_engines_create, ] providers = { @@ -471,7 +483,7 @@ scenario "seal_ha" { module = module.stop_vault depends_on = [ step.create_vault_cluster, - step.verify_write_test_data, + step.verify_secrets_engines_create, step.wait_for_initial_seal_rewrap, ] @@ -756,18 +768,26 @@ scenario "seal_ha" { } // Make sure our data is still available - step "verify_read_test_data" { - description = global.description.verify_read_test_data - module = module.vault_verify_read_data + step "verify_secrets_engines_read" { + description = global.description.verify_secrets_engines_read + module = module.vault_verify_secrets_engines_read depends_on = [step.wait_for_seal_rewrap] providers = { enos = local.enos_provider[matrix.distro] } - verifies = quality.vault_secrets_kv_read + verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_identity_entity_read, + quality.vault_api_identity_oidc_config_read, + quality.vault_api_identity_oidc_key_read, + quality.vault_api_identity_oidc_role_read, + quality.vault_secrets_kv_read + ] variables { + create_state = step.verify_secrets_engines_create.state hosts = step.get_updated_cluster_ips.follower_hosts vault_addr = step.create_vault_cluster.api_addr_localhost vault_install_dir = global.vault_install_dir[matrix.artifact_type] @@ -820,7 +840,7 @@ scenario "seal_ha" { module = module.stop_vault depends_on = [ step.wait_for_seal_rewrap, - step.verify_read_test_data, + step.verify_secrets_engines_read, ] providers = { @@ -949,15 +969,25 @@ scenario "seal_ha" { } // Make sure our data is still available after migration - step "verify_read_test_data_after_migration" { - module = module.vault_verify_read_data + step "verify_secrets_engines_read_after_migration" { + module = module.vault_verify_secrets_engines_read depends_on = [step.wait_for_seal_rewrap_after_migration] providers = { enos = local.enos_provider[matrix.distro] } + verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_identity_entity_read, + quality.vault_api_identity_oidc_config_read, + quality.vault_api_identity_oidc_key_read, + quality.vault_api_identity_oidc_role_read, + quality.vault_secrets_kv_read + ] + variables { + create_state = step.verify_secrets_engines_create.state hosts = step.get_cluster_ips_after_migration.follower_hosts vault_addr = step.create_vault_cluster.api_addr_localhost vault_install_dir = global.vault_install_dir[matrix.artifact_type] @@ -1048,6 +1078,11 @@ scenario "seal_ha" { value = step.create_secondary_seal_key.attributes } + output "secrets_engines_state" { + description = "The state of configured secrets engines" + value = step.verify_secrets_engines_create.state + } + output "unseal_keys_b64" { description = "The Vault cluster unseal keys" value = step.create_vault_cluster.unseal_keys_b64 diff --git a/enos/enos-scenario-smoke.hcl b/enos/enos-scenario-smoke.hcl index 2aaf3919e694..ae40fb3fe9fa 100644 --- a/enos/enos-scenario-smoke.hcl +++ b/enos/enos-scenario-smoke.hcl @@ -474,9 +474,9 @@ scenario "smoke" { } } - step "verify_write_test_data" { - description = global.description.verify_write_test_data - module = module.vault_verify_write_data + step "verify_secrets_engines_create" { + description = global.description.verify_secrets_engines_create + module = module.vault_verify_secrets_engines_create depends_on = [step.verify_vault_unsealed] providers = { @@ -484,9 +484,21 @@ scenario "smoke" { } verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_auth_userpass_user_write, + quality.vault_api_identity_entity_write, + quality.vault_api_identity_entity_alias_write, + quality.vault_api_identity_group_write, + quality.vault_api_identity_oidc_config_write, + quality.vault_api_identity_oidc_introspect_write, + quality.vault_api_identity_oidc_key_write, + quality.vault_api_identity_oidc_key_rotate_write, + quality.vault_api_identity_oidc_role_write, + quality.vault_api_identity_oidc_token_read, + quality.vault_api_sys_auth_userpass_user_write, + quality.vault_api_sys_policy_write, quality.vault_mount_auth, quality.vault_mount_kv, - quality.vault_secrets_auth_user_policy_write, quality.vault_secrets_kv_write, ] @@ -542,11 +554,11 @@ scenario "smoke" { } } - step "verify_read_test_data" { - description = global.description.verify_read_test_data - module = module.vault_verify_read_data + step "verify_secrets_engines_read" { + description = global.description.verify_secrets_engines_read + module = module.vault_verify_secrets_engines_read depends_on = [ - step.verify_write_test_data, + step.verify_secrets_engines_create, step.verify_replication ] @@ -554,9 +566,17 @@ scenario "smoke" { enos = local.enos_provider[matrix.distro] } - verifies = quality.vault_secrets_kv_read + verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_identity_entity_read, + quality.vault_api_identity_oidc_config_read, + quality.vault_api_identity_oidc_key_read, + quality.vault_api_identity_oidc_role_read, + quality.vault_secrets_kv_read + ] variables { + create_state = step.verify_secrets_engines_create.state hosts = step.get_vault_cluster_ips.follower_hosts vault_addr = step.create_vault_cluster.api_addr_localhost vault_install_dir = global.vault_install_dir[matrix.artifact_type] @@ -625,6 +645,11 @@ scenario "smoke" { value = step.create_vault_cluster.recovery_keys_hex } + output "secrets_engines_state" { + description = "The state of configured secrets engines" + value = step.verify_secrets_engines_create.state + } + output "seal_key_attributes" { description = "The Vault cluster seal attributes" value = step.create_seal_key.attributes diff --git a/enos/enos-scenario-upgrade.hcl b/enos/enos-scenario-upgrade.hcl index 6eaf749f13c6..501542d9d5e6 100644 --- a/enos/enos-scenario-upgrade.hcl +++ b/enos/enos-scenario-upgrade.hcl @@ -379,9 +379,9 @@ scenario "upgrade" { } } - step "verify_write_test_data" { - description = global.description.verify_write_test_data - module = module.vault_verify_write_data + step "verify_secrets_engines_create" { + description = global.description.verify_secrets_engines_create + module = module.vault_verify_secrets_engines_create depends_on = [ step.create_vault_cluster, step.get_vault_cluster_ips, @@ -392,9 +392,21 @@ scenario "upgrade" { } verifies = [ + quality.vault_api_auth_userpass_login_write, + quality.vault_api_auth_userpass_user_write, + quality.vault_api_identity_entity_write, + quality.vault_api_identity_entity_alias_write, + quality.vault_api_identity_group_write, + quality.vault_api_identity_oidc_config_write, + quality.vault_api_identity_oidc_introspect_write, + quality.vault_api_identity_oidc_key_write, + quality.vault_api_identity_oidc_key_rotate_write, + quality.vault_api_identity_oidc_role_write, + quality.vault_api_identity_oidc_token_read, + quality.vault_api_sys_auth_userpass_user_write, + quality.vault_api_sys_policy_write, quality.vault_mount_auth, quality.vault_mount_kv, - quality.vault_secrets_auth_user_policy_write, quality.vault_secrets_kv_write, ] @@ -418,7 +430,7 @@ scenario "upgrade" { module = module.vault_upgrade depends_on = [ step.create_vault_cluster, - step.verify_write_test_data, + step.verify_secrets_engines_create, ] providers = { @@ -622,11 +634,11 @@ scenario "upgrade" { } } - step "verify_read_test_data" { - description = global.description.verify_write_test_data - module = module.vault_verify_read_data + step "verify_secrets_engines_read" { + description = global.description.verify_secrets_engines_read + module = module.vault_verify_secrets_engines_read depends_on = [ - step.verify_write_test_data, + step.verify_secrets_engines_create, step.verify_vault_unsealed ] @@ -635,13 +647,16 @@ scenario "upgrade" { } verifies = [ - quality.vault_mount_auth, - quality.vault_mount_kv, - quality.vault_secrets_auth_user_policy_write, - quality.vault_secrets_kv_write, + quality.vault_api_auth_userpass_login_write, + quality.vault_api_identity_entity_read, + quality.vault_api_identity_oidc_config_read, + quality.vault_api_identity_oidc_key_read, + quality.vault_api_identity_oidc_role_read, + quality.vault_secrets_kv_read ] variables { + create_state = step.verify_secrets_engines_create.state hosts = step.get_updated_vault_cluster_ips.follower_hosts vault_addr = step.create_vault_cluster.api_addr_localhost vault_install_dir = global.vault_install_dir[matrix.artifact_type] @@ -698,7 +713,7 @@ scenario "upgrade" { depends_on = [ step.get_updated_vault_cluster_ips, step.verify_vault_unsealed, - step.verify_read_test_data, + step.verify_secrets_engines_read, ] providers = { @@ -784,6 +799,11 @@ scenario "upgrade" { value = step.create_seal_key.attributes } + output "secrets_engines_state" { + description = "The state of configured secrets engines" + value = step.verify_secrets_engines_create.state + } + output "unseal_keys_b64" { description = "The Vault cluster unseal keys" value = step.create_vault_cluster.unseal_keys_b64 diff --git a/enos/modules/vault_verify_read_data/scripts/verify-data.sh b/enos/modules/vault_verify_read_data/scripts/verify-data.sh deleted file mode 100644 index 4f25d273574b..000000000000 --- a/enos/modules/vault_verify_read_data/scripts/verify-data.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: BUSL-1.1 - -set -e - -function retry { - local retries=$1 - shift - local count=0 - - until "$@"; do - exit=$? - wait=$((2 ** count)) - count=$((count + 1)) - if [ "$count" -lt "$retries" ]; then - sleep "$wait" - else - return "$exit" - fi - done - - return 0 -} - -fail() { - echo "$1" 1>&2 - return 1 -} - -binpath="${VAULT_INSTALL_DIR}/vault" - -test -x "$binpath" || fail "unable to locate vault binary at $binpath" - -# To keep the authentication method and module verification consistent between all -# Enos scenarios we authenticate using testuser created by vault_verify_write_data module -retry 5 "$binpath" login -method=userpass username=testuser password=passuser1 -retry 5 "$binpath" kv get secret/test diff --git a/enos/modules/vault_verify_write_data/main.tf b/enos/modules/vault_verify_write_data/main.tf deleted file mode 100644 index 4f4401981eac..000000000000 --- a/enos/modules/vault_verify_write_data/main.tf +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: BUSL-1.1 - -terraform { - required_providers { - enos = { - source = "registry.terraform.io/hashicorp-forge/enos" - } - } -} - -variable "hosts" { - type = map(object({ - ipv6 = string - private_ip = string - public_ip = string - })) - description = "The Vault cluster instances that were created" -} - -variable "leader_host" { - type = object({ - ipv6 = string - private_ip = string - public_ip = string - }) - - description = "Vault cluster leader host" -} - -variable "vault_addr" { - type = string - description = "The local vault API listen address" -} - -variable "vault_install_dir" { - type = string - description = "The directory where the Vault binary will be installed" -} - -variable "vault_root_token" { - type = string - description = "The Vault root token" - default = null -} - -# We use this module to verify write data in all Enos scenarios. Since we cannot use -# Vault token to authenticate to secondary clusters in replication scenario we add a regular user -# here to keep the authentication method and module verification consistent between all scenarios -resource "enos_remote_exec" "smoke-enable-secrets-kv" { - # Only enable the secrets engine on the leader node - environment = { - VAULT_ADDR = var.vault_addr - VAULT_TOKEN = var.vault_root_token - VAULT_INSTALL_DIR = var.vault_install_dir - } - - scripts = [abspath("${path.module}/scripts/smoke-enable-secrets-kv.sh")] - - transport = { - ssh = { - host = var.leader_host.public_ip - } - } -} - -# Verify that we can enable the k/v secrets engine and write data to it. -resource "enos_remote_exec" "smoke-write-test-data" { - depends_on = [enos_remote_exec.smoke-enable-secrets-kv] - for_each = var.hosts - - environment = { - VAULT_ADDR = var.vault_addr - VAULT_TOKEN = var.vault_root_token - VAULT_INSTALL_DIR = var.vault_install_dir - TEST_KEY = "smoke${each.key}" - TEST_VALUE = "fire" - } - - scripts = [abspath("${path.module}/scripts/smoke-write-test-data.sh")] - - transport = { - ssh = { - host = each.value.public_ip - } - } -} diff --git a/enos/modules/vault_verify_write_data/scripts/smoke-enable-secrets-kv.sh b/enos/modules/vault_verify_write_data/scripts/smoke-enable-secrets-kv.sh deleted file mode 100644 index 666a32c33b62..000000000000 --- a/enos/modules/vault_verify_write_data/scripts/smoke-enable-secrets-kv.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) HashiCorp, Inc. -# SPDX-License-Identifier: BUSL-1.1 - -set -e - -retry() { - local retries=$1 - shift - local count=0 - - until "$@"; do - exit=$? - wait=$((2 ** count)) - count=$((count + 1)) - if [ "$count" -lt "$retries" ]; then - sleep "$wait" - else - return "$exit" - fi - done - - return 0 -} - -fail() { - echo "$1" 1>&2 - exit 1 -} - -[[ -z "$VAULT_ADDR" ]] && fail "VAULT_ADDR env variable has not been set" -[[ -z "$VAULT_INSTALL_DIR" ]] && fail "VAULT_INSTALL_DIR env variable has not been set" -[[ -z "$VAULT_TOKEN" ]] && fail "VAULT_TOKEN env variable has not been set" - -binpath=${VAULT_INSTALL_DIR}/vault - -test -x "$binpath" || fail "unable to locate vault binary at $binpath" - -retry 5 "$binpath" status > /dev/null 2>&1 - -# Create user policy -retry 5 "$binpath" policy write reguser - << EOF -path "*" { - capabilities = ["read", "list"] -} -EOF - -# Enable the userpass auth method -retry 5 "$binpath" auth enable userpass > /dev/null 2>&1 - -# Create new user and attach reguser policy -retry 5 "$binpath" write auth/userpass/users/testuser password="passuser1" policies="reguser" - -retry 5 "$binpath" secrets enable -path="secret" kv diff --git a/enos/modules/verify_secrets_engines/modules/create/auth.tf b/enos/modules/verify_secrets_engines/modules/create/auth.tf new file mode 100644 index 000000000000..cfbec2f84e18 --- /dev/null +++ b/enos/modules/verify_secrets_engines/modules/create/auth.tf @@ -0,0 +1,145 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +locals { + // Variables + auth_userpass_path = "userpass" # auth/userpass + user_name = "testuser" # auth/userpass/users/testuser + user_password = "passtestuser1" # auth/userpass/login/passtestuser1 + user_policy_name = "reguser" # sys/policy/reguser + + // Response data + user_login_data = jsondecode(enos_remote_exec.auth_login_testuser.stdout) + sys_auth_data = jsondecode(enos_remote_exec.read_sys_auth.stdout).data + + // Output + auth_output = { + sys = local.sys_auth_data + userpass = { + path = local.auth_userpass_path + user = { + name = local.user_name + password = local.user_password + policy_name = local.user_policy_name + login = local.user_login_data + } + } + } +} + +output "auth" { + value = local.auth_output +} + +# Enable userpass auth +resource "enos_remote_exec" "auth_enable_userpass" { + environment = { + AUTH_METHOD = "userpass" + AUTH_PATH = local.auth_userpass_path + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/auth-enable.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +# Get the sys/auth data after enabling our auth method +resource "enos_remote_exec" "read_sys_auth" { + depends_on = [ + enos_remote_exec.auth_enable_userpass, + ] + environment = { + REQPATH = "sys/auth" + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/read.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +# Create a default policy for our users that allows them to read and list. +resource "enos_remote_exec" "policy_read_reguser" { + environment = { + POLICY_NAME = local.user_policy_name + POLICY_CONFIG = <<-EOF + path "*" { + capabilities = ["read", "list"] + } + EOF + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/policy-write.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +# Create our user +resource "enos_remote_exec" "auth_create_testuser" { + depends_on = [ + enos_remote_exec.auth_enable_userpass, + enos_remote_exec.policy_read_reguser, + ] + + environment = { + AUTH_PATH = local.auth_userpass_path + PASSWORD = local.user_password + POLICIES = local.user_policy_name + USERNAME = local.user_name + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/auth-userpass-write.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +resource "enos_remote_exec" "auth_login_testuser" { + depends_on = [ + // Don't try to login until created our user and added it to the kv_writers group + enos_remote_exec.auth_create_testuser, + enos_remote_exec.identity_group_kv_writers, + ] + + environment = { + AUTH_PATH = local.auth_userpass_path + PASSWORD = local.user_password + USERNAME = local.user_name + VAULT_ADDR = var.vault_addr + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/auth-userpass-login.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} diff --git a/enos/modules/verify_secrets_engines/modules/create/identity.tf b/enos/modules/verify_secrets_engines/modules/create/identity.tf new file mode 100644 index 000000000000..6ee8810f0281 --- /dev/null +++ b/enos/modules/verify_secrets_engines/modules/create/identity.tf @@ -0,0 +1,380 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +locals { + // Variables + identity_entity_metadata = { + "organization" = "vault", + "team" = "qt", + } + group_name_oidc_readers = "oidc_token_readers" // identity/group/name/oidc_token_readers + oidc_config_issuer_url = "https://enos.example.com:1234" // identity/oidc/config + oidc_key_algorithms = ["RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "EdDSA"] + oidc_key_algorithm = local.oidc_key_algorithms[random_integer.oidc_key_algorithm_idx.result] + oidc_key_name = "reguser" // identity/oidc/key/reguser + oidc_key_rotation_period = 86400 // 24h + oidc_key_verification_ttl = 21600 // 6h + oidc_role_name = "reguser" // identity/oidc/role/reguser + oidc_role_ttl = 3600 // 1h + oidc_client_id = "reguser" // optional client ID but required if we want to scope a key and role together without a * + oidc_token_read_policy_name = "oidc_token_reader" + + // Response data + oidc_token_data = jsondecode(enos_remote_exec.oidc_token.stdout).data + group_oidc_token_readers_data = jsondecode(enos_remote_exec.identity_group_oidc_token_readers.stdout).data + initial_oidc_token_data = jsondecode(enos_remote_exec.initial_oidc_token.stdout).data + user_entity_data = jsondecode(enos_remote_exec.identity_entity_testuser.stdout).data + user_entity_alias_data = jsondecode(enos_remote_exec.identity_entity_alias_testuser.stdout).data + + // Output + identity_output = { + oidc = { + reader_group_name = local.group_name_oidc_readers + reader_policy_name = local.oidc_token_read_policy_name + issuer_url = local.oidc_config_issuer_url + key_algorithm = local.oidc_key_algorithm + key_name = local.oidc_key_name + key_rotation_period = local.oidc_key_rotation_period + key_verification_ttl = local.oidc_key_verification_ttl + role_name = local.oidc_role_name + role_ttl = local.oidc_role_ttl + client_id = local.oidc_client_id + } + identity_entity_metadata = local.identity_entity_metadata + data = { + entity = local.user_entity_data + entity_alias = local.user_entity_alias_data + oidc_token = local.oidc_token_data + group_oidc_token_readers = local.group_oidc_token_readers_data + } + } +} + +output "identity" { + value = local.identity_output +} + +// Get a random index for our algorithms so that we can randomly rotate through the various algorithms +resource "random_integer" "oidc_key_algorithm_idx" { + min = 0 + max = length(local.oidc_key_algorithms) - 1 +} + +// Create identity entity for our user +resource "enos_remote_exec" "identity_entity_testuser" { + depends_on = [ + enos_remote_exec.auth_create_testuser, + ] + + environment = { + REQPATH = "identity/entity" + PAYLOAD = jsonencode({ + name = local.user_name, + metadata = local.identity_entity_metadata, + policies = [local.user_policy_name], + }) + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/write-payload.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +// Create identity entity alias for our user +resource "enos_remote_exec" "identity_entity_alias_testuser" { + environment = { + REQPATH = "identity/entity-alias" + PAYLOAD = jsonencode({ + name = local.user_name, + canonical_id = local.user_entity_data.id + mount_accessor = local.sys_auth_data["${local.auth_userpass_path}/"].accessor + policies = [local.user_policy_name], + }) + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/write-payload.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +// Configure our the oidc token backend +resource "enos_remote_exec" "oidc_config" { + environment = { + REQPATH = "identity/oidc/config" + PAYLOAD = jsonencode({ + issuer = local.oidc_config_issuer_url, + }) + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/write-payload.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +// Create a named key that can sign OIDC identity token +resource "enos_remote_exec" "oidc_key" { + environment = { + REQPATH = "identity/oidc/key/${local.oidc_key_name}" + PAYLOAD = jsonencode({ + allowed_client_ids = [local.oidc_client_id], + algorithm = local.oidc_key_algorithm, + rotation_period = local.oidc_key_rotation_period, + verification_ttl = local.oidc_key_verification_ttl, + }) + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/write-payload.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +// Create a role with custom template and that uses the named key +resource "enos_remote_exec" "oidc_role" { + depends_on = [ + enos_remote_exec.oidc_key, + ] + + environment = { + REQPATH = "identity/oidc/role/${local.oidc_role_name}" + PAYLOAD = jsonencode({ + client_id = local.oidc_client_id, + key = local.oidc_key_name, + ttl = local.oidc_role_ttl + template = base64encode(<<-EOF + { + "team": {{identity.entity.metadata.team}}, + "organization": {{identity.entity.metadata.organization}}, + "groups": {{identity.entity.groups.names}} + } + EOF + ), + }) + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/write-payload.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +// Create a group policy that allows "reading" a new signed OIDC token +resource "enos_remote_exec" "policy_write_oidc_token" { + depends_on = [ + enos_remote_exec.secrets_enable_kv_secret, + ] + environment = { + POLICY_NAME = local.oidc_token_read_policy_name + POLICY_CONFIG = <<-EOF + path "identity/oidc/token/*" { + capabilities = ["read"] + } + EOF + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/policy-write.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +// Create oidc_token_readers group and add our testuser to it +resource "enos_remote_exec" "identity_group_oidc_token_readers" { + environment = { + REQPATH = "identity/group" + PAYLOAD = jsonencode({ + member_entity_ids = [local.user_entity_data.id], + name = local.group_name_oidc_readers, + policies = [local.oidc_token_read_policy_name], + }) + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/write-payload.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +// Generate a signed ID token with our test user +resource "enos_remote_exec" "initial_oidc_token" { + depends_on = [ + enos_remote_exec.oidc_role, + ] + + environment = { + REQPATH = "identity/oidc/token/${local.oidc_role_name}" + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = local.user_login_data.auth.client_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/read.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +// Introspect the signed ID and verify it +resource "enos_remote_exec" "oidc_introspect_initial_token" { + environment = { + ASSERT_ACTIVE = true // Our token should be "active" + PAYLOAD = jsonencode({ + token = local.initial_oidc_token_data.token, + client_id = local.initial_oidc_token_data.client_id + }) + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/identity-oidc-introspect-token.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +// Rotate the key with a zero TTL to force expiration +resource "enos_remote_exec" "oidc_key_rotate" { + depends_on = [ + enos_remote_exec.oidc_introspect_initial_token, + ] + + environment = { + REQPATH = "identity/oidc/key/${local.oidc_key_name}/rotate" + PAYLOAD = jsonencode({ + verification_ttl = 0, + }) + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/write-payload.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +// Introspect it again to make sure it's no longer active +resource "enos_remote_exec" "oidc_introspect_initial_token_post_rotate" { + depends_on = [ + enos_remote_exec.oidc_key_rotate, + ] + + environment = { + ASSERT_ACTIVE = false // Our token should not be "active" + PAYLOAD = jsonencode({ + token = local.initial_oidc_token_data.token, + client_id = local.initial_oidc_token_data.client_id + }) + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/identity-oidc-introspect-token.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +// Generate a new token that we can use later +resource "enos_remote_exec" "oidc_token" { + depends_on = [ + enos_remote_exec.oidc_introspect_initial_token_post_rotate, + ] + + environment = { + REQPATH = "identity/oidc/token/${local.oidc_role_name}" + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = local.user_login_data.auth.client_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/read.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +// Introspect the new token to ensure it's active before we export it for user later via outputs +resource "enos_remote_exec" "oidc_introspect_token" { + environment = { + ASSERT_ACTIVE = true // Our token should be "active" + PAYLOAD = jsonencode({ + token = local.oidc_token_data.token, + client_id = local.oidc_token_data.client_id + }) + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/identity-oidc-introspect-token.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} diff --git a/enos/modules/verify_secrets_engines/modules/create/kv.tf b/enos/modules/verify_secrets_engines/modules/create/kv.tf new file mode 100644 index 000000000000..269f64b73eec --- /dev/null +++ b/enos/modules/verify_secrets_engines/modules/create/kv.tf @@ -0,0 +1,126 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +locals { + // Variables + group_name_kv_writers = "kv_writers" # identity/group/name/kv_writers + kv_mount = "secret" # secret + kv_write_policy_name = "kv_writer" # sys/policy/kv_writer + kv_test_data_path_prefix = "smoke" + kv_test_data_value_prefix = "fire" + + // Response data + identity_group_kv_writers_data = jsondecode(enos_remote_exec.identity_group_kv_writers.stdout).data + + // Output + kv_output = { + reader_group_name = local.group_name_kv_writers + writer_policy_name = local.kv_write_policy_name + mount = local.kv_mount + test = { + path_prefix = local.kv_test_data_path_prefix + value_prefix = local.kv_test_data_value_prefix + } + data = { + identity_group_kv_writers = local.identity_group_kv_writers_data + } + } +} + +output "kv" { + value = local.kv_output +} + +# Enable kv secrets engine +resource "enos_remote_exec" "secrets_enable_kv_secret" { + environment = { + ENGINE = "kv" + MOUNT = local.kv_mount + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/secrets-enable.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +# Create a group policy that allows writing to our kv store +resource "enos_remote_exec" "policy_write_kv_writer" { + depends_on = [ + enos_remote_exec.secrets_enable_kv_secret, + ] + environment = { + POLICY_NAME = local.kv_write_policy_name + POLICY_CONFIG = <<-EOF + path "${local.kv_mount}/*" { + capabilities = ["create", "update", "read", "delete", "list"] + } + EOF + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/policy-write.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +# Create kv_writers group and add our testuser to it +resource "enos_remote_exec" "identity_group_kv_writers" { + environment = { + REQPATH = "identity/group" + PAYLOAD = jsonencode({ + member_entity_ids = [local.user_entity_data.id], // Created in identity.tf + name = local.group_name_kv_writers, + policies = [local.kv_write_policy_name], + }) + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = var.vault_root_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/write-payload.sh")] + + transport = { + ssh = { + host = var.leader_host.public_ip + } + } +} + +// Write test data as our user. +resource "enos_remote_exec" "kv_put_secret_test" { + depends_on = [ + enos_remote_exec.secrets_enable_kv_secret, + ] + for_each = var.hosts + + environment = { + MOUNT = local.kv_mount + SECRET_PATH = "${local.kv_test_data_path_prefix}-${each.key}" + KEY = "${local.kv_test_data_path_prefix}-${each.key}" + VALUE = "${local.kv_test_data_value_prefix}-${each.key}" + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = local.user_login_data.auth.client_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/kv-put.sh")] + + transport = { + ssh = { + host = each.value.public_ip + } + } +} diff --git a/enos/modules/verify_secrets_engines/modules/create/main.tf b/enos/modules/verify_secrets_engines/modules/create/main.tf new file mode 100644 index 000000000000..89ca1c80b406 --- /dev/null +++ b/enos/modules/verify_secrets_engines/modules/create/main.tf @@ -0,0 +1,53 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +terraform { + required_providers { + enos = { + source = "registry.terraform.io/hashicorp-forge/enos" + } + } +} + +variable "hosts" { + type = map(object({ + ipv6 = string + private_ip = string + public_ip = string + })) + description = "The Vault cluster instances that were created" +} + +variable "leader_host" { + type = object({ + ipv6 = string + private_ip = string + public_ip = string + }) + + description = "Vault cluster leader host" +} + +variable "vault_addr" { + type = string + description = "The local vault API listen address" +} + +variable "vault_install_dir" { + type = string + description = "The directory where the Vault binary will be installed" +} + +variable "vault_root_token" { + type = string + description = "The Vault root token" + default = null +} + +output "state" { + value = { + auth = local.auth_output + identity = local.identity_output + kv = local.kv_output + } +} diff --git a/enos/modules/verify_secrets_engines/modules/read/auth.tf b/enos/modules/verify_secrets_engines/modules/read/auth.tf new file mode 100644 index 000000000000..2ea06de22c37 --- /dev/null +++ b/enos/modules/verify_secrets_engines/modules/read/auth.tf @@ -0,0 +1,24 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +locals { + user_login_data = jsondecode(enos_remote_exec.auth_login_testuser.stdout) +} + +resource "enos_remote_exec" "auth_login_testuser" { + environment = { + AUTH_PATH = var.create_state.auth.userpass.path + PASSWORD = var.create_state.auth.userpass.user.password + USERNAME = var.create_state.auth.userpass.user.name + VAULT_ADDR = var.vault_addr + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/auth-userpass-login.sh")] + + transport = { + ssh = { + host = var.hosts[0].public_ip + } + } +} diff --git a/enos/modules/verify_secrets_engines/modules/read/identity.tf b/enos/modules/verify_secrets_engines/modules/read/identity.tf new file mode 100644 index 000000000000..0f347969a1fa --- /dev/null +++ b/enos/modules/verify_secrets_engines/modules/read/identity.tf @@ -0,0 +1,56 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +// Read our testuser identity entity and verify that it matches our expected alias, groups, policy, +// and metadata. +resource "enos_remote_exec" "identity_verify_entity" { + for_each = var.hosts + + environment = { + ENTITY_ALIAS_ID = var.create_state.identity.data.entity_alias.id + ENTITY_GROUP_IDS = jsonencode([ + var.create_state.kv.data.identity_group_kv_writers.id, + var.create_state.identity.data.group_oidc_token_readers.id, + ]) + ENTITY_METADATA = jsonencode(var.create_state.identity.identity_entity_metadata) + ENTITY_NAME = var.create_state.identity.data.entity.name + ENTITY_POLICIES = jsonencode([var.create_state.auth.userpass.user.policy_name]) + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = local.user_login_data.auth.client_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/identity-verify-entity.sh")] + + transport = { + ssh = { + host = each.value.public_ip + } + } +} + +// Read our OIDC key and role and verify that they have the correct configuration, TTLs, and algorithms. +resource "enos_remote_exec" "identity_verify_oidc" { + for_each = var.hosts + + environment = { + OIDC_ISSUER_URL = var.create_state.identity.oidc.issuer_url + OIDC_KEY_NAME = var.create_state.identity.oidc.key_name + OIDC_KEY_ROTATION_PERIOD = var.create_state.identity.oidc.key_rotation_period + OIDC_KEY_VERIFICATION_TTL = var.create_state.identity.oidc.key_verification_ttl + OIDC_KEY_ALGORITHM = var.create_state.identity.oidc.key_algorithm + OIDC_ROLE_NAME = var.create_state.identity.oidc.role_name + OIDC_ROLE_TTL = var.create_state.identity.oidc.role_ttl + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = local.user_login_data.auth.client_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/identity-verify-oidc.sh")] + + transport = { + ssh = { + host = each.value.public_ip + } + } +} diff --git a/enos/modules/verify_secrets_engines/modules/read/kv.tf b/enos/modules/verify_secrets_engines/modules/read/kv.tf new file mode 100644 index 000000000000..cfa4b7829e13 --- /dev/null +++ b/enos/modules/verify_secrets_engines/modules/read/kv.tf @@ -0,0 +1,24 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +resource "enos_remote_exec" "kv_get_verify_test_data" { + for_each = var.hosts + + environment = { + MOUNT = var.create_state.kv.mount + SECRET_PATH = "${var.create_state.kv.test.path_prefix}-${each.key}" + KEY = "${var.create_state.kv.test.path_prefix}-${each.key}" + VALUE = "${var.create_state.kv.test.value_prefix}-${each.key}" + VAULT_ADDR = var.vault_addr + VAULT_TOKEN = local.user_login_data.auth.client_token + VAULT_INSTALL_DIR = var.vault_install_dir + } + + scripts = [abspath("${path.module}/../../scripts/kv-verify-value.sh")] + + transport = { + ssh = { + host = each.value.public_ip + } + } +} diff --git a/enos/modules/vault_verify_read_data/main.tf b/enos/modules/verify_secrets_engines/modules/read/main.tf similarity index 68% rename from enos/modules/vault_verify_read_data/main.tf rename to enos/modules/verify_secrets_engines/modules/read/main.tf index 244dc388935d..f2ad27a60f3f 100644 --- a/enos/modules/vault_verify_read_data/main.tf +++ b/enos/modules/verify_secrets_engines/modules/read/main.tf @@ -18,6 +18,10 @@ variable "hosts" { description = "The Vault cluster instances that were created" } +variable "create_state" { + description = "The state of the secrets engines from the 'create' module" +} + variable "vault_addr" { type = string description = "The local vault API listen address" @@ -28,23 +32,12 @@ variable "vault_install_dir" { description = "The directory where the Vault binary will be installed" } -locals { - vault_bin_path = "${var.vault_install_dir}/vault" +variable "vault_root_token" { + type = string + description = "The Vault root token" + default = null } -resource "enos_remote_exec" "verify_kv_on_node" { - for_each = var.hosts - - environment = { - VAULT_ADDR = var.vault_addr - VAULT_INSTALL_DIR = var.vault_install_dir - } - - scripts = [abspath("${path.module}/scripts/verify-data.sh")] - - transport = { - ssh = { - host = each.value.public_ip - } - } +locals { + vault_bin_path = "${var.vault_install_dir}/vault" } diff --git a/enos/modules/verify_secrets_engines/scripts/auth-enable.sh b/enos/modules/verify_secrets_engines/scripts/auth-enable.sh new file mode 100644 index 000000000000..5601715a81a6 --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/auth-enable.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +set -e + +fail() { + echo "$1" 1>&2 + exit 1 +} + +[[ -z "$AUTH_METHOD" ]] && fail "AUTH_METHOD env variable has not been set" +[[ -z "$AUTH_PATH" ]] && fail "AUTH_PATH env variable has not been set" +[[ -z "$VAULT_ADDR" ]] && fail "VAULT_ADDR env variable has not been set" +[[ -z "$VAULT_INSTALL_DIR" ]] && fail "VAULT_INSTALL_DIR env variable has not been set" +[[ -z "$VAULT_TOKEN" ]] && fail "VAULT_TOKEN env variable has not been set" + +binpath=${VAULT_INSTALL_DIR}/vault +test -x "$binpath" || fail "unable to locate vault binary at $binpath" + +export VAULT_FORMAT=json +"$binpath" auth enable -path="$AUTH_PATH" "$AUTH_METHOD" diff --git a/enos/modules/verify_secrets_engines/scripts/auth-userpass-login.sh b/enos/modules/verify_secrets_engines/scripts/auth-userpass-login.sh new file mode 100644 index 000000000000..31b756f1f5a5 --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/auth-userpass-login.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +set -e + +fail() { + echo "$1" 1>&2 + exit 1 +} + +[[ -z "$AUTH_PATH" ]] && fail "AUTH_PATH env variable has not been set" +[[ -z "$PASSWORD" ]] && fail "PASSWORD env variable has not been set" +[[ -z "$USERNAME" ]] && fail "USERNAME env variable has not been set" +[[ -z "$VAULT_ADDR" ]] && fail "VAULT_ADDR env variable has not been set" +[[ -z "$VAULT_INSTALL_DIR" ]] && fail "VAULT_INSTALL_DIR env variable has not been set" + +binpath=${VAULT_INSTALL_DIR}/vault +test -x "$binpath" || fail "unable to locate vault binary at $binpath" + +export VAULT_FORMAT=json +"$binpath" write "auth/$AUTH_PATH/login/$USERNAME" password="$PASSWORD" diff --git a/enos/modules/vault_verify_write_data/scripts/smoke-write-test-data.sh b/enos/modules/verify_secrets_engines/scripts/auth-userpass-write.sh similarity index 51% rename from enos/modules/vault_verify_write_data/scripts/smoke-write-test-data.sh rename to enos/modules/verify_secrets_engines/scripts/auth-userpass-write.sh index 58eb2d8a238e..b8cca8bb1b63 100644 --- a/enos/modules/vault_verify_write_data/scripts/smoke-write-test-data.sh +++ b/enos/modules/verify_secrets_engines/scripts/auth-userpass-write.sh @@ -4,38 +4,21 @@ set -e -retry() { - local retries=$1 - shift - local count=0 - - until "$@"; do - exit=$? - wait=$((2 ** count)) - count=$((count + 1)) - if [ "$count" -lt "$retries" ]; then - sleep "$wait" - else - return "$exit" - fi - done - - return 0 -} - fail() { echo "$1" 1>&2 exit 1 } -[[ -z "$TEST_KEY" ]] && fail "TEST_KEY env variable has not been set" -[[ -z "$TEST_VALUE" ]] && fail "TEST_VALUE env variable has not been set" +[[ -z "$AUTH_PATH" ]] && fail "AUTH_PATH env variable has not been set" +[[ -z "$PASSWORD" ]] && fail "PASSWORD env variable has not been set" +[[ -z "$POLICIES" ]] && fail "POLICIES env variable has not been set" +[[ -z "$USERNAME" ]] && fail "USERNAME env variable has not been set" [[ -z "$VAULT_ADDR" ]] && fail "VAULT_ADDR env variable has not been set" [[ -z "$VAULT_INSTALL_DIR" ]] && fail "VAULT_INSTALL_DIR env variable has not been set" [[ -z "$VAULT_TOKEN" ]] && fail "VAULT_TOKEN env variable has not been set" binpath=${VAULT_INSTALL_DIR}/vault - test -x "$binpath" || fail "unable to locate vault binary at $binpath" -retry 5 "$binpath" kv put secret/test "$TEST_KEY=$TEST_VALUE" +export VAULT_FORMAT=json +"$binpath" write "auth/$AUTH_PATH/users/$USERNAME" password="$PASSWORD" policies="$POLICIES" diff --git a/enos/modules/verify_secrets_engines/scripts/identity-oidc-introspect-token.sh b/enos/modules/verify_secrets_engines/scripts/identity-oidc-introspect-token.sh new file mode 100644 index 000000000000..0e6e1eaabab7 --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/identity-oidc-introspect-token.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +set -e + +fail() { + echo "$1" 1>&2 + exit 1 +} + +[[ -z "$PAYLOAD" ]] && fail "PAYLOAD env variable has not been set" +[[ -z "$ASSERT_ACTIVE" ]] && fail "ASSERT_ACTIVE env variable has not been set" +[[ -z "$VAULT_ADDR" ]] && fail "VAULT_ADDR env variable has not been set" +[[ -z "$VAULT_INSTALL_DIR" ]] && fail "VAULT_INSTALL_DIR env variable has not been set" +[[ -z "$VAULT_TOKEN" ]] && fail "VAULT_TOKEN env variable has not been set" + +binpath=${VAULT_INSTALL_DIR}/vault +test -x "$binpath" || fail "unable to locate vault binary at $binpath" + +export VAULT_FORMAT=json +if ! output=$("$binpath" write identity/oidc/introspect - <<< "$PAYLOAD" 2>&1); then + # Attempt to write our error on stdout as JSON as our consumers of the script expect it to be JSON + printf '{"data":{"error":"%s"}}' "$output" + # Fail on stderr with a human readable message + fail "failed to write payload to identity/oidc/introspect: payload=$PAYLOAD output=$output" +fi + +printf "%s\n" "$output" # Write our response output JSON to stdout +if ! jq -Me --argjson ACTIVE "$ASSERT_ACTIVE" '.data.active == $ACTIVE' <<< "$output" &> /dev/null; then + # Write a failure message on STDERR + fail "token active state is invalid, expected .data.active='$ASSERT_ACTIVE'" +fi diff --git a/enos/modules/verify_secrets_engines/scripts/identity-verify-entity.sh b/enos/modules/verify_secrets_engines/scripts/identity-verify-entity.sh new file mode 100644 index 000000000000..2ee950368196 --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/identity-verify-entity.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +set -e + +fail() { + echo "$1" 1>&2 + exit 1 +} + +[[ -z "$ENTITY_ALIAS_ID" ]] && fail "ENTITY_ALIAS_ID env variable has not been set" +[[ -z "$ENTITY_GROUP_IDS" ]] && fail "ENTITY_GROUP_IDS env variable has not been set" +[[ -z "$ENTITY_METADATA" ]] && fail "ENTITY_METADATA env variable has not been set" +[[ -z "$ENTITY_NAME" ]] && fail "ENTITY_NAME env variable has not been set" +[[ -z "$ENTITY_POLICIES" ]] && fail "ENTITY_POLICIES env variable has not been set" +[[ -z "$VAULT_ADDR" ]] && fail "VAULT_ADDR env variable has not been set" +[[ -z "$VAULT_INSTALL_DIR" ]] && fail "VAULT_INSTALL_DIR env variable has not been set" +[[ -z "$VAULT_TOKEN" ]] && fail "VAULT_TOKEN env variable has not been set" + +binpath=${VAULT_INSTALL_DIR}/vault +test -x "$binpath" || fail "unable to locate vault binary at $binpath" + +export VAULT_FORMAT=json +if ! output=$("$binpath" read "identity/entity/name/$ENTITY_NAME" 2>&1); then + fail "failed to read identity/entity/name/$ENTITY_NAME: $output" +fi + +if ! jq -Mec --arg ALIAS "$ENTITY_ALIAS_ID" '.data.aliases[0].id == $ALIAS' <<< "$output"; then + fail "entity alias ID does not match, expected: $ENTITY_ALIAS_ID, got: $(jq -Mrc '.data.aliases' <<< "$output")" +fi + +if ! jq -Mec --argjson GROUPS "$ENTITY_GROUP_IDS" '.data.group_ids | sort as $have | $GROUPS | sort as $want | $have == $want' <<< "$output"; then + fail "entity group ID's do not match, expected: $ENTITY_GROUP_IDS, got: $(jq -Mrc '.data.group_ids' <<< "$output")" +fi + +if ! jq -Mec --argjson METADATA "$ENTITY_METADATA" '.data.metadata == $METADATA' <<< "$output"; then + fail "entity metadata does not match, expected: $ENTITY_METADATA, got: $(jq -Mrc '.data.metadata' <<< "$output")" +fi + +if ! jq -Mec --argjson POLICIES "$ENTITY_POLICIES" '.data.policies == $POLICIES' <<< "$output"; then + fail "entity policies do not match, expected: $ENTITY_POLICIES, got: $(jq -Mrc '.data.policies' <<< "$output")" +fi diff --git a/enos/modules/verify_secrets_engines/scripts/identity-verify-oidc.sh b/enos/modules/verify_secrets_engines/scripts/identity-verify-oidc.sh new file mode 100644 index 000000000000..3b095570aebb --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/identity-verify-oidc.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +set -e + +fail() { + echo "$1" 1>&2 + exit 1 +} + +[[ -z "$OIDC_ISSUER_URL" ]] && fail "OIDC_ISSUER_URL env variable has not been set" +[[ -z "$OIDC_KEY_NAME" ]] && fail "OIDC_KEY_NAME env variable has not been set" +[[ -z "$OIDC_KEY_ROTATION_PERIOD" ]] && fail "OIDC_KEY_ROTATION_PERIOD env variable has not been set" +[[ -z "$OIDC_KEY_VERIFICATION_TTL" ]] && fail "OIDC_KEY_VERIFICATION_TTL env variable has not been set" +[[ -z "$OIDC_KEY_ALGORITHM" ]] && fail "OIDC_KEY_ALGORITHM env variable has not been set" +[[ -z "$OIDC_ROLE_NAME" ]] && fail "OIDC_ROLE_NAME env variable has not been set" +[[ -z "$OIDC_ROLE_TTL" ]] && fail "OIDC_ROLE_TTL env variable has not been set" +[[ -z "$VAULT_ADDR" ]] && fail "VAULT_ADDR env variable has not been set" +[[ -z "$VAULT_INSTALL_DIR" ]] && fail "VAULT_INSTALL_DIR env variable has not been set" +[[ -z "$VAULT_TOKEN" ]] && fail "VAULT_TOKEN env variable has not been set" + +binpath=${VAULT_INSTALL_DIR}/vault +test -x "$binpath" || fail "unable to locate vault binary at $binpath" + +export VAULT_FORMAT=json + +# Verify that we have the correct issuer URL +if ! cfg=$("$binpath" read identity/oidc/config); then + fail "failed to read identity/oidc/config: $cfg" +elif ! jq -Merc --arg URL "$OIDC_ISSUER_URL" '.data.issuer == $URL' <<< "$cfg"; then + fail "oidc issuer URL is incorrect, expected: $OIDC_ISSUER_URL, got $(jq -Mrc '.data.issuer' <<< "$cfg")" +fi + +# Verify that our token algorithm, rotation period and verification TTL are correct +if ! key_res=$("$binpath" read "identity/oidc/key/$OIDC_KEY_NAME"); then + fail "failed to read identity/oidc/key/$OIDC_KEY_NAME: $key_res" +fi + +if ! jq -Merc --arg ALG "$OIDC_KEY_ALGORITHM" '.data.algorithm == $ALG' <<< "$key_res"; then + fail "oidc token algorithm is incorrect, expected: $OIDC_KEY_ALGORITHM, got $(jq -Mrc '.data.algorithm' <<< "$key_res")" +fi + +if ! jq -Merc --argjson RP "$OIDC_KEY_ROTATION_PERIOD" '.data.rotation_period == $RP' <<< "$key_res"; then + fail "oidc token rotation_period is incorrect, expected: $OIDC_KEY_ROTATION_PERIOD, got $(jq -Mrc '.data.rotation_period' <<< "$key_res")" +fi + +if ! jq -Merc --argjson TTL "$OIDC_KEY_VERIFICATION_TTL" '.data.verification_ttl == $TTL' <<< "$key_res"; then + fail "oidc token verification_ttl is incorrect, expected: $OIDC_KEY_VERIFICATION_TTL, got $(jq -Mrc '.data.verification_ttl' <<< "$key_res")" +fi + +# Verify that our role key and TTL are correct. +if ! role_res=$("$binpath" read "identity/oidc/role/$OIDC_ROLE_NAME"); then + fail "failed to read identity/oidc/role/$OIDC_ROLE_NAME: $role_res" +fi + +if ! jq -Merc --arg KEY "$OIDC_KEY_NAME" '.data.key == $KEY' <<< "$role_res"; then + fail "oidc role key is incorrect, expected: $OIDC_KEY_NAME, got $(jq -Mrc '.data.key' <<< "$role_res")" +fi + +if ! jq -Merc --argjson TTL "$OIDC_ROLE_TTL" '.data.ttl == $TTL' <<< "$role_res"; then + fail "oidc role ttl is incorrect, expected: $OIDC_ROLE_TTL, got $(jq -Mrc '.data.ttl' <<< "$role_res")" +fi diff --git a/enos/modules/verify_secrets_engines/scripts/kv-put.sh b/enos/modules/verify_secrets_engines/scripts/kv-put.sh new file mode 100644 index 000000000000..46e858f6c62d --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/kv-put.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +set -e + +fail() { + echo "$1" 1>&2 + exit 1 +} + +[[ -z "$KEY" ]] && fail "KEY env variable has not been set" +[[ -z "$MOUNT" ]] && fail "MOUNT env variable has not been set" +[[ -z "$SECRET_PATH" ]] && fail "SECRET_PATH env variable has not been set" +[[ -z "$VALUE" ]] && fail "VALUE env variable has not been set" +[[ -z "$VAULT_ADDR" ]] && fail "VAULT_ADDR env variable has not been set" +[[ -z "$VAULT_INSTALL_DIR" ]] && fail "VAULT_INSTALL_DIR env variable has not been set" +[[ -z "$VAULT_TOKEN" ]] && fail "VAULT_TOKEN env variable has not been set" + +binpath=${VAULT_INSTALL_DIR}/vault +test -x "$binpath" || fail "unable to locate vault binary at $binpath" + +export VAULT_FORMAT=json + +"$binpath" kv put -mount="$MOUNT" "$SECRET_PATH" "$KEY=$VALUE" diff --git a/enos/modules/verify_secrets_engines/scripts/kv-verify-value.sh b/enos/modules/verify_secrets_engines/scripts/kv-verify-value.sh new file mode 100644 index 000000000000..72427d869642 --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/kv-verify-value.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +set -e + +fail() { + echo "$1" 1>&2 + exit 1 +} + +[[ -z "$MOUNT" ]] && fail "MOUNT env variable has not been set" +[[ -z "$SECRET_PATH" ]] && fail "SECRET_PATH env variable has not been set" +[[ -z "$KEY" ]] && fail "KEY env variable has not been set" +[[ -z "$VALUE" ]] && fail "VALUE env variable has not been set" +[[ -z "$VAULT_ADDR" ]] && fail "VAULT_ADDR env variable has not been set" +[[ -z "$VAULT_INSTALL_DIR" ]] && fail "VAULT_INSTALL_DIR env variable has not been set" +[[ -z "$VAULT_TOKEN" ]] && fail "VAULT_TOKEN env variable has not been set" + +binpath=${VAULT_INSTALL_DIR}/vault +test -x "$binpath" || fail "unable to locate vault binary at $binpath" + +export VAULT_FORMAT=json +if res=$("$binpath" kv get "$MOUNT/$SECRET_PATH"); then + if jq -Merc --arg VALUE "$VALUE" --arg KEY "$KEY" '.data[$KEY] == $VALUE' <<< "$res"; then + printf "kv %s/%s %s=%s is valid\n" "$MOUNT" "$SECRET_PATH" "$KEY" "$VALUE" + exit 0 + fi + fail "kv $MOUNT/$SECRET_PATH $KEY=$VALUE invalid! Got: $(jq -Mrc --arg KEY "$KEY" '.data[$KEY]' <<< "$res")" +else + fail "failed to read kv data for $MOUNT/$SECRET_PATH: $res" +fi diff --git a/enos/modules/verify_secrets_engines/scripts/policy-write.sh b/enos/modules/verify_secrets_engines/scripts/policy-write.sh new file mode 100644 index 000000000000..18e011cc9686 --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/policy-write.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +set -e + +fail() { + echo "$1" 1>&2 + exit 1 +} + +[[ -z "$POLICY_NAME" ]] && fail "POLICY_NAME env variable has not been set" +[[ -z "$POLICY_CONFIG" ]] && fail "POLICY_CONFIG env variable has not been set" +[[ -z "$VAULT_ADDR" ]] && fail "VAULT_ADDR env variable has not been set" +[[ -z "$VAULT_INSTALL_DIR" ]] && fail "VAULT_INSTALL_DIR env variable has not been set" +[[ -z "$VAULT_TOKEN" ]] && fail "VAULT_TOKEN env variable has not been set" + +binpath=${VAULT_INSTALL_DIR}/vault +test -x "$binpath" || fail "unable to locate vault binary at $binpath" + +export VAULT_FORMAT=json +"$binpath" policy write "$POLICY_NAME" - <<< "$POLICY_CONFIG" diff --git a/enos/modules/verify_secrets_engines/scripts/read.sh b/enos/modules/verify_secrets_engines/scripts/read.sh new file mode 100644 index 000000000000..b522c6f55f5e --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/read.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +set -e + +fail() { + echo "$1" 1>&2 + exit 1 +} + +[[ -z "$REQPATH" ]] && fail "REQPATH env variable has not been set" +[[ -z "$VAULT_ADDR" ]] && fail "VAULT_ADDR env variable has not been set" +[[ -z "$VAULT_INSTALL_DIR" ]] && fail "VAULT_INSTALL_DIR env variable has not been set" +[[ -z "$VAULT_TOKEN" ]] && fail "VAULT_TOKEN env variable has not been set" + +binpath=${VAULT_INSTALL_DIR}/vault +test -x "$binpath" || fail "unable to locate vault binary at $binpath" + +export VAULT_FORMAT=json +"$binpath" read "$REQPATH" diff --git a/enos/modules/verify_secrets_engines/scripts/secrets-enable.sh b/enos/modules/verify_secrets_engines/scripts/secrets-enable.sh new file mode 100644 index 000000000000..7cc957a290bf --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/secrets-enable.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +set -e + +fail() { + echo "$1" 1>&2 + exit 1 +} + +[[ -z "$MOUNT" ]] && fail "MOUNT env variable has not been set" +[[ -z "$ENGINE" ]] && fail "MOUNT env variable has not been set" +[[ -z "$VAULT_ADDR" ]] && fail "VAULT_ADDR env variable has not been set" +[[ -z "$VAULT_INSTALL_DIR" ]] && fail "VAULT_INSTALL_DIR env variable has not been set" +[[ -z "$VAULT_TOKEN" ]] && fail "VAULT_TOKEN env variable has not been set" + +binpath=${VAULT_INSTALL_DIR}/vault +test -x "$binpath" || fail "unable to locate vault binary at $binpath" + +export VAULT_FORMAT=json +"$binpath" secrets enable -path="$MOUNT" "$ENGINE" diff --git a/enos/modules/verify_secrets_engines/scripts/write-payload.sh b/enos/modules/verify_secrets_engines/scripts/write-payload.sh new file mode 100644 index 000000000000..922fb2e5f76f --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/write-payload.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +set -e + +fail() { + echo "$1" 1>&2 + exit 1 +} + +[[ -z "$REQPATH" ]] && fail "REQPATH env variable has not been set" +[[ -z "$PAYLOAD" ]] && fail "PAYLOAD env variable has not been set" +[[ -z "$VAULT_ADDR" ]] && fail "VAULT_ADDR env variable has not been set" +[[ -z "$VAULT_INSTALL_DIR" ]] && fail "VAULT_INSTALL_DIR env variable has not been set" +[[ -z "$VAULT_TOKEN" ]] && fail "VAULT_TOKEN env variable has not been set" + +binpath=${VAULT_INSTALL_DIR}/vault +test -x "$binpath" || fail "unable to locate vault binary at $binpath" + +export VAULT_FORMAT=json +if output=$("$binpath" write "$REQPATH" - <<< "$PAYLOAD" 2>&1); then + printf "%s\n" "$output" +else + fail "failed to write payload: path=$REQPATH payload=$PAYLOAD out=$output" +fi