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