From ccceb19d028fe97e4880bb7f1be20ddc07f17950 Mon Sep 17 00:00:00 2001 From: Vault Automation Date: Wed, 18 Feb 2026 08:36:34 -0500 Subject: [PATCH] committed dynamic-roles.sh (#11833) (#12356) * Dynamic-roles:updated with review comments * Fix enos formatting: align variable assignments in scenario files * Fix terraform formatting in LDAP modules * Fix shell script formatting: add newlines and fix indentation * Fix shellcheck warnings: quote variables to prevent globbing * Change LDAP secrets engine verification to true * Add variable for LDAP static role verification * Configure SSH transport for LDAP dynamic roles Added SSH transport configuration for LDAP dynamic roles. * Fix formatting in ldap.tf * Change LDAP secrets engine verification to false --------- Co-authored-by: Amala Mathew Co-authored-by: mathew-amala Co-authored-by: Luis (LT) Carbonell --- enos/enos-scenario-agent.hcl | 15 +- enos/enos-scenario-autopilot.hcl | 15 +- enos/enos-scenario-dr-replication.hcl | 2 + enos/enos-scenario-pr-replication.hcl | 1 + enos/enos-scenario-proxy.hcl | 15 +- enos/enos-scenario-seal-ha.hcl | 15 +- enos/enos-scenario-smoke.hcl | 15 +- enos/enos-scenario-upgrade.hcl | 16 +- .../modules/read/ldap.tf | 12 +- .../modules/read/ldap/ldap.tf | 52 +++++++ .../modules/read/main.tf | 6 + .../ldap/Dynamic-roles/dynamic-roles-audit.sh | 63 ++++++++ .../Dynamic-roles/dynamic-roles-deletion.sh | 145 ++++++++++++++++++ .../Dynamic-roles/dynamic-roles-listing.sh | 62 ++++++++ .../Dynamic-roles/dynamic-roles-rollback.sh | 87 +++++++++++ .../Dynamic-roles/dynamic-roles-validation.sh | 74 +++++++++ .../ldap/Dynamic-roles/dynamic-roles.sh | 82 ++++++++++ 17 files changed, 628 insertions(+), 49 deletions(-) create mode 100644 enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-audit.sh create mode 100644 enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-deletion.sh create mode 100644 enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-listing.sh create mode 100644 enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-rollback.sh create mode 100644 enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-validation.sh create mode 100644 enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles.sh diff --git a/enos/enos-scenario-agent.hcl b/enos/enos-scenario-agent.hcl index d8dcfcd219..579eea9fad 100644 --- a/enos/enos-scenario-agent.hcl +++ b/enos/enos-scenario-agent.hcl @@ -624,13 +624,14 @@ scenario "agent" { ] variables { - create_state = step.verify_secrets_engines_create.state - hosts = step.get_vault_cluster_ips.follower_hosts - ip_version = matrix.ip_version - vault_addr = step.create_vault_cluster.api_addr_localhost - vault_edition = matrix.edition - vault_install_dir = global.vault_install_dir[matrix.artifact_type] - vault_root_token = step.create_vault_cluster.root_token + create_state = step.verify_secrets_engines_create.state + hosts = step.get_vault_cluster_ips.follower_hosts + ip_version = matrix.ip_version + vault_addr = step.create_vault_cluster.api_addr_localhost + vault_edition = matrix.edition + vault_install_dir = global.vault_install_dir[matrix.artifact_type] + vault_root_token = step.create_vault_cluster.root_token + vault_audit_log_path = step.create_vault_cluster.audit_device_file_path } } diff --git a/enos/enos-scenario-autopilot.hcl b/enos/enos-scenario-autopilot.hcl index c6fe07e264..6c5586974b 100644 --- a/enos/enos-scenario-autopilot.hcl +++ b/enos/enos-scenario-autopilot.hcl @@ -635,13 +635,14 @@ scenario "autopilot" { ] variables { - create_state = step.verify_secrets_engines_create.state - hosts = step.get_updated_vault_cluster_ips.follower_hosts - ip_version = matrix.ip_version - vault_addr = step.upgrade_vault_cluster_with_autopilot.api_addr_localhost - vault_edition = matrix.edition - vault_install_dir = local.vault_install_dir - vault_root_token = step.create_vault_cluster.root_token + create_state = step.verify_secrets_engines_create.state + hosts = step.get_updated_vault_cluster_ips.follower_hosts + ip_version = matrix.ip_version + vault_addr = step.upgrade_vault_cluster_with_autopilot.api_addr_localhost + vault_edition = matrix.edition + vault_install_dir = local.vault_install_dir + vault_root_token = step.create_vault_cluster.root_token + vault_audit_log_path = step.create_vault_cluster.audit_device_file_path } } diff --git a/enos/enos-scenario-dr-replication.hcl b/enos/enos-scenario-dr-replication.hcl index 5f4b5dc380..46066cc87f 100644 --- a/enos/enos-scenario-dr-replication.hcl +++ b/enos/enos-scenario-dr-replication.hcl @@ -1163,6 +1163,7 @@ scenario "dr_replication" { verify_pki_certs = false verify_aws_engine_creds = false verify_ssh_secrets = false + vault_audit_log_path = step.create_secondary_cluster.audit_device_file_path } } @@ -1306,6 +1307,7 @@ scenario "dr_replication" { verify_pki_certs = false verify_aws_engine_creds = false verify_ssh_secrets = false + vault_audit_log_path = step.create_secondary_cluster.audit_device_file_path } } diff --git a/enos/enos-scenario-pr-replication.hcl b/enos/enos-scenario-pr-replication.hcl index d95dc1af98..02b462ef9d 100644 --- a/enos/enos-scenario-pr-replication.hcl +++ b/enos/enos-scenario-pr-replication.hcl @@ -995,6 +995,7 @@ scenario "pr_replication" { verify_pki_certs = false verify_aws_engine_creds = false verify_ssh_secrets = false + vault_audit_log_path = step.create_secondary_cluster.audit_device_file_path } } diff --git a/enos/enos-scenario-proxy.hcl b/enos/enos-scenario-proxy.hcl index c973ebcf5f..8948949eb4 100644 --- a/enos/enos-scenario-proxy.hcl +++ b/enos/enos-scenario-proxy.hcl @@ -600,13 +600,14 @@ scenario "proxy" { ] variables { - create_state = step.verify_secrets_engines_create.state - hosts = step.get_vault_cluster_ips.follower_hosts - ip_version = matrix.ip_version - vault_addr = step.create_vault_cluster.api_addr_localhost - vault_edition = matrix.edition - vault_install_dir = global.vault_install_dir[matrix.artifact_type] - vault_root_token = step.create_vault_cluster.root_token + create_state = step.verify_secrets_engines_create.state + hosts = step.get_vault_cluster_ips.follower_hosts + ip_version = matrix.ip_version + vault_addr = step.create_vault_cluster.api_addr_localhost + vault_edition = matrix.edition + vault_install_dir = global.vault_install_dir[matrix.artifact_type] + vault_root_token = step.create_vault_cluster.root_token + vault_audit_log_path = step.create_vault_cluster.audit_device_file_path } } diff --git a/enos/enos-scenario-seal-ha.hcl b/enos/enos-scenario-seal-ha.hcl index e0fe63713d..1d8a5f8ac4 100644 --- a/enos/enos-scenario-seal-ha.hcl +++ b/enos/enos-scenario-seal-ha.hcl @@ -850,13 +850,14 @@ scenario "seal_ha" { ] variables { - create_state = step.verify_secrets_engines_create.state - hosts = step.get_updated_cluster_ips.follower_hosts - ip_version = matrix.ip_version - vault_addr = step.create_vault_cluster.api_addr_localhost - vault_edition = matrix.edition - vault_install_dir = global.vault_install_dir[matrix.artifact_type] - vault_root_token = step.create_vault_cluster.root_token + create_state = step.verify_secrets_engines_create.state + hosts = step.get_updated_cluster_ips.follower_hosts + ip_version = matrix.ip_version + vault_addr = step.create_vault_cluster.api_addr_localhost + vault_edition = matrix.edition + vault_install_dir = global.vault_install_dir[matrix.artifact_type] + vault_root_token = step.create_vault_cluster.root_token + vault_audit_log_path = step.create_vault_cluster.audit_device_file_path } } diff --git a/enos/enos-scenario-smoke.hcl b/enos/enos-scenario-smoke.hcl index 0097acbdfd..8963999901 100644 --- a/enos/enos-scenario-smoke.hcl +++ b/enos/enos-scenario-smoke.hcl @@ -683,13 +683,14 @@ scenario "smoke" { ] variables { - create_state = step.verify_secrets_engines_create.state - hosts = step.get_vault_cluster_ips.follower_hosts - ip_version = matrix.ip_version - vault_addr = step.create_vault_cluster.api_addr_localhost - vault_edition = matrix.edition - vault_install_dir = global.vault_install_dir[matrix.artifact_type] - vault_root_token = step.create_vault_cluster.root_token + create_state = step.verify_secrets_engines_create.state + hosts = step.get_vault_cluster_ips.follower_hosts + ip_version = matrix.ip_version + vault_addr = step.create_vault_cluster.api_addr_localhost + vault_edition = matrix.edition + vault_install_dir = global.vault_install_dir[matrix.artifact_type] + vault_root_token = step.create_vault_cluster.root_token + vault_audit_log_path = step.create_vault_cluster.audit_device_file_path } } diff --git a/enos/enos-scenario-upgrade.hcl b/enos/enos-scenario-upgrade.hcl index ac69be03c5..a1a4c5aefd 100644 --- a/enos/enos-scenario-upgrade.hcl +++ b/enos/enos-scenario-upgrade.hcl @@ -711,14 +711,14 @@ scenario "upgrade" { ] variables { - create_state = step.verify_secrets_engines_create.state - hosts = step.get_updated_vault_cluster_ips.follower_hosts - ip_version = matrix.ip_version - vault_addr = step.create_vault_cluster.api_addr_localhost - vault_edition = matrix.edition - vault_install_dir = global.vault_install_dir[matrix.artifact_type] - vault_root_token = step.create_vault_cluster.root_token - + create_state = step.verify_secrets_engines_create.state + hosts = step.get_updated_vault_cluster_ips.follower_hosts + ip_version = matrix.ip_version + vault_addr = step.create_vault_cluster.api_addr_localhost + vault_edition = matrix.edition + vault_install_dir = global.vault_install_dir[matrix.artifact_type] + vault_root_token = step.create_vault_cluster.root_token + vault_audit_log_path = step.create_vault_cluster.audit_device_file_path } } diff --git a/enos/modules/verify_secrets_engines/modules/read/ldap.tf b/enos/modules/verify_secrets_engines/modules/read/ldap.tf index e2d3c13b37..12c4ec0520 100644 --- a/enos/modules/verify_secrets_engines/modules/read/ldap.tf +++ b/enos/modules/verify_secrets_engines/modules/read/ldap.tf @@ -6,11 +6,11 @@ module "verify_ldap_secret_engine" { count = var.ldap_enabled ? 1 : 0 source = "./ldap" - create_state = var.create_state - vault_addr = var.vault_addr - vault_root_token = var.vault_root_token - vault_install_dir = var.vault_install_dir - - hosts = var.hosts + create_state = var.create_state + vault_addr = var.vault_addr + vault_root_token = var.vault_root_token + vault_install_dir = var.vault_install_dir + hosts = var.hosts + vault_audit_log_path = var.vault_audit_log_path } diff --git a/enos/modules/verify_secrets_engines/modules/read/ldap/ldap.tf b/enos/modules/verify_secrets_engines/modules/read/ldap/ldap.tf index 6cbeb80825..f971810264 100644 --- a/enos/modules/verify_secrets_engines/modules/read/ldap/ldap.tf +++ b/enos/modules/verify_secrets_engines/modules/read/ldap/ldap.tf @@ -8,6 +8,9 @@ terraform { } } } +locals { + dynamic_role_name = "dynamic-role" +} variable "hosts" { type = map(object({ @@ -80,6 +83,17 @@ variable "enable_rollback_verification" { default = true } +variable "enable_dynamic_role_verification" { + type = bool + description = "Enable LDAP secrets engine dynamic role" + default = true +} + +variable "vault_audit_log_path" { + type = string + description = "The file path for the audit device" +} + variable "enable_static_role_verification" { type = bool description = "Enable LDAP secrets engine static role verification" @@ -307,6 +321,43 @@ resource "enos_remote_exec" "ldap_library_checkout_lease_renew" { } } } +# Configure and verify LDAP secrets engine rollback behavior +resource "enos_remote_exec" "verify_dynamic_role" { + count = var.enable_dynamic_role_verification ? 1 : 0 + + depends_on = [ + enos_remote_exec.ldap_verify_secrets, + enos_remote_exec.ldap_verify_rotation + ] + environment = { + MOUNT = var.create_state.ldap.ldap_mount + LDAP_SERVER = var.create_state.ldap.host.private_ip + LDAP_PORT = var.create_state.ldap.port + LDAP_USERNAME = var.create_state.ldap.username + LDAP_ADMIN_PW = var.create_state.ldap.pw + VAULT_ADDR = var.vault_addr + VAULT_INSTALL_DIR = var.vault_install_dir + VAULT_TOKEN = var.vault_root_token + ROLE_NAME = local.dynamic_role_name + DEFAULT_TTL = tostring(var.default_ttl) + MAX_TTL = tostring(var.max_ttl) + } + + scripts = [ + abspath("${path.module}/../../../scripts/ldap/Dynamic-roles/dynamic-roles.sh"), + abspath("${path.module}/../../../scripts/ldap/Dynamic-roles/dynamic-roles-validation.sh"), + abspath("${path.module}/../../../scripts/ldap/Dynamic-roles/dynamic-roles-listing.sh"), + abspath("${path.module}/../../../scripts/ldap/Dynamic-roles/dynamic-roles-audit.sh"), + abspath("${path.module}/../../../scripts/ldap/Dynamic-roles/dynamic-roles-rollback.sh"), + abspath("${path.module}/../../../scripts/ldap/Dynamic-roles/dynamic-roles-deletion.sh") + ] + transport = { + ssh = { + host = var.hosts[0].public_ip + } + } +} + # Verify Static role resource "enos_remote_exec" "ldap_static_roles" { @@ -333,3 +384,4 @@ resource "enos_remote_exec" "ldap_static_roles" { } } } + diff --git a/enos/modules/verify_secrets_engines/modules/read/main.tf b/enos/modules/verify_secrets_engines/modules/read/main.tf index dbf3f2403d..52bd24c027 100644 --- a/enos/modules/verify_secrets_engines/modules/read/main.tf +++ b/enos/modules/verify_secrets_engines/modules/read/main.tf @@ -50,6 +50,12 @@ variable "vault_root_token" { default = null } +variable "vault_audit_log_path" { + type = string + description = "The file path for the audit device" + default = null +} + variable "aws_enabled" { type = bool description = <<-EOF diff --git a/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-audit.sh b/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-audit.sh new file mode 100644 index 0000000000..80a310a631 --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-audit.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +# Copyright IBM Corp. 2016, 2025 +# 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 "$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" + +AUDIT_LOG="${VAULT_AUDIT_LOG:-/var/log/vault/vault_audit.log}" +ROLE_NAME="${ROLE_NAME:-dynamic-role}" + +echo "Test: Audit Trail Verification for Dynamic Role Operations" +echo "Audit log path: $AUDIT_LOG" +echo "Role name: $ROLE_NAME" + +if ! sudo test -f "$AUDIT_LOG"; then + fail "Audit log file not found at $AUDIT_LOG" +fi + +echo "Test 1: Checking if role creation was audited..." + +if ! sudo grep -q "\"path\":\"${MOUNT}/role/${ROLE_NAME}\"" "$AUDIT_LOG"; then + fail "ERROR: Role path not found in audit log" +fi + +if sudo grep "\"path\":\"${MOUNT}/role/${ROLE_NAME}\"" "$AUDIT_LOG" | sudo grep -q "\"operation\":\"create\""; then + echo "SUCCESS: Role creation activity found in audit log" +elif sudo grep "\"path\":\"${MOUNT}/role/${ROLE_NAME}\"" "$AUDIT_LOG" | sudo grep -q "\"operation\":\"update\""; then + echo "SUCCESS: Role creation activity found in audit log (as update)" +else + fail "ERROR: Role creation/update operation not found in audit log" +fi + +echo "Test 2: Checking if role update was audited..." + +role_entries=$(sudo grep "\"path\":\"${MOUNT}/role/${ROLE_NAME}\"" "$AUDIT_LOG") + +if echo "$role_entries" | sudo grep -q "\"default_ttl\""; then + echo "SUCCESS: Role update activity (default_ttl modification) found in audit log." + echo "$role_entries" +elif echo "$role_entries" | sudo grep -q "\"max_ttl\""; then + echo "SUCCESS: Role update activity (max_ttl modification) found in audit log" +else + echo "$role_entries" + fail "WARNING: Role update activity not clearly identified in audit log" +fi + +echo "Test 3: Checking if role read was audited..." + +if echo "$role_entries" | sudo grep -q "\"operation\":\"read\""; then + echo "SUCCESS: Role read activity found in audit log" +else + fail "ARNING: Role read activity not found in audit log" +fi +echo "All audit tests passed successfully." diff --git a/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-deletion.sh b/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-deletion.sh new file mode 100644 index 0000000000..d380539239 --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-deletion.sh @@ -0,0 +1,145 @@ +#!/usr/bin/env bash +# Copyright IBM Corp. 2016, 2025 +# SPDX-License-Identifier: BUSL-1.1 + +set -e + +fail() { + echo "$1" 1>&2 + exit 1 +} +retry() { + local retries=$1 + shift + local count=0 + + until "$@"; do + exit=$? + wait=$((2 ** count)) + count=$((count + 1)) + if [ "$count" -lt "$retries" ]; then + sleep "$wait" + echo "retry $count" + else + return "$exit" + fi + done + + return 0 +} + +[[ -z "$MOUNT" ]] && fail "MOUNT env variable has not been set" +[[ -z "$LDAP_SERVER" ]] && fail "LDAP_SERVER env variable has not been set" +[[ -z "$LDAP_PORT" ]] && fail "LDAP_PORT env variable has not been set" +[[ -z "$LDAP_USERNAME" ]] && fail "LDAP_USERNAME env variable has not been set" +[[ -z "$LDAP_ADMIN_PW" ]] && fail "LDAP_ADMIN_PW 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 + +ROLE_NAME="${ROLE_NAME:-dynamic-role}" +# Test Case: Delete a dynamic role +test_delete_nonexistent_role() { + echo "Test Case: Delete a nonexistent dynamic role" + + local test_role="${ROLE_NAME}_deletion" + + if "$binpath" read "${MOUNT}/role/${test_role}" > /dev/null 2>&1; then + fail "ERROR: Role '$test_role' unexpectedly exists." + fi + + echo "Confirmed: Role '$test_role' does not exist." + + "$binpath" delete "${MOUNT}/role/${test_role}" > /dev/null 2>&1 + + if "$binpath" read "${MOUNT}/role/${test_role}" > /dev/null 2>&1; then + fail "FAIL: Role unexpectedly exists after deletion command!" + fi + + echo "PASS: Role deleted successfully. Read returned error as expected." +} +# Test Case: Role Deleted with Active Leases +test_role_deletion_with_active_leases() { + echo "TEST CASE: Role Deleted with Active Leases (Cleanup)" + + local lease_role="${ROLE_NAME}_lease_cleanup" + + if ! output=$("$binpath" write "${MOUNT}/role/${lease_role}" \ + creation_ldif=@creation.ldif \ + deletion_ldif=@deletion.ldif \ + rollback_ldif=@rollback.ldif \ + username_template="v-lease-{{random 5}}" \ + default_ttl=3600s 2>&1); then + fail "ERROR: Failed to create role: ${output}" + fi + + echo "Role '$lease_role' created." + + if ! creds_output=$("$binpath" read -format=json "${MOUNT}/creds/${lease_role}" 2>&1); then + fail "ERROR: Failed to generate credentials: ${creds_output}" + fi + + lease_user=$(echo "$creds_output" | jq -r .data.username) + + if [[ -z "$lease_user" || "$lease_user" == "null" ]]; then + fail "ERROR: Could not generate a user: ${creds_output}" + fi + + echo "Generated dynamic user: '$lease_user'" + + "$binpath" delete "${MOUNT}/role/${lease_role}" > /dev/null 2>&1 + echo "Deleted role '$lease_role'. Waiting for cleanup..." + + "$binpath" lease revoke -prefix "${MOUNT}/creds/${lease_role}" > /dev/null 2>&1 + + # Define the check function + wait_for_user_deletion() { + check_user=$(ldapsearch -x -H "ldap://${LDAP_SERVER}:${LDAP_PORT}" \ + -b "dc=${LDAP_USERNAME},dc=com" \ + -D "cn=admin,dc=${LDAP_USERNAME},dc=com" \ + -w "${LDAP_ADMIN_PW}" \ + "(uid=$lease_user)" dn 2>&1) + + ! grep -q "^dn:" <<< "$check_user" + } + + # Use retry to poll (6 attempts = ~63 seconds max) + if retry 6 wait_for_user_deletion; then + echo "SUCCESS: User '$lease_user' deleted!" + else + fail "ERROR: User still exists after cleanup timeout" + fi +} + +# Test Case: Invalid LDIF Template +test_invalid_ldif_template() { + echo "--- TEST CASE: Invalid LDIF Template (Negative Input) ---" + + local bad_role="${ROLE_NAME}_invalid_ldif" + + echo "Attempting to write invalid LDIF..." + + set +e + output=$("$binpath" write "${MOUNT}/role/${bad_role}" \ + creation_ldif="dn: this-is-total-garbage" \ + deletion_ldif=@delete.ldif 2>&1) + ret_code=$? + set -e + + if [ $ret_code -ne 0 ]; then + if grep -iq "invalid\|failed to parse" <<< "$output"; then + echo "SUCCESS: System correctly rejected invalid LDIF." + echo "Error Message: ${output}" + else + echo "SUCCESS: System rejected invalid LDIF (generic error)." + echo "Error Message: ${output}" + fi + else + fail "FAIL: System accepted invalid data: ${output}" + fi +} diff --git a/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-listing.sh b/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-listing.sh new file mode 100644 index 0000000000..d110e326e3 --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-listing.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# Copyright IBM Corp. 2016, 2025 +# 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 "$LDAP_SERVER" ]] && fail "LDAP_SERVER env variable has not been set" +[[ -z "$LDAP_PORT" ]] && fail "LDAP_PORT env variable has not been set" +[[ -z "$LDAP_USERNAME" ]] && fail "LDAP_USERNAME env variable has not been set" +[[ -z "$LDAP_ADMIN_PW" ]] && fail "LDAP_ADMIN_PW 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 + +ROLE_NAME="${ROLE_NAME:-dynamic-role}" + +# Test Case: Validate dynamic role Schema +test_validate_schema() { + echo "Test: Validate dynamic role schema" + + if ! output=$("$binpath" write "${MOUNT}/role/${ROLE_NAME}_schema" \ + creation_ldif=@creation.ldif \ + deletion_ldif=@deletion.ldif \ + rollback_ldif=@rollback.ldif \ + username_template="v-schema-{{random 5}}" 2>&1); then + fail "ERROR: Schema validation failed: ${output}" + fi + + echo "SUCCESS: Schema validation successful." +} + +# Test Case: Listing Dynamic Roles +test_list_dynamic_roles() { + echo "Test: List Dynamic Roles" + + if ! list_output=$("$binpath" list "${MOUNT}/role" 2>&1); then + fail "ERROR: Failed to list roles: ${list_output}" + fi + + if ! grep -q "$ROLE_NAME" <<< "$list_output"; then + fail "ERROR: Role not found in list: ${list_output}" + fi + + echo "SUCCESS: Role '$ROLE_NAME' found in list." + echo "$list_output" +} + +test_validate_schema +test_list_dynamic_roles + +echo "All validation and listing tests passed successfully." diff --git a/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-rollback.sh b/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-rollback.sh new file mode 100644 index 0000000000..463bcc2c17 --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-rollback.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +# Copyright IBM Corp. 2016, 2025 +# 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 "$LDAP_SERVER" ]] && fail "LDAP_SERVER env variable has not been set" +[[ -z "$LDAP_PORT" ]] && fail "LDAP_PORT env variable has not been set" +[[ -z "$LDAP_USERNAME" ]] && fail "LDAP_USERNAME env variable has not been set" +[[ -z "$LDAP_ADMIN_PW" ]] && fail "LDAP_ADMIN_PW 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 + +ROLE_NAME="${ROLE_NAME:-dynamic-role}" + +# Advanced LDAP dynamic role tests: Rollback, Deletion, Negative scenarios +# Test Case: Rollback on Creation Failure +test_rollback_on_creation_failure() { + echo "Test Case: Rollback on Creation Failure (Negative Scenario)" + + FAIL_ROLE_NAME="rollback-test-role" + FIXED_USER="rb_user_verify" + + cat << EOF > create_for_rollback.ldif +dn: uid={{.Username}},ou=users,dc=$LDAP_USERNAME,dc=com +objectClass: inetOrgPerson +sn: {{.Username}} +cn: {{.Username}} +uid: {{.Username}} +userPassword: {{.Password}} + +dn: uid=fail-{{.Username}},ou=users,dc=$LDAP_USERNAME,dc=com +objectClass: THIS_CLASS_DOESNOT_EXIST +sn: fail-user +cn: fail-user +EOF + + cat << EOF > rollback.ldif +dn: uid={{.Username}},ou=users,dc=$LDAP_USERNAME,dc=com +changetype: delete +EOF + + "$binpath" write ldap/role/$FAIL_ROLE_NAME \ + creation_ldif=@create_for_rollback.ldif \ + deletion_ldif=@deletion.ldif \ + rollback_ldif=@rollback.ldif \ + username_template="$FIXED_USER" \ + default_ttl=3600s > /dev/null + + echo "Triggering credential generation for $FAIL_ROLE_NAME (Expected to FAIL)..." + + if "$binpath" read ldap/creds/$FAIL_ROLE_NAME > /dev/null 2>&1; then + fail "ERROR: API succeeded unexpectedly. The invalid LDIF should have caused a failure." + else + echo "SUCCESS: API returned error as expected (simulated partial failure)." + fi + + echo "Verifying Rollback: Checking if '$FIXED_USER' was properly cleaned up..." + + SEARCH_RES=$(ldapsearch -x -H "ldap://${LDAP_SERVER}:${LDAP_PORT}" \ + -b "ou=users,dc=${LDAP_USERNAME},dc=com" \ + -D "cn=admin,dc=${LDAP_USERNAME},dc=com" \ + -w "${LDAP_ADMIN_PW}" \ + "(uid=$FIXED_USER)" 2>&1) + + if grep -q "^dn:" <<< "$SEARCH_RES"; then + fail "ERROR: user $FIXED_USER found in ldap." + else + echo "SUCCESS: User not found! Rollback successful" + fi +} + +test_rollback_on_creation_failure + +echo "Rollback tests passed successfully." diff --git a/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-validation.sh b/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-validation.sh new file mode 100644 index 0000000000..8e116ba1f7 --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles-validation.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# Copyright IBM Corp. 2016, 2025 +# 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 "$LDAP_SERVER" ]] && fail "LDAP_SERVER env variable has not been set" +[[ -z "$LDAP_PORT" ]] && fail "LDAP_PORT env variable has not been set" +[[ -z "$LDAP_USERNAME" ]] && fail "LDAP_USERNAME env variable has not been set" +[[ -z "$LDAP_ADMIN_PW" ]] && fail "LDAP_ADMIN_PW 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 + +ROLE_NAME="${ROLE_NAME:-dynamic-role}" + +# Test Case: Template Validation - Valid Syntax +test_valid_template_syntax() { + echo "Test Case: Template Validation (Valid Syntax)" + + if ! output=$("$binpath" write "${MOUNT}/role/${ROLE_NAME}_valid" \ + creation_ldif=@creation.ldif \ + deletion_ldif=@deletion.ldif \ + rollback_ldif=@rollback.ldif \ + username_template="v-temp-{{random 10}}" 2>&1); then + fail "ERROR: Vault rejected valid template at role creation: ${output}" + fi + + echo "SUCCESS: Vault accepted valid template (200 OK)" +} + +# Type 2: Negative Test - Invalid Template +test_invalid_template_syntax() { + echo "Test Case: Template Validation (Invalid Syntax)" + + set +e + output=$("$binpath" write "${MOUNT}/role/${ROLE_NAME}_invalid" \ + creation_ldif=@creation.ldif \ + deletion_ldif=@deletion.ldif \ + rollback_ldif=@rollback.ldif \ + username_template="{{.Invalid_Syntax}}" 2>&1) + set +e + output=$("$binpath" read "${MOUNT}/creds/${ROLE_NAME}_invalid" 2>&1) + ret_code=$? + set -e + + if [ $ret_code -eq 0 ]; then + fail "ERROR: Vault accepted invalid template and generated credentials! Output: ${output}" + fi + + if echo "$output" | grep -q "can't evaluate field Invalid_Syntax"; then + echo "SUCCESS: Vault correctly rejected invalid template at credential generation" + echo "Error message: ${output}" + else + echo "Debug Output: ${output}" + fail "ERROR: Expected template validation error but got different error" + fi +} + +test_valid_template_syntax +test_invalid_template_syntax + +echo "All template validation tests passed successfully." diff --git a/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles.sh b/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles.sh new file mode 100644 index 0000000000..9579e52f2d --- /dev/null +++ b/enos/modules/verify_secrets_engines/scripts/ldap/Dynamic-roles/dynamic-roles.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# Copyright IBM Corp. 2016, 2025 +# 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 "$LDAP_SERVER" ]] && fail "LDAP_SERVER env variable has not been set" +[[ -z "$LDAP_PORT" ]] && fail "LDAP_PORT env variable has not been set" +[[ -z "$LDAP_USERNAME" ]] && fail "LDAP_USERNAME env variable has not been set" +[[ -z "$LDAP_ADMIN_PW" ]] && fail "LDAP_ADMIN_PW 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 + +ROLE_NAME="${ROLE_NAME:-dynamic-role}" +# Test Case: create a dynamic role +test_create_dynamic_role() { + echo "Test Case: Create Dynamic Role ($ROLE_NAME)" + + if ! output=$("$binpath" write "${MOUNT}/role/${ROLE_NAME}" \ + username_template="v-temp-{{random 10}}" \ + default_ttl=3200s \ + max_ttl=7200s 2>&1); then + fail "ERROR: Failed to create Dynamic Role: ${output}" + fi + + echo "SUCCESS: Dynamic Role created." +} + +# Test Case: Read a newly created dynamic role +test_read_dynamic_role() { + echo "Test Case: Read newly created dynamic role" + + if ! read_output=$("$binpath" read "${MOUNT}/role/${ROLE_NAME}" 2>&1); then + fail "ERROR: Failed to read role: ${read_output}" + fi + + if ! grep -q "v-temp" <<< "$read_output"; then + echo "Debug Output: $read_output" + fail "ERROR: Role read succeeded but creation_ldif not found." + else + echo "Debug Output: $read_output" + fi + echo "SUCCESS: Role is readable and configuration matches." +} + +# Test Case: Update existing dynamic role +test_update_dynamic_role() { + echo "Test case: Update existing dynamic role TTL" + + if ! output=$("$binpath" write "${MOUNT}/role/${ROLE_NAME}" default_ttl=7200s 2>&1); then + fail "ERROR: Failed to update role: ${output}" + fi + + if ! updated_ttl=$("$binpath" read -field=default_ttl "${MOUNT}/role/${ROLE_NAME}" 2>&1); then + fail "ERROR: Failed to read updated TTL: ${updated_ttl}" + fi + + if echo "$updated_ttl" | grep -q "7200"; then + echo "SUCCESS: Role updated successfully (TTL=7200)." + else + echo "Debug Output: $updated_ttl" + fail "ERROR: Update mismatch. Expected 7200, got $updated_ttl" + fi +} + +test_create_dynamic_role +test_read_dynamic_role +test_update_dynamic_role + +echo "All basic dynamic role tests passed successfully."