Files
vault/enos/enos-scenario-dr-replication.hcl
Tin Vo ac3bb7b2d4 VAULT-32188: Enos test for PKI certificates (#29007)
* updating pki test

* updating pki test

* updating pki test

* updating pki script

* resolving conflicts

* adding pki cert verifications

* resolving conflicts

* updating test

* removing comments

* addressing bash formatting

* updating test

* adding description

* fixing lint error

* fixing lint error

* fixing lint issue

* removing unneeded scenario

* resolving conflicts

* debugging pipeline error

* fixing pipeline tests'

* fixing pipeline tests'

* testing smoke test

* fixing pipeline error

* debugging pipeline error

* debugging pipeline error

* debugging pipeline error

* debugging agent test ci failure

* fixing ci errors

* uncomment token

* updating script

* updating hosts

* fixing lint

* fixing lint

* fixing lint

* adding revoked certificate

* undo kv.tf change

* updating cert issuing

* updating issuing certs to include issuer

* updating pki cert verification

* addressing comments

* fixing lint

* fixing lint

* fixing lint

* fixing lint

* updating verify_secrets_engine_read module

* fixing lint

* fixing lint

* fixing lint

* debugging lint

* testing pipeline

* adding verify variables for autopilot

* adding pki read variable for autopilot

* updating vault engine read variables

* addressing comments

* fixing lint

* update test for enterprise

* update pki tests to adapt to enterprise
2025-01-23 11:30:20 -08:00

1394 lines
50 KiB
HCL

# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1
scenario "dr_replication" {
description = <<-EOF
The DR replication scenario configures disaster recovery replication between two Vault clusters and
verifies behavior and failure tolerance. The build can be a local branch, any CRT built Vault
Enterprise artifact saved to the local machine, or any CRT built Vault Enterprise artifact in
the stable channel in Artifactory.
The scenario deploys two Vault Enterprise clusters and establishes disaster recovery replication
between the primary cluster and the disaster recovery replication secondary cluster. Next, we write
test data to the primary cluster and verify that the data is replicated to the secondary cluster.
We then promote the secondary cluster to be the primary cluster and demote the primary cluster to be
the secondary cluster. We then update the secondary cluster to connect to the new primary cluster.
Finally, we verify that the secondary cluster is unsealed after enabling replication and verify the
disaster recovery replication status between the primary and secondary clusters.
# How to run this scenario
For general instructions on running a scenario, refer to the Enos docs: https://eng-handbook.hashicorp.services/internal-tools/enos/running-a-scenario/
For troubleshooting tips and common errors, see https://eng-handbook.hashicorp.services/internal-tools/enos/troubleshooting/.
Variables required for all scenario variants:
- aws_ssh_private_key_path (more info about AWS SSH keypairs: https://eng-handbook.hashicorp.services/internal-tools/enos/getting-started/#set-your-aws-key-pair-name-and-private-key)
- aws_ssh_keypair_name
- vault_build_date*
- vault_product_version
- vault_revision*
* If you don't already know what build date and revision you should be using, see
https://eng-handbook.hashicorp.services/internal-tools/enos/troubleshooting/#execution-error-expected-vs-got-for-vault-versioneditionrevisionbuild-date.
Variables required for some scenario variants:
- artifactory_username (if using `artifact_source:artifactory` in your filter)
- artifactory_token (if using `artifact_source:artifactory` in your filter)
- aws_region (if different from the default value in enos-variables.hcl)
- consul_license_path (if using an ENT edition of Consul)
- distro_version_<distro> (if different from the default version for your target
distro. See supported distros and default versions in the distro_version_<distro>
definitions in enos-variables.hcl)
- vault_artifact_path (the path to where you have a Vault artifact already downloaded,
if using `artifact_source:crt` in your filter)
- vault_license_path (if using an ENT edition of Vault)
EOF
matrix {
arch = global.archs
artifact_source = global.artifact_sources
artifact_type = global.artifact_types
config_mode = global.config_modes
consul_edition = global.consul_editions
consul_version = global.consul_versions
distro = global.distros
edition = global.enterprise_editions
ip_version = global.ip_versions
primary_backend = global.backends
primary_seal = global.seals
secondary_backend = global.backends
secondary_seal = global.seals
// Our local builder always creates bundles
exclude {
artifact_source = ["local"]
artifact_type = ["package"]
}
// PKCS#11 can only be used on ent.hsm and ent.hsm.fips1402.
exclude {
primary_seal = ["pkcs11"]
edition = [for e in matrix.edition : e if !strcontains(e, "hsm")]
}
exclude {
secondary_seal = ["pkcs11"]
edition = [for e in matrix.edition : e if !strcontains(e, "hsm")]
}
// softhsm packages not available for leap/sles.
exclude {
primary_seal = ["pkcs11"]
distro = ["leap", "sles"]
}
exclude {
secondary_seal = ["pkcs11"]
distro = ["leap", "sles"]
}
// Testing in IPV6 mode is currently implemented for integrated Raft storage only
exclude {
ip_version = ["6"]
primary_backend = ["consul"]
}
exclude {
ip_version = ["6"]
secondary_backend = ["consul"]
}
}
terraform_cli = terraform_cli.default
terraform = terraform.default
providers = [
provider.aws.default,
provider.enos.ec2_user,
provider.enos.ubuntu
]
locals {
artifact_path = matrix.artifact_source != "artifactory" ? abspath(var.vault_artifact_path) : null
enos_provider = {
amzn = provider.enos.ec2_user
leap = provider.enos.ec2_user
rhel = provider.enos.ec2_user
sles = provider.enos.ec2_user
ubuntu = provider.enos.ubuntu
}
manage_service = matrix.artifact_type == "bundle"
vault_install_dir = matrix.artifact_type == "bundle" ? var.vault_install_dir : global.vault_install_dir[matrix.artifact_type]
}
step "build_vault" {
description = global.description.build_vault
module = "build_${matrix.artifact_source}"
variables {
build_tags = var.vault_local_build_tags != null ? var.vault_local_build_tags : global.build_tags[matrix.edition]
artifact_path = local.artifact_path
goarch = matrix.arch
goos = "linux"
artifactory_host = matrix.artifact_source == "artifactory" ? var.artifactory_host : null
artifactory_repo = matrix.artifact_source == "artifactory" ? var.artifactory_repo : null
artifactory_username = matrix.artifact_source == "artifactory" ? var.artifactory_username : null
artifactory_token = matrix.artifact_source == "artifactory" ? var.artifactory_token : null
arch = matrix.artifact_source == "artifactory" ? matrix.arch : null
product_version = var.vault_product_version
artifact_type = matrix.artifact_type
distro = matrix.artifact_source == "artifactory" ? matrix.distro : null
edition = matrix.artifact_source == "artifactory" ? matrix.edition : null
revision = var.vault_revision
}
}
step "ec2_info" {
description = global.description.ec2_info
module = module.ec2_info
}
step "create_vpc" {
description = global.description.create_vpc
module = module.create_vpc
variables {
common_tags = global.tags
ip_version = matrix.ip_version
}
}
// This step reads the contents of the backend license if we're using a Consul backend and
// an "ent" Consul edition.
step "read_backend_license" {
description = global.description.read_backend_license
skip_step = (matrix.primary_backend == "raft" && matrix.secondary_backend == "raft") || matrix.consul_edition == "ce"
module = module.read_license
variables {
file_name = global.backend_license_path
}
}
step "read_vault_license" {
description = global.description.read_vault_license
module = module.read_license
variables {
file_name = abspath(joinpath(path.root, "./support/vault.hclic"))
}
}
step "create_primary_seal_key" {
description = global.description.create_seal_key
module = "seal_${matrix.primary_seal}"
depends_on = [step.create_vpc]
providers = {
enos = provider.enos.ubuntu
}
variables {
cluster_id = step.create_vpc.id
cluster_meta = "primary"
common_tags = global.tags
}
}
step "create_secondary_seal_key" {
description = global.description.create_seal_key
module = "seal_${matrix.secondary_seal}"
depends_on = [step.create_vpc]
providers = {
enos = provider.enos.ubuntu
}
variables {
cluster_id = step.create_vpc.id
cluster_meta = "secondary"
common_tags = global.tags
other_resources = step.create_primary_seal_key.resource_names
}
}
// Create all of our instances for both primary and secondary clusters
step "create_primary_cluster_targets" {
description = global.description.create_vault_cluster_targets
module = module.target_ec2_instances
depends_on = [
step.create_vpc,
]
providers = {
enos = local.enos_provider[matrix.distro]
}
variables {
ami_id = step.ec2_info.ami_ids[matrix.arch][matrix.distro][global.distro_version[matrix.distro]]
cluster_tag_key = global.vault_tag_key
common_tags = global.tags
seal_key_names = step.create_primary_seal_key.resource_names
vpc_id = step.create_vpc.id
}
}
step "create_primary_cluster_backend_targets" {
description = global.description.create_vault_cluster_targets
module = matrix.primary_backend == "consul" ? module.target_ec2_instances : module.target_ec2_shim
depends_on = [
step.create_vpc,
]
providers = {
enos = provider.enos.ubuntu
}
variables {
ami_id = step.ec2_info.ami_ids["arm64"]["ubuntu"][global.distro_version["ubuntu"]]
cluster_tag_key = global.backend_tag_key
common_tags = global.tags
seal_key_names = step.create_primary_seal_key.resource_names
vpc_id = step.create_vpc.id
}
}
step "create_secondary_cluster_targets" {
description = global.description.create_vault_cluster_targets
module = module.target_ec2_instances
depends_on = [step.create_vpc]
providers = {
enos = local.enos_provider[matrix.distro]
}
variables {
ami_id = step.ec2_info.ami_ids[matrix.arch][matrix.distro][global.distro_version[matrix.distro]]
cluster_tag_key = global.vault_tag_key
common_tags = global.tags
seal_key_names = step.create_secondary_seal_key.resource_names
vpc_id = step.create_vpc.id
}
}
step "create_secondary_cluster_backend_targets" {
description = global.description.create_vault_cluster_targets
module = matrix.secondary_backend == "consul" ? module.target_ec2_instances : module.target_ec2_shim
depends_on = [step.create_vpc]
providers = {
enos = provider.enos.ubuntu
}
variables {
ami_id = step.ec2_info.ami_ids["arm64"]["ubuntu"][global.distro_version["ubuntu"]]
cluster_tag_key = global.backend_tag_key
common_tags = global.tags
seal_key_names = step.create_secondary_seal_key.resource_names
vpc_id = step.create_vpc.id
}
}
step "create_primary_backend_cluster" {
description = global.description.create_backend_cluster
module = "backend_${matrix.primary_backend}"
depends_on = [
step.create_primary_cluster_backend_targets,
]
providers = {
enos = provider.enos.ubuntu
}
verifies = [
// verified in modules
quality.consul_autojoin_aws,
quality.consul_config_file,
quality.consul_ha_leader_election,
quality.consul_service_start_server,
// verified in enos_consul_start resource
quality.consul_api_agent_host_read,
quality.consul_api_health_node_read,
quality.consul_api_operator_raft_config_read,
quality.consul_cli_validate,
quality.consul_health_state_passing_read_nodes_minimum,
quality.consul_operator_raft_configuration_read_voters_minimum,
quality.consul_service_systemd_notified,
quality.consul_service_systemd_unit,
]
variables {
cluster_name = step.create_primary_cluster_backend_targets.cluster_name
cluster_tag_key = global.backend_tag_key
hosts = step.create_primary_cluster_backend_targets.hosts
license = (matrix.primary_backend == "consul" && matrix.consul_edition == "ent") ? step.read_backend_license.license : null
release = {
edition = matrix.consul_edition
version = matrix.consul_version
}
}
}
step "create_primary_cluster" {
description = global.description.create_vault_cluster
module = module.vault_cluster
depends_on = [
step.create_primary_backend_cluster,
step.build_vault,
step.create_primary_cluster_targets
]
providers = {
enos = local.enos_provider[matrix.distro]
}
verifies = [
// verified in modules
quality.consul_service_start_client,
quality.vault_artifact_bundle,
quality.vault_artifact_deb,
quality.vault_artifact_rpm,
quality.vault_audit_log,
quality.vault_audit_socket,
quality.vault_audit_syslog,
quality.vault_autojoin_aws,
quality.vault_config_env_variables,
quality.vault_config_file,
quality.vault_config_log_level,
quality.vault_init,
quality.vault_license_required_ent,
quality.vault_listener_ipv4,
quality.vault_listener_ipv6,
quality.vault_service_start,
quality.vault_storage_backend_consul,
quality.vault_storage_backend_raft,
// verified in enos_vault_start resource
quality.vault_api_sys_config_read,
quality.vault_api_sys_ha_status_read,
quality.vault_api_sys_health_read,
quality.vault_api_sys_host_info_read,
quality.vault_api_sys_replication_status_read,
quality.vault_api_sys_seal_status_api_read_matches_sys_health,
quality.vault_api_sys_storage_raft_autopilot_configuration_read,
quality.vault_api_sys_storage_raft_autopilot_state_read,
quality.vault_api_sys_storage_raft_configuration_read,
quality.vault_cli_status_exit_code,
quality.vault_service_systemd_notified,
quality.vault_service_systemd_unit,
]
variables {
artifactory_release = matrix.artifact_source == "artifactory" ? step.build_vault.vault_artifactory_release : null
backend_cluster_name = step.create_primary_cluster_backend_targets.cluster_name
backend_cluster_tag_key = global.backend_tag_key
config_mode = matrix.config_mode
consul_license = (matrix.primary_backend == "consul" && matrix.consul_edition == "ent") ? step.read_backend_license.license : null
cluster_name = step.create_primary_cluster_targets.cluster_name
consul_release = matrix.primary_backend == "consul" ? {
edition = matrix.consul_edition
version = matrix.consul_version
} : null
enable_audit_devices = var.vault_enable_audit_devices
hosts = step.create_primary_cluster_targets.hosts
install_dir = global.vault_install_dir[matrix.artifact_type]
ip_version = matrix.ip_version
license = matrix.edition != "ce" ? step.read_vault_license.license : null
local_artifact_path = local.artifact_path
manage_service = local.manage_service
packages = concat(global.packages, global.distro_packages[matrix.distro][global.distro_version[matrix.distro]])
seal_attributes = step.create_primary_seal_key.attributes
seal_type = matrix.primary_seal
storage_backend = matrix.primary_backend
}
}
step "get_local_metadata" {
skip_step = matrix.artifact_source != "local"
module = module.get_local_metadata
}
step "wait_for_primary_cluster_leader" {
description = global.description.wait_for_cluster_to_have_leader
module = module.vault_wait_for_leader
depends_on = [step.create_primary_cluster]
providers = {
enos = local.enos_provider[matrix.distro]
}
verifies = [
quality.vault_api_sys_leader_read,
quality.vault_unseal_ha_leader_election,
]
variables {
hosts = step.create_primary_cluster_targets.hosts
ip_version = matrix.ip_version
timeout = 120 // seconds
vault_addr = step.create_primary_cluster.api_addr_localhost
vault_install_dir = local.vault_install_dir
vault_root_token = step.create_primary_cluster.root_token
}
}
step "create_secondary_backend_cluster" {
description = global.description.create_backend_cluster
module = "backend_${matrix.secondary_backend}"
depends_on = [
step.create_secondary_cluster_backend_targets
]
providers = {
enos = provider.enos.ubuntu
}
variables {
cluster_name = step.create_secondary_cluster_backend_targets.cluster_name
cluster_tag_key = global.backend_tag_key
hosts = step.create_secondary_cluster_backend_targets.hosts
license = (matrix.secondary_backend == "consul" && matrix.consul_edition == "ent") ? step.read_backend_license.license : null
release = {
edition = matrix.consul_edition
version = matrix.consul_version
}
}
}
step "create_secondary_cluster" {
module = module.vault_cluster
depends_on = [
step.create_secondary_backend_cluster,
step.build_vault,
step.create_secondary_cluster_targets
]
providers = {
enos = local.enos_provider[matrix.distro]
}
verifies = [
// verified in modules
quality.consul_autojoin_aws,
quality.consul_config_file,
quality.consul_ha_leader_election,
quality.consul_service_start_client,
// verified in enos_consul_start resource
quality.consul_api_agent_host_read,
quality.consul_api_health_node_read,
quality.consul_api_operator_raft_config_read,
quality.consul_health_state_passing_read_nodes_minimum,
quality.consul_operator_raft_configuration_read_voters_minimum,
quality.consul_service_systemd_notified,
quality.consul_service_systemd_unit,
]
variables {
artifactory_release = matrix.artifact_source == "artifactory" ? step.build_vault.vault_artifactory_release : null
backend_cluster_name = step.create_secondary_cluster_backend_targets.cluster_name
backend_cluster_tag_key = global.backend_tag_key
config_mode = matrix.config_mode
consul_license = (matrix.secondary_backend == "consul" && matrix.consul_edition == "ent") ? step.read_backend_license.license : null
cluster_name = step.create_secondary_cluster_targets.cluster_name
consul_release = matrix.secondary_backend == "consul" ? {
edition = matrix.consul_edition
version = matrix.consul_version
} : null
enable_audit_devices = var.vault_enable_audit_devices
hosts = step.create_secondary_cluster_targets.hosts
install_dir = global.vault_install_dir[matrix.artifact_type]
ip_version = matrix.ip_version
license = matrix.edition != "ce" ? step.read_vault_license.license : null
local_artifact_path = local.artifact_path
manage_service = local.manage_service
packages = concat(global.packages, global.distro_packages[matrix.distro][global.distro_version[matrix.distro]])
seal_attributes = step.create_secondary_seal_key.attributes
seal_type = matrix.secondary_seal
storage_backend = matrix.secondary_backend
}
}
step "wait_for_secondary_cluster_leader" {
description = global.description.wait_for_cluster_to_have_leader
module = module.vault_wait_for_leader
depends_on = [step.create_secondary_cluster]
providers = {
enos = local.enos_provider[matrix.distro]
}
verifies = [
quality.vault_api_sys_leader_read,
quality.vault_unseal_ha_leader_election,
]
variables {
hosts = step.create_secondary_cluster_targets.hosts
ip_version = matrix.ip_version
timeout = 120 // seconds
vault_addr = step.create_secondary_cluster.api_addr_localhost
vault_install_dir = local.vault_install_dir
vault_root_token = step.create_secondary_cluster.root_token
}
}
step "verify_that_vault_primary_cluster_is_unsealed" {
description = global.description.verify_vault_unsealed
module = module.vault_wait_for_cluster_unsealed
depends_on = [
step.create_primary_cluster,
step.wait_for_primary_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.create_primary_cluster_targets.hosts
vault_addr = step.create_primary_cluster.api_addr_localhost
vault_install_dir = global.vault_install_dir[matrix.artifact_type]
}
}
step "verify_that_vault_secondary_cluster_is_unsealed" {
description = global.description.verify_vault_unsealed
module = module.vault_wait_for_cluster_unsealed
depends_on = [
step.create_secondary_cluster,
step.wait_for_secondary_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.create_secondary_cluster_targets.hosts
vault_addr = step.create_secondary_cluster.api_addr_localhost
vault_install_dir = global.vault_install_dir[matrix.artifact_type]
}
}
step "get_primary_cluster_ips" {
description = global.description.get_vault_cluster_ip_addresses
module = module.vault_get_cluster_ips
depends_on = [step.verify_that_vault_primary_cluster_is_unsealed]
providers = {
enos = local.enos_provider[matrix.distro]
}
verifies = [
quality.vault_api_sys_ha_status_read,
quality.vault_api_sys_leader_read,
quality.vault_cli_operator_members,
]
variables {
hosts = step.create_primary_cluster_targets.hosts
ip_version = matrix.ip_version
vault_addr = step.create_primary_cluster.api_addr_localhost
vault_install_dir = global.vault_install_dir[matrix.artifact_type]
vault_root_token = step.create_primary_cluster.root_token
}
}
step "get_secondary_cluster_ips" {
description = global.description.get_vault_cluster_ip_addresses
module = module.vault_get_cluster_ips
depends_on = [step.verify_that_vault_secondary_cluster_is_unsealed]
providers = {
enos = local.enos_provider[matrix.distro]
}
verifies = [
quality.vault_api_sys_ha_status_read,
quality.vault_api_sys_leader_read,
quality.vault_cli_operator_members,
]
variables {
hosts = step.create_secondary_cluster_targets.hosts
ip_version = matrix.ip_version
vault_addr = step.create_secondary_cluster.api_addr_localhost
vault_install_dir = global.vault_install_dir[matrix.artifact_type]
vault_root_token = step.create_secondary_cluster.root_token
}
}
step "verify_vault_version" {
description = global.description.verify_vault_version
module = module.vault_verify_version
depends_on = [step.get_primary_cluster_ips]
providers = {
enos = local.enos_provider[matrix.distro]
}
verifies = [
quality.vault_api_sys_version_history_keys,
quality.vault_api_sys_version_history_key_info,
quality.vault_version_build_date,
quality.vault_version_edition,
quality.vault_version_release,
]
variables {
hosts = step.create_primary_cluster_targets.hosts
vault_addr = step.create_primary_cluster.api_addr_localhost
vault_edition = matrix.edition
vault_install_dir = global.vault_install_dir[matrix.artifact_type]
vault_product_version = matrix.artifact_source == "local" ? step.get_local_metadata.version : var.vault_product_version
vault_revision = matrix.artifact_source == "local" ? step.get_local_metadata.revision : var.vault_revision
vault_build_date = matrix.artifact_source == "local" ? step.get_local_metadata.build_date : var.vault_build_date
vault_root_token = step.create_primary_cluster.root_token
}
}
step "verify_ui" {
description = global.description.verify_ui
module = module.vault_verify_ui
depends_on = [step.get_primary_cluster_ips]
providers = {
enos = local.enos_provider[matrix.distro]
}
verifies = quality.vault_ui_assets
variables {
vault_addr = step.create_primary_cluster.api_addr_localhost
hosts = step.create_primary_cluster_targets.hosts
}
}
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 = {
enos = local.enos_provider[matrix.distro]
}
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_kv_write,
]
variables {
hosts = step.create_primary_cluster_targets.hosts
leader_host = step.get_primary_cluster_ips.leader_host
vault_addr = step.create_primary_cluster.api_addr_localhost
vault_install_dir = global.vault_install_dir[matrix.artifact_type]
vault_root_token = step.create_primary_cluster.root_token
}
}
# ================================================
# DISASTER RECOVERY (DR) REPLICATION SETUP
# ================================================
# - Wait for seal rewrap to complete on both clusters.
# - Configure DR primary replication on cluster A.
# - Generate secondary token on cluster A.
# - Configure DR secondary replication on cluster B.
# - Confirm replication status on both clusters.
// Wait for our seals to finish any inflight rewraps before we enable DR replication as we don't
// want to accidentally swap seal info on the secondary before it has finished.
step "configure_dr_replication_primary" {
description = <<-EOF
Create the necessary superuser auth policy necessary for DR replication, assign it
to a our previously create test user, and enable DR replication on the primary
cluster.
EOF
module = module.vault_setup_dr_primary
depends_on = [
step.get_primary_cluster_ips,
step.get_secondary_cluster_ips,
step.verify_secrets_engines_on_primary,
]
providers = {
enos = local.enos_provider[matrix.distro]
}
verifies = [
quality.vault_api_sys_auth_userpass_user_write,
quality.vault_api_sys_policy_write,
quality.vault_api_sys_replication_dr_primary_enable_write,
quality.vault_cli_policy_write,
]
variables {
ip_version = matrix.ip_version
primary_leader_host = step.get_primary_cluster_ips.leader_host
vault_addr = step.create_primary_cluster.api_addr_localhost
vault_install_dir = global.vault_install_dir[matrix.artifact_type]
vault_root_token = step.create_primary_cluster.root_token
}
}
step "generate_secondary_token" {
description = <<-EOF
Generate a random token and configure the DR replication primary secondary-token and
configure the Vault cluster primary replication with the token. Export the wrapping token
so that secondary clusters can utilize it.
EOF
module = module.generate_secondary_token
depends_on = [step.configure_dr_replication_primary]
verifies = quality.vault_api_sys_replication_dr_primary_secondary_token_write
providers = {
enos = local.enos_provider[matrix.distro]
}
variables {
ip_version = matrix.ip_version
primary_leader_host = step.get_primary_cluster_ips.leader_host
replication_type = "dr"
vault_addr = step.create_primary_cluster.api_addr_localhost
vault_install_dir = global.vault_install_dir[matrix.artifact_type]
vault_root_token = step.create_primary_cluster.root_token
}
}
step "wait_for_primary_seal_rewrap" {
module = module.vault_wait_for_seal_rewrap
depends_on = [
step.generate_secondary_token,
]
providers = {
enos = local.enos_provider[matrix.distro]
}
variables {
hosts = step.create_primary_cluster.hosts
vault_addr = step.create_primary_cluster.api_addr_localhost
vault_install_dir = global.vault_install_dir[matrix.artifact_type]
vault_root_token = step.create_primary_cluster.root_token
}
}
step "wait_for_secondary_seal_rewrap" {
module = module.vault_wait_for_seal_rewrap
depends_on = [
step.wait_for_primary_seal_rewrap,
]
providers = {
enos = local.enos_provider[matrix.distro]
}
variables {
hosts = step.create_secondary_cluster.hosts
vault_addr = step.create_secondary_cluster.api_addr_localhost
vault_install_dir = global.vault_install_dir[matrix.artifact_type]
vault_root_token = step.create_secondary_cluster.root_token
}
}
step "configure_dr_replication_secondary" {
description = <<-EOF
Enable dr replication on the secondary cluster with the wrapping token created by
the primary cluster.
EOF
module = module.vault_setup_replication_secondary
depends_on = [
step.wait_for_secondary_seal_rewrap,
step.generate_secondary_token,
]
providers = {
enos = local.enos_provider[matrix.distro]
}
verifies = [
quality.vault_api_sys_leader_read,
quality.vault_api_sys_replication_dr_secondary_enable_write,
quality.vault_api_sys_replication_dr_status_read,
]
variables {
ip_version = matrix.ip_version
secondary_leader_host = step.get_secondary_cluster_ips.leader_host
replication_type = "dr"
vault_addr = step.create_secondary_cluster.api_addr_localhost
vault_install_dir = global.vault_install_dir[matrix.artifact_type]
vault_root_token = step.create_secondary_cluster.root_token
wrapping_token = step.generate_secondary_token.secondary_token
}
}
step "unseal_secondary_followers" {
description = <<-EOF
After replication is enabled the secondary cluster followers need to be unsealed.
Secondary unseal keys are passed differently depending primary and secondary seal
type combinations. See the guide for more information:
https://developer.hashicorp.com/vault/docs/enterprise/replication#seals
EOF
module = module.vault_unseal_replication_followers
depends_on = [
step.configure_dr_replication_secondary
]
providers = {
enos = local.enos_provider[matrix.distro]
}
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]
vault_seal_type = matrix.primary_seal == "shamir" ? matrix.primary_seal : matrix.secondary_seal
vault_unseal_keys = matrix.primary_seal == "shamir" ? step.create_primary_cluster.unseal_keys_hex : step.create_primary_cluster.recovery_keys_hex
}
}
step "verify_secondary_cluster_is_unsealed_after_enabling_replication" {
description = global.description.verify_vault_unsealed
module = module.vault_wait_for_cluster_unsealed
depends_on = [
step.unseal_secondary_followers
]
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.create_secondary_cluster_targets.hosts
vault_addr = step.create_secondary_cluster.api_addr_localhost
vault_install_dir = global.vault_install_dir[matrix.artifact_type]
}
}
step "verify_dr_replication" {
description = <<-EOF
Verify that the DR replication status meets our expectations after enabling replication
and ensuring that all secondary nodes are unsealed.
EOF
module = module.vault_verify_dr_replication
depends_on = [
step.configure_dr_replication_secondary,
step.unseal_secondary_followers,
step.verify_secondary_cluster_is_unsealed_after_enabling_replication,
]
providers = {
enos = local.enos_provider[matrix.distro]
}
verifies = [
quality.vault_api_sys_replication_dr_read_connection_status_connected,
quality.vault_api_sys_replication_dr_status_read,
quality.vault_api_sys_replication_dr_status_read_cluster_address,
quality.vault_api_sys_replication_dr_status_read_state_not_idle,
quality.vault_api_sys_replication_dr_status_known_primary_cluster_addrs,
]
variables {
ip_version = matrix.ip_version
primary_leader_host = step.get_primary_cluster_ips.leader_host
secondary_leader_host = step.get_secondary_cluster_ips.leader_host
vault_addr = step.create_primary_cluster.api_addr_localhost
vault_install_dir = global.vault_install_dir[matrix.artifact_type]
}
}
# ==============================
# FAILOVER SCENARIO STEPS
# ==============================
# 1. Generate a batch DR operation token.
# 2. Promote the current secondary cluster B to become the new primary cluster.
# 3. Demote cluster A to secondary status.
# 4. Test access to Vault data on the new primary cluster B.
# 5. Point demoted cluster A to the new primary cluster B (Multistep process).
# 6. Verify that the data is replicated to the new primary cluster B.
step "generate_batch_dr_operation_token" {
description = <<-EOF
Generate a batch DR operation token that you can use to promote and demote clusters as needed.
EOF
module = module.generate_dr_operation_token
depends_on = [step.verify_dr_replication]
providers = {
enos = local.enos_provider[matrix.distro]
}
variables {
vault_install_dir = local.vault_install_dir
ip_version = matrix.ip_version
primary_leader_host = step.get_primary_cluster_ips.leader_host
vault_addr = step.create_primary_cluster.api_addr_localhost
vault_root_token = step.create_primary_cluster.root_token
storage_backend = matrix.primary_backend
}
}
step "vault_failover_promote_dr_secondary_cluster" {
description = <<-EOF
Promote the secondary cluster to be the primary cluster. This step will also
generate a new DR operation token for the secondary cluster to connect to the new
primary cluster.
EOF
module = module.vault_failover_promote_dr_secondary
depends_on = [step.generate_batch_dr_operation_token]
providers = {
enos = local.enos_provider[matrix.distro]
}
variables {
vault_install_dir = local.vault_install_dir
vault_addr = step.create_secondary_cluster.api_addr_localhost
ip_version = matrix.ip_version
secondary_leader_host = step.get_secondary_cluster_ips.leader_host
vault_root_token = step.create_secondary_cluster.root_token
dr_operation_token = step.generate_batch_dr_operation_token.dr_operation_token
}
}
step "wait_for_promoted_cluster_leader" {
description = global.description.wait_for_cluster_to_have_leader
module = module.vault_wait_for_leader
depends_on = [step.vault_failover_promote_dr_secondary_cluster]
providers = {
enos = local.enos_provider[matrix.distro]
}
verifies = [
quality.vault_api_sys_leader_read,
quality.vault_unseal_ha_leader_election,
]
variables {
hosts = step.create_secondary_cluster_targets.hosts
ip_version = matrix.ip_version
timeout = 120 // seconds
vault_addr = step.create_secondary_cluster.api_addr_localhost
vault_install_dir = local.vault_install_dir
vault_root_token = step.create_secondary_cluster.root_token
}
}
step "vault_failover_demote_dr_primary_cluster" {
description = <<-EOF
Demote the primary cluster to be the secondary cluster. This step will also
generate a new DR operation token for the secondary cluster to connect to the new
primary cluster.
EOF
module = module.vault_failover_demote_dr_primary
depends_on = [step.wait_for_promoted_cluster_leader]
providers = {
enos = local.enos_provider[matrix.distro]
}
variables {
ip_version = matrix.ip_version
primary_leader_host = step.get_primary_cluster_ips.leader_host
vault_addr = step.create_primary_cluster.api_addr_localhost
vault_install_dir = local.vault_install_dir
vault_root_token = step.create_primary_cluster.root_token
}
}
step "wait_for_demoted_cluster_leader" {
description = global.description.wait_for_cluster_to_have_leader
module = module.vault_wait_for_leader
depends_on = [step.vault_failover_demote_dr_primary_cluster]
providers = {
enos = local.enos_provider[matrix.distro]
}
verifies = [
quality.vault_api_sys_leader_read,
quality.vault_unseal_ha_leader_election,
]
variables {
hosts = step.create_primary_cluster_targets.hosts
ip_version = matrix.ip_version
timeout = 120 // seconds
vault_addr = step.create_primary_cluster.api_addr_localhost
vault_install_dir = local.vault_install_dir
vault_root_token = step.create_primary_cluster.root_token
}
}
step "verify_new_primary_cluster_unsealed" {
description = global.description.verify_vault_unsealed
module = module.vault_wait_for_cluster_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.create_secondary_cluster_targets.hosts
timeout = 120 // seconds
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_secrets_engines_read
module = module.vault_verify_secrets_engines_read
depends_on = [
step.wait_for_demoted_cluster_leader,
step.verify_new_primary_cluster_unsealed,
]
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_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]
vault_root_token = step.create_secondary_cluster.root_token
verify_pki_certs = false
}
}
step "generate_demoted_secondary_public_key" {
description = <<-EOF
Generate a random token and configure the DR replication primary secondary-token and
configure the Vault cluster primary replication with the token. Export the wrapping token
so that secondary clusters can utilize it.
EOF
module = module.generate_secondary_public_key
depends_on = [
step.verify_replicated_data_during_failover,
]
verifies = quality.vault_api_sys_replication_dr_primary_secondary_token_write
providers = {
enos = local.enos_provider[matrix.distro]
}
variables {
ip_version = matrix.ip_version
primary_leader_host = step.get_primary_cluster_ips.leader_host
vault_addr = step.create_primary_cluster.api_addr_localhost
vault_install_dir = global.vault_install_dir[matrix.artifact_type]
vault_root_token = step.create_primary_cluster.root_token
}
}
step "generate_demoted_secondary_token" {
description = <<-EOF
Generate a random token and configure the DR replication primary secondary-token and
configure the Vault cluster primary replication with the token. Export the wrapping token
so that secondary clusters can utilize it.
EOF
module = module.generate_failover_secondary_token
depends_on = [step.generate_demoted_secondary_public_key]
verifies = quality.vault_api_sys_replication_dr_primary_secondary_token_write
providers = {
enos = local.enos_provider[matrix.distro]
}
variables {
ip_version = matrix.ip_version
primary_leader_host = step.get_secondary_cluster_ips.leader_host
secondary_public_key = step.generate_demoted_secondary_public_key.secondary_public_key
vault_addr = step.create_secondary_cluster.api_addr_localhost
vault_install_dir = global.vault_install_dir[matrix.artifact_type]
vault_root_token = step.create_primary_cluster.root_token
}
}
step "vault_failover_update_dr_primary_cluster" {
description = <<-EOF
Update the secondary cluster to connect to the new primary cluster.
EOF
module = module.vault_failover_update_dr_primary
depends_on = [
step.generate_demoted_secondary_token,
step.vault_failover_demote_dr_primary_cluster
]
providers = {
enos = local.enos_provider[matrix.distro]
}
variables {
vault_install_dir = local.vault_install_dir
ip_version = matrix.ip_version
secondary_leader_host = step.get_primary_cluster_ips.leader_host
vault_addr = step.create_primary_cluster.api_addr_localhost
vault_root_token = step.create_primary_cluster.root_token
dr_operation_token = step.generate_batch_dr_operation_token.dr_operation_token
wrapping_token = step.generate_demoted_secondary_token.secondary_token
}
}
step "verify_failover_dr_replication" {
description = <<-EOF
Verify that the DR replication status meets our expectations after enabling replication
and ensuring that all secondary nodes are unsealed.
EOF
module = module.vault_verify_dr_replication
depends_on = [step.vault_failover_update_dr_primary_cluster]
providers = {
enos = local.enos_provider[matrix.distro]
}
verifies = [
quality.vault_api_sys_replication_dr_read_connection_status_connected,
quality.vault_api_sys_replication_dr_status_read,
quality.vault_api_sys_replication_dr_status_read_cluster_address,
quality.vault_api_sys_replication_dr_status_read_state_not_idle,
quality.vault_api_sys_replication_dr_status_known_primary_cluster_addrs,
]
variables {
ip_version = matrix.ip_version
primary_leader_host = step.get_secondary_cluster_ips.leader_host
secondary_leader_host = step.get_primary_cluster_ips.leader_host
vault_addr = step.create_primary_cluster.api_addr_localhost
vault_install_dir = global.vault_install_dir[matrix.artifact_type]
}
}
step "verify_failover_replicated_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.verify_secrets_engines_on_primary,
step.verify_failover_dr_replication
]
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_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]
vault_root_token = step.create_secondary_cluster.root_token
verify_pki_certs = false
}
}
// Output the results of the scenario.
output "audit_device_file_path" {
description = "The file path for the file audit device, if enabled"
value = step.create_primary_cluster.audit_device_file_path
}
output "primary_cluster_hosts" {
description = "The Vault primary cluster target hosts"
value = step.create_primary_cluster_targets.hosts
}
output "primary_cluster_root_token" {
description = "The Vault primary cluster root token"
value = step.create_primary_cluster.root_token
}
output "primary_cluster_unseal_keys_b64" {
description = "The Vault primary cluster unseal keys"
value = step.create_primary_cluster.unseal_keys_b64
}
output "primary_cluster_unseal_keys_hex" {
description = "The Vault primary cluster unseal keys hex"
value = step.create_primary_cluster.unseal_keys_hex
}
output "primary_cluster_recovery_key_shares" {
description = "The Vault primary cluster recovery key shares"
value = step.create_primary_cluster.recovery_key_shares
}
output "primary_cluster_recovery_keys_b64" {
description = "The Vault primary cluster recovery keys b64"
value = step.create_primary_cluster.recovery_keys_b64
}
output "primary_cluster_recovery_keys_hex" {
description = "The Vault primary cluster recovery keys hex"
value = step.create_primary_cluster.recovery_keys_hex
}
output "secondary_cluster_hosts" {
description = "The Vault secondary cluster public IPs"
value = step.create_secondary_cluster_targets.hosts
}
output "secondary_cluster_root_token" {
description = "The Vault secondary cluster root token"
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
}
output "batch_dr_operation_token" {
description = "The dr primary replication token"
value = step.generate_batch_dr_operation_token.dr_operation_token
}
output "demoted_secondary_public_key" {
description = "The dr secondary public key"
value = step.generate_demoted_secondary_public_key.secondary_public_key
}
output "demoted_secondary_token" {
description = "The dr secondary public key"
value = step.generate_demoted_secondary_token.secondary_token
}
output "initial_primary_replication_status" {
description = "The Vault primary cluster dr replication status"
value = step.verify_dr_replication.primary_replication_status
}
output "initial_known_primary_cluster_addresses" {
description = "The Vault primary cluster known primary cluster addresses"
value = step.verify_dr_replication.known_primary_cluster_addrs
}
output "initial_secondary_dr_replication_status" {
description = "The Vault secondary cluster dr replication status"
value = step.verify_dr_replication.secondary_replication_status
}
output "intial_primary_replication_data_secondaries" {
description = "The Vault primary cluster secondaries connection status"
value = step.verify_dr_replication.primary_replication_data_secondaries
}
output "initial_secondary_replication_data_primaries" {
description = "The Vault secondary cluster primaries connection status"
value = step.verify_dr_replication.secondary_replication_data_primaries
}
output "get_primary_cluster_ips_leader" {
description = "The Vault updated primary cluster dr replication status"
value = step.get_primary_cluster_ips.leader_public_ip
}
output "get_secondary_cluster_ips_leader" {
description = "The Vault updated primary cluster dr replication status"
value = step.get_secondary_cluster_ips.leader_public_ip
}
output "failover_primary_replication_status" {
description = "The Vault updated primary cluster dr replication status"
value = step.verify_failover_dr_replication.primary_replication_status
}
output "failover_known_primary_cluster_addresses" {
description = "The Vault secondary cluster dr replication status"
value = step.verify_failover_dr_replication.known_primary_cluster_addrs
}
output "failover_secondary_replication_status" {
description = "The Vault updated secondary cluster dr replication status"
value = step.verify_failover_dr_replication.secondary_replication_status
}
output "failover_primary_replication_data_secondaries" {
description = "The Vault updated primary cluster secondaries connection status"
value = step.verify_failover_dr_replication.primary_replication_data_secondaries
}
output "failover_secondary_replication_data_primaries" {
description = "The Vault updated secondary cluster primaries connection status"
value = step.verify_failover_dr_replication.secondary_replication_data_primaries
}
}