From 9ad290513c0389bea83313636499115fe915ac29 Mon Sep 17 00:00:00 2001 From: Christopher Papke Date: Thu, 19 Jun 2025 09:53:28 -0700 Subject: [PATCH 01/13] add tarball_url --- examples/azure/poc/dsf_deployment/sonar.tf | 4 ++++ examples/azure/poc/dsf_deployment/variables.tf | 13 ++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/examples/azure/poc/dsf_deployment/sonar.tf b/examples/azure/poc/dsf_deployment/sonar.tf index 952fea0c6..c12c8cb41 100644 --- a/examples/azure/poc/dsf_deployment/sonar.tf +++ b/examples/azure/poc/dsf_deployment/sonar.tf @@ -16,6 +16,7 @@ module "hub_main" { resource_group = local.resource_group subnet_id = module.network[0].vnet_subnets[0] binaries_location = var.tarball_location + tarball_url = var.tarball_url password = local.password storage_details = var.hub_storage_details instance_size = var.hub_instance_size @@ -62,6 +63,7 @@ module "hub_dr" { resource_group = local.resource_group subnet_id = module.network[0].vnet_subnets[1] binaries_location = var.tarball_location + tarball_url = var.tarball_url password = local.password storage_details = var.hub_storage_details instance_size = var.hub_instance_size @@ -116,6 +118,7 @@ module "agentless_gw_main" { subnet_id = module.network[0].vnet_subnets[0] storage_details = var.agentless_gw_storage_details binaries_location = var.tarball_location + tarball_url = var.tarball_url instance_size = var.agentless_gw_instance_size base_directory = var.sonar_machine_base_directory password = local.password @@ -149,6 +152,7 @@ module "agentless_gw_dr" { subnet_id = module.network[0].vnet_subnets[1] storage_details = var.agentless_gw_storage_details binaries_location = var.tarball_location + tarball_url = var.tarball_url instance_size = var.agentless_gw_instance_size base_directory = var.sonar_machine_base_directory password = local.password diff --git a/examples/azure/poc/dsf_deployment/variables.tf b/examples/azure/poc/dsf_deployment/variables.tf index 23416dd9f..1b9cb41b7 100644 --- a/examples/azure/poc/dsf_deployment/variables.tf +++ b/examples/azure/poc/dsf_deployment/variables.tf @@ -192,7 +192,18 @@ variable "tarball_location" { az_blob = string }) description = "Storage account and container location of the DSF Sonar installation software. az_blob is the full path to the tarball file within the storage account container" - default = null + default = { + az_resource_group = "" + az_storage_account = "" + az_container = "" + az_blob = "" + } +} + +variable "tarball_url" { + type = string + default = "" + description = "HTTPS DSF installation location. If not set, binaries_location is used" } variable "hub_hadr" { From 962b435619fd136e0bde1c93305a92a339cb797f Mon Sep 17 00:00:00 2001 From: Christopher Papke Date: Thu, 19 Jun 2025 11:03:55 -0700 Subject: [PATCH 02/13] use passed in subnet_ids --- .../azure/poc/dsf_deployment/agent_sources.tf | 2 +- examples/azure/poc/dsf_deployment/dam.tf | 4 +- examples/azure/poc/dsf_deployment/dra.tf | 4 +- .../azure/poc/dsf_deployment/networking.tf | 74 ++++++------------- examples/azure/poc/dsf_deployment/sonar.tf | 8 +- .../azure/poc/dsf_deployment/variables.tf | 7 ++ 6 files changed, 40 insertions(+), 59 deletions(-) diff --git a/examples/azure/poc/dsf_deployment/agent_sources.tf b/examples/azure/poc/dsf_deployment/agent_sources.tf index 837016365..48edc4ac0 100644 --- a/examples/azure/poc/dsf_deployment/agent_sources.tf +++ b/examples/azure/poc/dsf_deployment/agent_sources.tf @@ -11,7 +11,7 @@ module "db_with_agent" { resource_group = local.resource_group binaries_location = var.dam_agent_installation_location db_type = local.db_types_for_agent[count.index] - subnet_id = module.network[0].vnet_subnets[0] + subnet_id = local.db_subnet_ids[count.index % length(local.db_subnet_ids)] ssh_key = { ssh_public_key = tls_private_key.ssh_key.public_key_openssh ssh_private_key_file_path = local_sensitive_file.ssh_key.filename diff --git a/examples/azure/poc/dsf_deployment/dam.tf b/examples/azure/poc/dsf_deployment/dam.tf index 103bb5aad..8c501eb87 100644 --- a/examples/azure/poc/dsf_deployment/dam.tf +++ b/examples/azure/poc/dsf_deployment/dam.tf @@ -12,7 +12,7 @@ module "mx" { friendly_name = join("-", [local.deployment_name_salted, "mx"]) resource_group = local.resource_group dam_version = var.dam_version - subnet_id = module.network[0].vnet_subnets[0] + subnet_id = local.mx_subnet_id license = var.dam_license ssh_key = { ssh_public_key = tls_private_key.ssh_key.public_key_openssh @@ -47,7 +47,7 @@ module "agent_gw" { friendly_name = join("-", [local.deployment_name_salted, "agent", "gw", count.index]) resource_group = local.resource_group dam_version = var.dam_version - subnet_id = module.network[0].vnet_subnets[0] + subnet_id = local.agent_gw_subnet_id ssh_key = { ssh_public_key = tls_private_key.ssh_key.public_key_openssh ssh_private_key_file_path = local_sensitive_file.ssh_key.filename diff --git a/examples/azure/poc/dsf_deployment/dra.tf b/examples/azure/poc/dsf_deployment/dra.tf index ca6378056..93b70ec71 100644 --- a/examples/azure/poc/dsf_deployment/dra.tf +++ b/examples/azure/poc/dsf_deployment/dra.tf @@ -16,7 +16,7 @@ module "dra_admin" { count = var.enable_dra ? 1 : 0 name = join("-", [local.deployment_name_salted, "dra", "admin"]) - subnet_id = module.network[0].vnet_subnets[0] + subnet_id = local.dra_admin_subnet_id resource_group = local.resource_group storage_details = var.dra_admin_storage_details ssh_public_key = tls_private_key.ssh_key.public_key_openssh @@ -54,7 +54,7 @@ module "dra_analytics" { count = local.dra_analytics_count name = join("-", [local.deployment_name_salted, "dra", "analytics", count.index]) - subnet_id = module.network[0].vnet_subnets[1] + subnet_id = local.dra_analytics_subnet_id resource_group = local.resource_group storage_details = var.dra_analytics_storage_details ssh_public_key = tls_private_key.ssh_key.public_key_openssh diff --git a/examples/azure/poc/dsf_deployment/networking.tf b/examples/azure/poc/dsf_deployment/networking.tf index c91e60573..4bdd74d83 100644 --- a/examples/azure/poc/dsf_deployment/networking.tf +++ b/examples/azure/poc/dsf_deployment/networking.tf @@ -1,22 +1,26 @@ -# locals { -# hub_subnet_id = var.subnet_ids != null ? var.subnet_ids.hub_subnet_id : module.vpc[0].public_subnets[0] -# hub_dr_subnet_id = var.subnet_ids != null ? var.subnet_ids.hub_dr_subnet_id : module.vpc[0].public_subnets[1] -# agentless_gw_subnet_id = var.subnet_ids != null ? var.subnet_ids.agentless_gw_subnet_id : module.vpc[0].private_subnets[0] -# agentless_gw_dr_subnet_id = var.subnet_ids != null ? var.subnet_ids.agentless_gw_dr_subnet_id : module.vpc[0].private_subnets[1] -# db_subnet_ids = var.subnet_ids != null ? var.subnet_ids.db_subnet_ids : module.vpc[0].public_subnets -# mx_subnet_id = var.subnet_ids != null ? var.subnet_ids.mx_subnet_id : module.vpc[0].public_subnets[0] -# dra_admin_subnet_id = var.subnet_ids != null ? var.subnet_ids.dra_admin_subnet_id : module.vpc[0].public_subnets[0] -# dra_analytics_subnet_id = var.subnet_ids != null ? var.subnet_ids.dra_analytics_subnet_id : module.vpc[0].private_subnets[0] -# agent_gw_subnet_id = var.subnet_ids != null ? var.subnet_ids.agent_gw_subnet_id : module.vpc[0].private_subnets[0] -# } - locals { + create_network = var.subnet_ids == null && var.subnet_id == null + + hub_subnet_id = coalesce(try(var.subnet_ids.hub_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[0]) + hub_dr_subnet_id = coalesce(try(var.subnet_ids.hub_dr_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[1]) + + agentless_gw_subnet_id = coalesce(try(var.subnet_ids.agentless_gw_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[0]) + agentless_gw_dr_subnet_id = coalesce(try(var.subnet_ids.agentless_gw_dr_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[1]) + + db_subnet_ids = coalescelist(try(var.subnet_ids.db_subnet_ids, null), compact([var.subnet_id]), module.network[0].vnet_subnets) + + mx_subnet_id = coalesce(try(var.subnet_ids.mx_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[0]) + agent_gw_subnet_id = coalesce(try(var.subnet_ids.agent_gw_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[0]) + + dra_admin_subnet_id = coalesce(try(var.subnet_ids.dra_admin_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[0]) + dra_analytics_subnet_id = coalesce(try(var.subnet_ids.dra_analytics_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[1]) + subnet_prefixes = cidrsubnets(var.vnet_ip_range, 8, 8) } # network module "network" { - count = 1 + count = local.create_network ? 1 : 0 source = "Azure/network/azurerm" version = "5.3.0" vnet_name = "${local.deployment_name_salted}-${module.globals.current_user_name}" @@ -32,41 +36,8 @@ module "network" { ] } -# data "aws_subnet" "hub" { -# id = local.hub_subnet_id -# } - -# data "aws_subnet" "hub_dr" { -# id = local.hub_dr_subnet_id -# } - -# data "aws_subnet" "agentless_gw" { -# id = local.agentless_gw_subnet_id -# } - -# data "aws_subnet" "agentless_gw_dr" { -# id = local.agentless_gw_dr_subnet_id -# } - -# data "aws_subnet" "mx" { -# id = local.mx_subnet_id -# } - -# data "aws_subnet" "agent_gw" { -# id = local.agent_gw_subnet_id -# } - -# data "aws_subnet" "dra_admin" { -# id = local.dra_admin_subnet_id -# } - -# data "aws_subnet" "dra_analytics" { -# id = local.dra_analytics_subnet_id -# } - -# NAT - resource "azurerm_public_ip" "nat_gw_public_ip" { + count = local.create_network ? 1 : 0 name = join("-", [var.deployment_name, "nat", "public", "ip"]) location = local.resource_group.location resource_group_name = local.resource_group.name @@ -75,6 +46,7 @@ resource "azurerm_public_ip" "nat_gw_public_ip" { } resource "azurerm_nat_gateway" "nat_gw" { + count = local.create_network ? 1 : 0 name = join("-", [var.deployment_name, "nat", "gw"]) location = local.resource_group.location resource_group_name = local.resource_group.name @@ -83,12 +55,14 @@ resource "azurerm_nat_gateway" "nat_gw" { } resource "azurerm_nat_gateway_public_ip_association" "nat_gw_public_ip_association" { - nat_gateway_id = azurerm_nat_gateway.nat_gw.id - public_ip_address_id = azurerm_public_ip.nat_gw_public_ip.id + count = local.create_network ? 1 : 0 + nat_gateway_id = azurerm_nat_gateway.nat_gw[0].id + public_ip_address_id = azurerm_public_ip.nat_gw_public_ip[0].id } # subnet 1 is the private subnet resource "azurerm_subnet_nat_gateway_association" "nat_gw_vnet_association" { + count = local.create_network ? 1 : 0 subnet_id = module.network[0].vnet_subnets[1] - nat_gateway_id = azurerm_nat_gateway.nat_gw.id + nat_gateway_id = azurerm_nat_gateway.nat_gw[0].id } \ No newline at end of file diff --git a/examples/azure/poc/dsf_deployment/sonar.tf b/examples/azure/poc/dsf_deployment/sonar.tf index c12c8cb41..cd99c4b8d 100644 --- a/examples/azure/poc/dsf_deployment/sonar.tf +++ b/examples/azure/poc/dsf_deployment/sonar.tf @@ -14,7 +14,7 @@ module "hub_main" { friendly_name = join("-", [local.deployment_name_salted, "hub"]) resource_group = local.resource_group - subnet_id = module.network[0].vnet_subnets[0] + subnet_id = local.hub_subnet_id binaries_location = var.tarball_location tarball_url = var.tarball_url password = local.password @@ -61,7 +61,7 @@ module "hub_dr" { friendly_name = join("-", [local.deployment_name_salted, "hub", "DR"]) resource_group = local.resource_group - subnet_id = module.network[0].vnet_subnets[1] + subnet_id = local.hub_dr_subnet_id binaries_location = var.tarball_location tarball_url = var.tarball_url password = local.password @@ -115,7 +115,7 @@ module "agentless_gw_main" { friendly_name = join("-", [local.deployment_name_salted, "agentless", "gw", count.index]) resource_group = local.resource_group - subnet_id = module.network[0].vnet_subnets[0] + subnet_id = local.agentless_gw_subnet_id storage_details = var.agentless_gw_storage_details binaries_location = var.tarball_location tarball_url = var.tarball_url @@ -149,7 +149,7 @@ module "agentless_gw_dr" { friendly_name = join("-", [local.deployment_name_salted, "agentless", "gw", count.index, "DR"]) resource_group = local.resource_group - subnet_id = module.network[0].vnet_subnets[1] + subnet_id = local.agentless_gw_dr_subnet_id storage_details = var.agentless_gw_storage_details binaries_location = var.tarball_location tarball_url = var.tarball_url diff --git a/examples/azure/poc/dsf_deployment/variables.tf b/examples/azure/poc/dsf_deployment/variables.tf index 1b9cb41b7..e3b9784e5 100644 --- a/examples/azure/poc/dsf_deployment/variables.tf +++ b/examples/azure/poc/dsf_deployment/variables.tf @@ -92,6 +92,12 @@ variable "vnet_ip_range" { description = "Vnet ip range" } +variable "subnet_id" { + type = string + default = null + description = "The ID of an existing subnet to put all resources in" +} + variable "subnet_ids" { type = object({ hub_subnet_id = string @@ -99,6 +105,7 @@ variable "subnet_ids" { agentless_gw_subnet_id = string agentless_gw_dr_subnet_id = string mx_subnet_id = string + db_subnet_ids = list(string) agent_gw_subnet_id = string dra_admin_subnet_id = string dra_analytics_subnet_id = string From f31d65f3fcdc14a70e5ed1e928d88398d23bc62b Mon Sep 17 00:00:00 2001 From: Christopher Papke Date: Thu, 19 Jun 2025 12:08:16 -0700 Subject: [PATCH 03/13] fix coalescelists --- examples/azure/poc/dsf_deployment/networking.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/azure/poc/dsf_deployment/networking.tf b/examples/azure/poc/dsf_deployment/networking.tf index 4bdd74d83..5f4236aa6 100644 --- a/examples/azure/poc/dsf_deployment/networking.tf +++ b/examples/azure/poc/dsf_deployment/networking.tf @@ -7,7 +7,7 @@ locals { agentless_gw_subnet_id = coalesce(try(var.subnet_ids.agentless_gw_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[0]) agentless_gw_dr_subnet_id = coalesce(try(var.subnet_ids.agentless_gw_dr_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[1]) - db_subnet_ids = coalescelist(try(var.subnet_ids.db_subnet_ids, null), compact([var.subnet_id]), module.network[0].vnet_subnets) + db_subnet_ids = coalescelist(try(var.subnet_ids.db_subnet_ids, []), compact([var.subnet_id]), module.network[0].vnet_subnets) mx_subnet_id = coalesce(try(var.subnet_ids.mx_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[0]) agent_gw_subnet_id = coalesce(try(var.subnet_ids.agent_gw_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[0]) From b40d7a334e231d52f8b7bde768287aa5ac3dcd0a Mon Sep 17 00:00:00 2001 From: Christopher Papke Date: Mon, 23 Jun 2025 07:51:38 -0700 Subject: [PATCH 04/13] update desc --- examples/azure/poc/dsf_deployment/variables.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/azure/poc/dsf_deployment/variables.tf b/examples/azure/poc/dsf_deployment/variables.tf index e3b9784e5..495c83274 100644 --- a/examples/azure/poc/dsf_deployment/variables.tf +++ b/examples/azure/poc/dsf_deployment/variables.tf @@ -95,7 +95,7 @@ variable "vnet_ip_range" { variable "subnet_id" { type = string default = null - description = "The ID of an existing subnet to put all resources in" + description = "The ID of an existing subnet to put all resources in. Either 'subnet_id' or 'subnet_ids' should be provided but not both." } variable "subnet_ids" { @@ -111,7 +111,7 @@ variable "subnet_ids" { dra_analytics_subnet_id = string }) default = null - description = "The IDs of existing subnets to deploy resources in. Keep empty if you wish to provision new VPC and subnets. db_subnet_ids can be an empty list only if no databases should be provisioned" + description = "The IDs of existing subnets to deploy resources in. Keep empty if you wish to provision new VPC and subnets, or if you are providing the subnet_id variable. db_subnet_ids can be an empty list only if no databases should be provisioned." validation { condition = var.subnet_ids == null || try(var.subnet_ids.hub_subnet_id != null && var.subnet_ids.hub_dr_subnet_id != null && var.subnet_ids.agentless_gw_subnet_id != null && var.subnet_ids.agentless_gw_dr_subnet_id != null && var.subnet_ids.dra_admin_subnet_id != null && var.subnet_ids.dra_analytics_subnet_id != null, false) error_message = "Value must either be null or specified for all." From 6ffb2b5abd283836341f41f2610ce56fe47c988c Mon Sep 17 00:00:00 2001 From: Christopher Papke Date: Mon, 23 Jun 2025 09:35:25 -0700 Subject: [PATCH 05/13] update federation --- .../sonar_multi_account_deployment/main.tf | 195 ++++++++++++++++-- 1 file changed, 176 insertions(+), 19 deletions(-) diff --git a/examples/aws/installation/sonar_multi_account_deployment/main.tf b/examples/aws/installation/sonar_multi_account_deployment/main.tf index 6cd09c2e0..e6b1d2d79 100644 --- a/examples/aws/installation/sonar_multi_account_deployment/main.tf +++ b/examples/aws/installation/sonar_multi_account_deployment/main.tf @@ -310,31 +310,168 @@ module "agentless_gw_hadr" { ] } -locals { - hub_gws_combinations = setproduct( - [{ instance : module.hub_main, private_key_file_path : local.hub_main_private_key_file_path }, { instance : module.hub_dr, private_key_file_path : local.hub_dr_private_key_file_path }], - concat( - [for idx, val in module.agentless_gw_main : { instance : val, private_key_file_path : local.gw_main_private_key_file_path }], - [for idx, val in module.agentless_gw_dr : { instance : val, private_key_file_path : local.gw_dr_private_key_file_path }] - ) - ) +module "gw_main_federation" { + source = "imperva/dsf-federation/null" + version = "1.7.30" # latest release tag + + for_each = { + for idx, val in module.agentless_gw_main : idx => val + } + + hub_info = { + hub_ip_address = module.hub_main.private_ip + hub_federation_ip_address = module.hub_main.private_ip + hub_private_ssh_key_path = local.hub_main_private_key_file_path + hub_ssh_user = module.hub_main.ssh_user + } + gw_info = { + gw_ip_address = each.value.private_ip + gw_federation_ip_address = each.value.private_ip + gw_private_ssh_key_path = local.gw_main_private_key_file_path + gw_ssh_user = each.value.ssh_user + } + hub_proxy_info = var.proxy_address != null ? { + proxy_address = var.proxy_address + proxy_private_ssh_key_path = var.proxy_ssh_key_path + proxy_ssh_user = var.proxy_ssh_user + } : null + gw_proxy_info = var.proxy_address != null ? { + proxy_address = var.proxy_address + proxy_private_ssh_key_path = var.proxy_ssh_key_path + proxy_ssh_user = var.proxy_ssh_user + } : null + depends_on = [ + module.hub_main, + module.agentless_gw_main, + + module.hub_hadr, + module.agentless_gw_hadr + ] +} + +resource "null_resource" "force_gw_replication" { + # for_each = module.agentless_gw_dr + for_each = { for idx, val in module.agentless_gw_dr : idx => val } + + provisioner "local-exec" { + command = <<-EOT + #!/bin/bash + set -x -e + + PROXY_CMD="" + if [ -n "${coalesce(var.proxy_address, "")}" ]; then + PROXY_CMD='ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ${var.proxy_ssh_key_path} -W %h:%p ${var.proxy_ssh_user}@${var.proxy_address}' + fi + + # wait for existing replication to finish + while [[ "$(ssh -o ConnectionAttempts=6 -o ConnectTimeout=15 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ProxyCommand="$PROXY_CMD" -i ${local.gw_dr_private_key_file_path} ${each.value.ssh_user}@${each.value.private_ip} 'sudo $JSONAR_BASEDIR/bin/arbiter-setup is-repl-running')" != *"No replication cycle is currently running"* ]]; do + sleep 10 + done + + # force replication to make sure we are up to date + ssh -o ConnectionAttempts=6 -o ConnectTimeout=15 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ProxyCommand="$PROXY_CMD" -i ${local.gw_dr_private_key_file_path} ${each.value.ssh_user}@${each.value.private_ip} 'sudo $JSONAR_BASEDIR/bin/arbiter-setup run-replication' + EOT + interpreter = ["/bin/bash", "-c"] + } + depends_on = [ + module.agentless_gw_dr, + + module.gw_main_federation, + ] } -module "federation" { - count = length(local.hub_gws_combinations) +module "gw_dr_federation" { source = "imperva/dsf-federation/null" version = "1.7.30" # latest release tag + + for_each = { + for idx, val in module.agentless_gw_dr : idx => val + } + + hub_info = { + hub_ip_address = module.hub_main.private_ip + hub_federation_ip_address = module.hub_main.private_ip + hub_private_ssh_key_path = local.hub_main_private_key_file_path + hub_ssh_user = module.hub_main.ssh_user + } gw_info = { - gw_ip_address = local.hub_gws_combinations[count.index][1].instance.private_ip - gw_federation_ip_address = local.hub_gws_combinations[count.index][1].instance.private_ip - gw_private_ssh_key_path = local.hub_gws_combinations[count.index][1].private_key_file_path - gw_ssh_user = local.hub_gws_combinations[count.index][1].instance.ssh_user + gw_ip_address = each.value.private_ip + gw_federation_ip_address = each.value.private_ip + gw_private_ssh_key_path = local.gw_dr_private_key_file_path + gw_ssh_user = each.value.ssh_user } + hub_proxy_info = var.proxy_address != null ? { + proxy_address = var.proxy_address + proxy_private_ssh_key_path = var.proxy_ssh_key_path + proxy_ssh_user = var.proxy_ssh_user + } : null + gw_proxy_info = var.proxy_address != null ? { + proxy_address = var.proxy_address + proxy_private_ssh_key_path = var.proxy_ssh_key_path + proxy_ssh_user = var.proxy_ssh_user + } : null + depends_on = [ + null_resource.force_gw_replication, + ] +} + +module "hub_dr_gw_main_federation" { + source = "imperva/dsf-federation/null" + version = "1.7.30" # latest release tag + + for_each = var.hub_hadr ? { + for idx, val in module.agentless_gw_main : idx => val + } : {} + + hub_info = { + hub_ip_address = module.hub_dr.private_ip + hub_federation_ip_address = module.hub_dr.private_ip + hub_private_ssh_key_path = local.hub_dr_private_key_file_path + hub_ssh_user = module.hub_dr.ssh_user + } + gw_info = { + gw_ip_address = each.value.private_ip + gw_federation_ip_address = each.value.private_ip + gw_private_ssh_key_path = local.gw_dr_private_key_file_path + gw_ssh_user = each.value.ssh_user + } + hub_proxy_info = var.proxy_address != null ? { + proxy_address = var.proxy_address + proxy_private_ssh_key_path = var.proxy_ssh_key_path + proxy_ssh_user = var.proxy_ssh_user + } : null + gw_proxy_info = var.proxy_address != null ? { + proxy_address = var.proxy_address + proxy_private_ssh_key_path = var.proxy_ssh_key_path + proxy_ssh_user = var.proxy_ssh_user + } : null + depends_on = [ + module.hub_dr, + module.agentless_gw_main, + + module.gw_dr_federation + ] +} + +module "hub_dr_gw_dr_federation" { + source = "imperva/dsf-federation/null" + version = "1.7.30" # latest release tag + + for_each = var.hub_hadr ? { + for idx, val in module.agentless_gw_dr : idx => val + } : {} + hub_info = { - hub_ip_address = local.hub_gws_combinations[count.index][0].instance.private_ip - hub_federation_ip_address = local.hub_gws_combinations[count.index][0].instance.private_ip - hub_private_ssh_key_path = local.hub_gws_combinations[count.index][0].private_key_file_path - hub_ssh_user = local.hub_gws_combinations[count.index][0].instance.ssh_user + hub_ip_address = module.hub_dr.private_ip + hub_federation_ip_address = module.hub_dr.private_ip + hub_private_ssh_key_path = local.hub_dr_private_key_file_path + hub_ssh_user = module.hub_dr.ssh_user + } + gw_info = { + gw_ip_address = each.value.private_ip + gw_federation_ip_address = each.value.private_ip + gw_private_ssh_key_path = local.gw_dr_private_key_file_path + gw_ssh_user = each.value.ssh_user } hub_proxy_info = var.proxy_address != null ? { proxy_address = var.proxy_address @@ -347,7 +484,27 @@ module "federation" { proxy_ssh_user = var.proxy_ssh_user } : null depends_on = [ + module.hub_dr, + module.agentless_gw_dr, + + module.gw_dr_federation + ] +} + + +resource "null_resource" "sonar_setup_completed" { + depends_on = [ + module.hub_main, + module.hub_dr, module.hub_hadr, - module.agentless_gw_hadr + + module.agentless_gw_main, + module.agentless_gw_dr, + module.agentless_gw_hadr, + + module.gw_main_federation, + module.gw_dr_federation, + module.hub_dr_gw_main_federation, + module.hub_dr_gw_dr_federation, ] } From 71453e2728d9ef959bb4e48172ae468819824bf9 Mon Sep 17 00:00:00 2001 From: Christopher Papke Date: Mon, 23 Jun 2025 11:13:02 -0700 Subject: [PATCH 06/13] remove hadr var --- .../installation/sonar_multi_account_deployment/main.tf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/aws/installation/sonar_multi_account_deployment/main.tf b/examples/aws/installation/sonar_multi_account_deployment/main.tf index e6b1d2d79..9afbb94df 100644 --- a/examples/aws/installation/sonar_multi_account_deployment/main.tf +++ b/examples/aws/installation/sonar_multi_account_deployment/main.tf @@ -419,9 +419,9 @@ module "hub_dr_gw_main_federation" { source = "imperva/dsf-federation/null" version = "1.7.30" # latest release tag - for_each = var.hub_hadr ? { + for_each = { for idx, val in module.agentless_gw_main : idx => val - } : {} + } hub_info = { hub_ip_address = module.hub_dr.private_ip @@ -457,9 +457,9 @@ module "hub_dr_gw_dr_federation" { source = "imperva/dsf-federation/null" version = "1.7.30" # latest release tag - for_each = var.hub_hadr ? { + for_each = { for idx, val in module.agentless_gw_dr : idx => val - } : {} + } hub_info = { hub_ip_address = module.hub_dr.private_ip From 34150d91513183f946d4a93cc6fb4c22a8992154 Mon Sep 17 00:00:00 2001 From: Christopher Papke Date: Tue, 24 Jun 2025 09:49:02 -0700 Subject: [PATCH 07/13] actually use passed in networking --- .../azure/poc/dsf_deployment/agent_sources.tf | 4 +- examples/azure/poc/dsf_deployment/dam.tf | 12 ++-- examples/azure/poc/dsf_deployment/dra.tf | 6 +- .../azure/poc/dsf_deployment/networking.tf | 55 ++++++++++++++++--- examples/azure/poc/dsf_deployment/outputs.tf | 2 +- examples/azure/poc/dsf_deployment/sonar.tf | 20 +++---- .../azure/poc/dsf_deployment/variables.tf | 2 +- 7 files changed, 69 insertions(+), 32 deletions(-) diff --git a/examples/azure/poc/dsf_deployment/agent_sources.tf b/examples/azure/poc/dsf_deployment/agent_sources.tf index 5176ade5f..7eb799cdd 100644 --- a/examples/azure/poc/dsf_deployment/agent_sources.tf +++ b/examples/azure/poc/dsf_deployment/agent_sources.tf @@ -11,12 +11,12 @@ module "db_with_agent" { resource_group = local.resource_group binaries_location = var.dam_agent_installation_location db_type = local.db_types_for_agent[count.index] - subnet_id = local.db_subnet_ids[count.index % length(local.db_subnet_ids)] + subnet_id = local.db_subnet_id ssh_key = { ssh_public_key = tls_private_key.ssh_key.public_key_openssh ssh_private_key_file_path = local_sensitive_file.ssh_key.filename } - allowed_ssh_cidrs = concat([format("%s/32", module.mx[0].private_ip)], module.network[0].vnet_address_space) + allowed_ssh_cidrs = concat([format("%s/32", module.mx[0].private_ip)], local.all_subnet_address_spaces) registration_params = { agent_gateway_host = module.agent_gw[0].private_ip diff --git a/examples/azure/poc/dsf_deployment/dam.tf b/examples/azure/poc/dsf_deployment/dam.tf index 60ffef867..1f464129f 100644 --- a/examples/azure/poc/dsf_deployment/dam.tf +++ b/examples/azure/poc/dsf_deployment/dam.tf @@ -20,9 +20,9 @@ module "mx" { } mx_password = local.password allowed_web_console_and_api_cidrs = var.web_console_cidr - allowed_agent_gw_cidrs = module.network[0].vnet_address_space + allowed_agent_gw_cidrs = local.all_subnet_address_spaces allowed_ssh_cidrs = concat(local.workstation_cidr, var.allowed_ssh_cidrs) - allowed_hub_cidrs = module.network[0].vnet_address_space + allowed_hub_cidrs = local.all_subnet_address_spaces hub_details = var.enable_sonar ? { address = coalesce(module.hub_main[0].public_ip, module.hub_main[0].private_ip) @@ -53,10 +53,10 @@ module "agent_gw" { ssh_private_key_file_path = local_sensitive_file.ssh_key.filename } mx_password = local.password - allowed_agent_cidrs = module.network[0].vnet_address_space - allowed_mx_cidrs = module.network[0].vnet_address_space - allowed_ssh_cidrs = concat(module.network[0].vnet_address_space, var.allowed_ssh_cidrs) - allowed_gw_clusters_cidrs = module.network[0].vnet_address_space + allowed_agent_cidrs = local.all_subnet_address_spaces + allowed_mx_cidrs = local.all_subnet_address_spaces + allowed_ssh_cidrs = concat(local.all_subnet_address_spaces, var.allowed_ssh_cidrs) + allowed_gw_clusters_cidrs = local.all_subnet_address_spaces management_server_host_for_registration = module.mx[0].private_ip management_server_host_for_api_access = module.mx[0].public_ip large_scale_mode = var.large_scale_mode.agent_gw diff --git a/examples/azure/poc/dsf_deployment/dra.tf b/examples/azure/poc/dsf_deployment/dra.tf index 84042ef48..566f149aa 100644 --- a/examples/azure/poc/dsf_deployment/dra.tf +++ b/examples/azure/poc/dsf_deployment/dra.tf @@ -2,7 +2,7 @@ locals { dra_analytics_count = var.enable_dra ? var.dra_analytics_count : 0 dra_admin_public_ip = var.enable_dra ? [format("%s/32", module.dra_admin[0].public_ip)] : [] - dra_admin_cidr_list = concat(module.network[0].vnet_address_space, local.dra_admin_public_ip) + dra_admin_cidr_list = concat(local.all_subnet_address_spaces, local.dra_admin_public_ip) dra_admin_image_exits = var.dra_admin_image_details != null ? true : false dra_admin_vhd_exits = var.dra_admin_vhd_details != null ? true : false @@ -24,7 +24,7 @@ module "dra_admin" { admin_ssh_password = local.password allowed_web_console_cidrs = var.web_console_cidr - allowed_analytics_cidrs = module.network[0].vnet_address_space + allowed_analytics_cidrs = local.all_subnet_address_spaces allowed_hub_cidrs = local.hub_cidr_list allowed_ssh_cidrs = concat(local.workstation_cidr, var.allowed_ssh_cidrs) @@ -62,7 +62,7 @@ module "dra_analytics" { analytics_ssh_password = local.password archiver_password = local.password - allowed_admin_cidrs = module.network[0].vnet_address_space + allowed_admin_cidrs = local.all_subnet_address_spaces allowed_ssh_cidrs = concat(local.workstation_cidr, local.hub_cidr_list) #allowed_ssh_cidrs = concat(var.allowed_ssh_cidrs, local.hub_cidr_list, local.workstation_cidr) diff --git a/examples/azure/poc/dsf_deployment/networking.tf b/examples/azure/poc/dsf_deployment/networking.tf index 5f4236aa6..991cdff67 100644 --- a/examples/azure/poc/dsf_deployment/networking.tf +++ b/examples/azure/poc/dsf_deployment/networking.tf @@ -1,21 +1,58 @@ locals { create_network = var.subnet_ids == null && var.subnet_id == null - hub_subnet_id = coalesce(try(var.subnet_ids.hub_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[0]) - hub_dr_subnet_id = coalesce(try(var.subnet_ids.hub_dr_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[1]) + hub_subnet_id = coalesce(try(var.subnet_ids.hub_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[0], null)) + hub_dr_subnet_id = coalesce(try(var.subnet_ids.hub_dr_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[1], null)) - agentless_gw_subnet_id = coalesce(try(var.subnet_ids.agentless_gw_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[0]) - agentless_gw_dr_subnet_id = coalesce(try(var.subnet_ids.agentless_gw_dr_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[1]) + agentless_gw_subnet_id = coalesce(try(var.subnet_ids.agentless_gw_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[0], null)) + agentless_gw_dr_subnet_id = coalesce(try(var.subnet_ids.agentless_gw_dr_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[1], null)) - db_subnet_ids = coalescelist(try(var.subnet_ids.db_subnet_ids, []), compact([var.subnet_id]), module.network[0].vnet_subnets) + db_subnet_id = coalesce(try(var.subnet_ids.db_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[0], null)) - mx_subnet_id = coalesce(try(var.subnet_ids.mx_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[0]) - agent_gw_subnet_id = coalesce(try(var.subnet_ids.agent_gw_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[0]) + mx_subnet_id = coalesce(try(var.subnet_ids.mx_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[0], null)) + agent_gw_subnet_id = coalesce(try(var.subnet_ids.agent_gw_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[0], null)) - dra_admin_subnet_id = coalesce(try(var.subnet_ids.dra_admin_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[0]) - dra_analytics_subnet_id = coalesce(try(var.subnet_ids.dra_analytics_subnet_id, null), var.subnet_id, module.network[0].vnet_subnets[1]) + dra_admin_subnet_id = coalesce(try(var.subnet_ids.dra_admin_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[0], null)) + dra_analytics_subnet_id = coalesce(try(var.subnet_ids.dra_analytics_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[1], null)) subnet_prefixes = cidrsubnets(var.vnet_ip_range, 8, 8) + + subnet_ids = [ + local.hub_subnet_id, + local.hub_dr_subnet_id, + local.agent_gw_subnet_id, + local.agentless_gw_dr_subnet_id, + local.db_subnet_id, + local.mx_subnet_id, + local.agent_gw_subnet_id, + local.dra_admin_subnet_id, + local.dra_analytics_subnet_id, + ] + + ipv4_regex = "([0-9]{1,3}\\.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?" + + _all_subnet_address_spaces = distinct(sort( + concat([ + for subnet in data.azurerm_subnet.all_subnets : + subnet.address_prefixes + ]...) + )) + + # we can't currently use ipv6 IPs + all_subnet_address_spaces = [ + for cidr in local._all_subnet_address_spaces: cidr + if can(regex(local.ipv4_regex, cidr)) + ] +} + +data "azurerm_subnet" "all_subnets" { + for_each = { + for index, id in local.subnet_ids : index => id + } + + resource_group_name = split("/", each.value)[4] + virtual_network_name = split("/", each.value)[8] + name = split("/", each.value)[10] } # network diff --git a/examples/azure/poc/dsf_deployment/outputs.tf b/examples/azure/poc/dsf_deployment/outputs.tf index e99d7a42e..4e8642346 100644 --- a/examples/azure/poc/dsf_deployment/outputs.tf +++ b/examples/azure/poc/dsf_deployment/outputs.tf @@ -15,7 +15,7 @@ output "generated_network" { value = try({ vnet = module.network[0].vnet_id subnets = module.network[0].vnet_subnets - address_space = module.network[0].vnet_address_space + address_space = local.all_subnet_address_spaces }, null) } diff --git a/examples/azure/poc/dsf_deployment/sonar.tf b/examples/azure/poc/dsf_deployment/sonar.tf index 51decca79..0e59a519c 100644 --- a/examples/azure/poc/dsf_deployment/sonar.tf +++ b/examples/azure/poc/dsf_deployment/sonar.tf @@ -3,8 +3,8 @@ locals { hub_public_ip = var.enable_sonar ? (length(module.hub_main[0].public_ip) > 0 ? format("%s/32", module.hub_main[0].public_ip) : null) : null hub_dr_public_ip = var.enable_sonar && var.hub_hadr ? (length(module.hub_dr[0].public_ip) > 0 ? format("%s/32", module.hub_dr[0].public_ip) : null) : null - # WA since the following doesn't work: hub_cidr_list = concat(module.network[0].vnet_address_space, compact([local.hub_public_ip, local.hub_dr_public_ip])) - hub_cidr_list = var.enable_sonar ? (var.hub_hadr ? concat(module.network[0].vnet_address_space, [local.hub_public_ip, local.hub_dr_public_ip]) : concat(module.network[0].vnet_address_space, [local.hub_public_ip])) : module.network[0].vnet_address_space + # WA since the following doesn't work: hub_cidr_list = concat(local.all_subnet_address_spaces, compact([local.hub_public_ip, local.hub_dr_public_ip])) + hub_cidr_list = var.enable_sonar ? (var.hub_hadr ? concat(local.all_subnet_address_spaces, [local.hub_public_ip, local.hub_dr_public_ip]) : concat(local.all_subnet_address_spaces, [local.hub_public_ip])) : local.all_subnet_address_spaces } module "hub_main" { @@ -29,8 +29,8 @@ module "hub_main" { ssh_private_key_file_path = local_sensitive_file.ssh_key.filename } allowed_web_console_and_api_cidrs = var.web_console_cidr - allowed_hub_cidrs = module.network[0].vnet_address_space - allowed_agentless_gw_cidrs = module.network[0].vnet_address_space + allowed_hub_cidrs = local.all_subnet_address_spaces + allowed_agentless_gw_cidrs = local.all_subnet_address_spaces allowed_dra_admin_cidrs = local.dra_admin_cidr_list allowed_all_cidrs = local.workstation_cidr allowed_ssh_cidrs = var.allowed_ssh_cidrs @@ -79,8 +79,8 @@ module "hub_dr" { ssh_private_key_file_path = local_sensitive_file.ssh_key.filename } allowed_web_console_and_api_cidrs = var.web_console_cidr - allowed_hub_cidrs = module.network[0].vnet_address_space - allowed_agentless_gw_cidrs = module.network[0].vnet_address_space + allowed_hub_cidrs = local.all_subnet_address_spaces + allowed_agentless_gw_cidrs = local.all_subnet_address_spaces allowed_dra_admin_cidrs = local.dra_admin_cidr_list allowed_all_cidrs = local.workstation_cidr allowed_ssh_cidrs = var.allowed_ssh_cidrs @@ -127,8 +127,8 @@ module "agentless_gw_main" { ssh_public_key = tls_private_key.ssh_key.public_key_openssh ssh_private_key_file_path = local_sensitive_file.ssh_key.filename } - allowed_agentless_gw_cidrs = module.network[0].vnet_address_space - allowed_hub_cidrs = module.network[0].vnet_address_space + allowed_agentless_gw_cidrs = local.all_subnet_address_spaces + allowed_hub_cidrs = local.all_subnet_address_spaces allowed_all_cidrs = local.workstation_cidr allowed_ssh_cidrs = var.allowed_ssh_cidrs ingress_communication_via_proxy = { @@ -164,8 +164,8 @@ module "agentless_gw_dr" { ssh_public_key = tls_private_key.ssh_key.public_key_openssh ssh_private_key_file_path = local_sensitive_file.ssh_key.filename } - allowed_agentless_gw_cidrs = module.network[0].vnet_address_space - allowed_hub_cidrs = module.network[0].vnet_address_space + allowed_agentless_gw_cidrs = local.all_subnet_address_spaces + allowed_hub_cidrs = local.all_subnet_address_spaces allowed_all_cidrs = local.workstation_cidr allowed_ssh_cidrs = var.allowed_ssh_cidrs ingress_communication_via_proxy = { diff --git a/examples/azure/poc/dsf_deployment/variables.tf b/examples/azure/poc/dsf_deployment/variables.tf index 495c83274..823a2577f 100644 --- a/examples/azure/poc/dsf_deployment/variables.tf +++ b/examples/azure/poc/dsf_deployment/variables.tf @@ -105,7 +105,7 @@ variable "subnet_ids" { agentless_gw_subnet_id = string agentless_gw_dr_subnet_id = string mx_subnet_id = string - db_subnet_ids = list(string) + db_subnet_id = string agent_gw_subnet_id = string dra_admin_subnet_id = string dra_analytics_subnet_id = string From 04a1b747f6d1179f1c56fc72608669dfa80a1701 Mon Sep 17 00:00:00 2001 From: Christopher Papke Date: Wed, 25 Jun 2025 08:02:37 -0700 Subject: [PATCH 08/13] working on networking --- .../azure/poc/dsf_deployment/agent_sources.tf | 2 +- examples/azure/poc/dsf_deployment/dam.tf | 12 ++-- examples/azure/poc/dsf_deployment/dra.tf | 6 +- .../azure/poc/dsf_deployment/networking.tf | 64 ++++++------------- examples/azure/poc/dsf_deployment/outputs.tf | 2 +- examples/azure/poc/dsf_deployment/sonar.tf | 20 +++--- .../azure/poc/dsf_deployment/variables.tf | 22 +------ 7 files changed, 43 insertions(+), 85 deletions(-) diff --git a/examples/azure/poc/dsf_deployment/agent_sources.tf b/examples/azure/poc/dsf_deployment/agent_sources.tf index 7eb799cdd..9402c9a28 100644 --- a/examples/azure/poc/dsf_deployment/agent_sources.tf +++ b/examples/azure/poc/dsf_deployment/agent_sources.tf @@ -16,7 +16,7 @@ module "db_with_agent" { ssh_public_key = tls_private_key.ssh_key.public_key_openssh ssh_private_key_file_path = local_sensitive_file.ssh_key.filename } - allowed_ssh_cidrs = concat([format("%s/32", module.mx[0].private_ip)], local.all_subnet_address_spaces) + allowed_ssh_cidrs = concat([format("%s/32", module.mx[0].private_ip)], local.subnet_address_spaces) registration_params = { agent_gateway_host = module.agent_gw[0].private_ip diff --git a/examples/azure/poc/dsf_deployment/dam.tf b/examples/azure/poc/dsf_deployment/dam.tf index 1f464129f..31fb78870 100644 --- a/examples/azure/poc/dsf_deployment/dam.tf +++ b/examples/azure/poc/dsf_deployment/dam.tf @@ -20,9 +20,9 @@ module "mx" { } mx_password = local.password allowed_web_console_and_api_cidrs = var.web_console_cidr - allowed_agent_gw_cidrs = local.all_subnet_address_spaces + allowed_agent_gw_cidrs = local.subnet_address_spaces allowed_ssh_cidrs = concat(local.workstation_cidr, var.allowed_ssh_cidrs) - allowed_hub_cidrs = local.all_subnet_address_spaces + allowed_hub_cidrs = local.subnet_address_spaces hub_details = var.enable_sonar ? { address = coalesce(module.hub_main[0].public_ip, module.hub_main[0].private_ip) @@ -53,10 +53,10 @@ module "agent_gw" { ssh_private_key_file_path = local_sensitive_file.ssh_key.filename } mx_password = local.password - allowed_agent_cidrs = local.all_subnet_address_spaces - allowed_mx_cidrs = local.all_subnet_address_spaces - allowed_ssh_cidrs = concat(local.all_subnet_address_spaces, var.allowed_ssh_cidrs) - allowed_gw_clusters_cidrs = local.all_subnet_address_spaces + allowed_agent_cidrs = local.subnet_address_spaces + allowed_mx_cidrs = local.subnet_address_spaces + allowed_ssh_cidrs = concat(local.subnet_address_spaces, var.allowed_ssh_cidrs) + allowed_gw_clusters_cidrs = local.subnet_address_spaces management_server_host_for_registration = module.mx[0].private_ip management_server_host_for_api_access = module.mx[0].public_ip large_scale_mode = var.large_scale_mode.agent_gw diff --git a/examples/azure/poc/dsf_deployment/dra.tf b/examples/azure/poc/dsf_deployment/dra.tf index 566f149aa..96c621550 100644 --- a/examples/azure/poc/dsf_deployment/dra.tf +++ b/examples/azure/poc/dsf_deployment/dra.tf @@ -2,7 +2,7 @@ locals { dra_analytics_count = var.enable_dra ? var.dra_analytics_count : 0 dra_admin_public_ip = var.enable_dra ? [format("%s/32", module.dra_admin[0].public_ip)] : [] - dra_admin_cidr_list = concat(local.all_subnet_address_spaces, local.dra_admin_public_ip) + dra_admin_cidr_list = concat(local.subnet_address_spaces, local.dra_admin_public_ip) dra_admin_image_exits = var.dra_admin_image_details != null ? true : false dra_admin_vhd_exits = var.dra_admin_vhd_details != null ? true : false @@ -24,7 +24,7 @@ module "dra_admin" { admin_ssh_password = local.password allowed_web_console_cidrs = var.web_console_cidr - allowed_analytics_cidrs = local.all_subnet_address_spaces + allowed_analytics_cidrs = local.subnet_address_spaces allowed_hub_cidrs = local.hub_cidr_list allowed_ssh_cidrs = concat(local.workstation_cidr, var.allowed_ssh_cidrs) @@ -62,7 +62,7 @@ module "dra_analytics" { analytics_ssh_password = local.password archiver_password = local.password - allowed_admin_cidrs = local.all_subnet_address_spaces + allowed_admin_cidrs = local.subnet_address_spaces allowed_ssh_cidrs = concat(local.workstation_cidr, local.hub_cidr_list) #allowed_ssh_cidrs = concat(var.allowed_ssh_cidrs, local.hub_cidr_list, local.workstation_cidr) diff --git a/examples/azure/poc/dsf_deployment/networking.tf b/examples/azure/poc/dsf_deployment/networking.tf index 991cdff67..57a902735 100644 --- a/examples/azure/poc/dsf_deployment/networking.tf +++ b/examples/azure/poc/dsf_deployment/networking.tf @@ -1,63 +1,41 @@ locals { - create_network = var.subnet_ids == null && var.subnet_id == null + hub_subnet_id = coalesce(var.subnet_id, try(module.network[0].vnet_subnets[0], null)) + hub_dr_subnet_id = coalesce(var.subnet_id, try(module.network[0].vnet_subnets[1], null)) - hub_subnet_id = coalesce(try(var.subnet_ids.hub_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[0], null)) - hub_dr_subnet_id = coalesce(try(var.subnet_ids.hub_dr_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[1], null)) + agentless_gw_subnet_id = coalesce(var.subnet_id, try(module.network[0].vnet_subnets[0], null)) + agentless_gw_dr_subnet_id = coalesce(var.subnet_id, try(module.network[0].vnet_subnets[1], null)) - agentless_gw_subnet_id = coalesce(try(var.subnet_ids.agentless_gw_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[0], null)) - agentless_gw_dr_subnet_id = coalesce(try(var.subnet_ids.agentless_gw_dr_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[1], null)) + db_subnet_id = coalesce(var.subnet_id, try(module.network[0].vnet_subnets[0], null)) - db_subnet_id = coalesce(try(var.subnet_ids.db_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[0], null)) + mx_subnet_id = coalesce(var.subnet_id, try(module.network[0].vnet_subnets[0], null)) + agent_gw_subnet_id = coalesce(var.subnet_id, try(module.network[0].vnet_subnets[0], null)) - mx_subnet_id = coalesce(try(var.subnet_ids.mx_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[0], null)) - agent_gw_subnet_id = coalesce(try(var.subnet_ids.agent_gw_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[0], null)) - - dra_admin_subnet_id = coalesce(try(var.subnet_ids.dra_admin_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[0], null)) - dra_analytics_subnet_id = coalesce(try(var.subnet_ids.dra_analytics_subnet_id, null), var.subnet_id, try(module.network[0].vnet_subnets[1], null)) + dra_admin_subnet_id = coalesce(var.subnet_id, try(module.network[0].vnet_subnets[0], null)) + dra_analytics_subnet_id = coalesce(var.subnet_id, try(module.network[0].vnet_subnets[1], null)) subnet_prefixes = cidrsubnets(var.vnet_ip_range, 8, 8) - subnet_ids = [ - local.hub_subnet_id, - local.hub_dr_subnet_id, - local.agent_gw_subnet_id, - local.agentless_gw_dr_subnet_id, - local.db_subnet_id, - local.mx_subnet_id, - local.agent_gw_subnet_id, - local.dra_admin_subnet_id, - local.dra_analytics_subnet_id, - ] - ipv4_regex = "([0-9]{1,3}\\.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?" - _all_subnet_address_spaces = distinct(sort( - concat([ - for subnet in data.azurerm_subnet.all_subnets : - subnet.address_prefixes - ]...) - )) - + _subnet_address_spaces = var.subnet_id == null ? module.network[0].vnet_address_space : data.azurerm_subnet.provided_subnet[0].address_prefixes # we can't currently use ipv6 IPs - all_subnet_address_spaces = [ + subnet_address_spaces = [ for cidr in local._all_subnet_address_spaces: cidr if can(regex(local.ipv4_regex, cidr)) ] } -data "azurerm_subnet" "all_subnets" { - for_each = { - for index, id in local.subnet_ids : index => id - } +data "azurerm_subnet" "provided_subnet" { + count = var.subnet_id == null ? 0 : 1 - resource_group_name = split("/", each.value)[4] - virtual_network_name = split("/", each.value)[8] - name = split("/", each.value)[10] + resource_group_name = split("/", var.subnet_id)[4] + virtual_network_name = split("/", var.subnet_id)[8] + name = split("/", var.subnet_id)[10] } # network module "network" { - count = local.create_network ? 1 : 0 + count = var.subnet_id == null ? 1 : 0 source = "Azure/network/azurerm" version = "5.3.0" vnet_name = "${local.deployment_name_salted}-${module.globals.current_user_name}" @@ -74,7 +52,7 @@ module "network" { } resource "azurerm_public_ip" "nat_gw_public_ip" { - count = local.create_network ? 1 : 0 + count = var.subnet_id == null ? 1 : 0 name = join("-", [var.deployment_name, "nat", "public", "ip"]) location = local.resource_group.location resource_group_name = local.resource_group.name @@ -83,7 +61,7 @@ resource "azurerm_public_ip" "nat_gw_public_ip" { } resource "azurerm_nat_gateway" "nat_gw" { - count = local.create_network ? 1 : 0 + count = var.subnet_id == null ? 1 : 0 name = join("-", [var.deployment_name, "nat", "gw"]) location = local.resource_group.location resource_group_name = local.resource_group.name @@ -92,14 +70,14 @@ resource "azurerm_nat_gateway" "nat_gw" { } resource "azurerm_nat_gateway_public_ip_association" "nat_gw_public_ip_association" { - count = local.create_network ? 1 : 0 + count = var.subnet_id == null ? 1 : 0 nat_gateway_id = azurerm_nat_gateway.nat_gw[0].id public_ip_address_id = azurerm_public_ip.nat_gw_public_ip[0].id } # subnet 1 is the private subnet resource "azurerm_subnet_nat_gateway_association" "nat_gw_vnet_association" { - count = local.create_network ? 1 : 0 + count = var.subnet_id == null ? 1 : 0 subnet_id = module.network[0].vnet_subnets[1] nat_gateway_id = azurerm_nat_gateway.nat_gw[0].id } \ No newline at end of file diff --git a/examples/azure/poc/dsf_deployment/outputs.tf b/examples/azure/poc/dsf_deployment/outputs.tf index 4e8642346..944a1feff 100644 --- a/examples/azure/poc/dsf_deployment/outputs.tf +++ b/examples/azure/poc/dsf_deployment/outputs.tf @@ -15,7 +15,7 @@ output "generated_network" { value = try({ vnet = module.network[0].vnet_id subnets = module.network[0].vnet_subnets - address_space = local.all_subnet_address_spaces + address_space = local.subnet_address_spaces }, null) } diff --git a/examples/azure/poc/dsf_deployment/sonar.tf b/examples/azure/poc/dsf_deployment/sonar.tf index 0e59a519c..1ea639f20 100644 --- a/examples/azure/poc/dsf_deployment/sonar.tf +++ b/examples/azure/poc/dsf_deployment/sonar.tf @@ -3,8 +3,8 @@ locals { hub_public_ip = var.enable_sonar ? (length(module.hub_main[0].public_ip) > 0 ? format("%s/32", module.hub_main[0].public_ip) : null) : null hub_dr_public_ip = var.enable_sonar && var.hub_hadr ? (length(module.hub_dr[0].public_ip) > 0 ? format("%s/32", module.hub_dr[0].public_ip) : null) : null - # WA since the following doesn't work: hub_cidr_list = concat(local.all_subnet_address_spaces, compact([local.hub_public_ip, local.hub_dr_public_ip])) - hub_cidr_list = var.enable_sonar ? (var.hub_hadr ? concat(local.all_subnet_address_spaces, [local.hub_public_ip, local.hub_dr_public_ip]) : concat(local.all_subnet_address_spaces, [local.hub_public_ip])) : local.all_subnet_address_spaces + # WA since the following doesn't work: hub_cidr_list = concat(local.subnet_address_spaces, compact([local.hub_public_ip, local.hub_dr_public_ip])) + hub_cidr_list = var.enable_sonar ? (var.hub_hadr ? concat(local.subnet_address_spaces, [local.hub_public_ip, local.hub_dr_public_ip]) : concat(local.subnet_address_spaces, [local.hub_public_ip])) : local.subnet_address_spaces } module "hub_main" { @@ -29,8 +29,8 @@ module "hub_main" { ssh_private_key_file_path = local_sensitive_file.ssh_key.filename } allowed_web_console_and_api_cidrs = var.web_console_cidr - allowed_hub_cidrs = local.all_subnet_address_spaces - allowed_agentless_gw_cidrs = local.all_subnet_address_spaces + allowed_hub_cidrs = local.subnet_address_spaces + allowed_agentless_gw_cidrs = local.subnet_address_spaces allowed_dra_admin_cidrs = local.dra_admin_cidr_list allowed_all_cidrs = local.workstation_cidr allowed_ssh_cidrs = var.allowed_ssh_cidrs @@ -79,8 +79,8 @@ module "hub_dr" { ssh_private_key_file_path = local_sensitive_file.ssh_key.filename } allowed_web_console_and_api_cidrs = var.web_console_cidr - allowed_hub_cidrs = local.all_subnet_address_spaces - allowed_agentless_gw_cidrs = local.all_subnet_address_spaces + allowed_hub_cidrs = local.subnet_address_spaces + allowed_agentless_gw_cidrs = local.subnet_address_spaces allowed_dra_admin_cidrs = local.dra_admin_cidr_list allowed_all_cidrs = local.workstation_cidr allowed_ssh_cidrs = var.allowed_ssh_cidrs @@ -127,8 +127,8 @@ module "agentless_gw_main" { ssh_public_key = tls_private_key.ssh_key.public_key_openssh ssh_private_key_file_path = local_sensitive_file.ssh_key.filename } - allowed_agentless_gw_cidrs = local.all_subnet_address_spaces - allowed_hub_cidrs = local.all_subnet_address_spaces + allowed_agentless_gw_cidrs = local.subnet_address_spaces + allowed_hub_cidrs = local.subnet_address_spaces allowed_all_cidrs = local.workstation_cidr allowed_ssh_cidrs = var.allowed_ssh_cidrs ingress_communication_via_proxy = { @@ -164,8 +164,8 @@ module "agentless_gw_dr" { ssh_public_key = tls_private_key.ssh_key.public_key_openssh ssh_private_key_file_path = local_sensitive_file.ssh_key.filename } - allowed_agentless_gw_cidrs = local.all_subnet_address_spaces - allowed_hub_cidrs = local.all_subnet_address_spaces + allowed_agentless_gw_cidrs = local.subnet_address_spaces + allowed_hub_cidrs = local.subnet_address_spaces allowed_all_cidrs = local.workstation_cidr allowed_ssh_cidrs = var.allowed_ssh_cidrs ingress_communication_via_proxy = { diff --git a/examples/azure/poc/dsf_deployment/variables.tf b/examples/azure/poc/dsf_deployment/variables.tf index 823a2577f..b18bf2429 100644 --- a/examples/azure/poc/dsf_deployment/variables.tf +++ b/examples/azure/poc/dsf_deployment/variables.tf @@ -95,27 +95,7 @@ variable "vnet_ip_range" { variable "subnet_id" { type = string default = null - description = "The ID of an existing subnet to put all resources in. Either 'subnet_id' or 'subnet_ids' should be provided but not both." -} - -variable "subnet_ids" { - type = object({ - hub_subnet_id = string - hub_dr_subnet_id = string - agentless_gw_subnet_id = string - agentless_gw_dr_subnet_id = string - mx_subnet_id = string - db_subnet_id = string - agent_gw_subnet_id = string - dra_admin_subnet_id = string - dra_analytics_subnet_id = string - }) - default = null - description = "The IDs of existing subnets to deploy resources in. Keep empty if you wish to provision new VPC and subnets, or if you are providing the subnet_id variable. db_subnet_ids can be an empty list only if no databases should be provisioned." - validation { - condition = var.subnet_ids == null || try(var.subnet_ids.hub_subnet_id != null && var.subnet_ids.hub_dr_subnet_id != null && var.subnet_ids.agentless_gw_subnet_id != null && var.subnet_ids.agentless_gw_dr_subnet_id != null && var.subnet_ids.dra_admin_subnet_id != null && var.subnet_ids.dra_analytics_subnet_id != null, false) - error_message = "Value must either be null or specified for all." - } + description = "The ID of an existing subnet to put all resources in." } ############################## From 3a7d3368c14b8738765e94c30376cb54b321677a Mon Sep 17 00:00:00 2001 From: Christopher Papke Date: Wed, 25 Jun 2025 08:07:01 -0700 Subject: [PATCH 09/13] working on networking --- examples/azure/poc/dsf_deployment/networking.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/azure/poc/dsf_deployment/networking.tf b/examples/azure/poc/dsf_deployment/networking.tf index 57a902735..8151e777d 100644 --- a/examples/azure/poc/dsf_deployment/networking.tf +++ b/examples/azure/poc/dsf_deployment/networking.tf @@ -20,7 +20,7 @@ locals { _subnet_address_spaces = var.subnet_id == null ? module.network[0].vnet_address_space : data.azurerm_subnet.provided_subnet[0].address_prefixes # we can't currently use ipv6 IPs subnet_address_spaces = [ - for cidr in local._all_subnet_address_spaces: cidr + for cidr in local._subnet_address_spaces: cidr if can(regex(local.ipv4_regex, cidr)) ] } From 8b6b2e77a2490481b07a77ba8a2edc738522f592 Mon Sep 17 00:00:00 2001 From: Segev Elmalech Date: Wed, 2 Jul 2025 12:04:49 +0300 Subject: [PATCH 10/13] refactor destroy step in Azure CLI workflow --- .github/workflows/dsf_poc_cli_azure.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/dsf_poc_cli_azure.yml b/.github/workflows/dsf_poc_cli_azure.yml index 1bfb68de9..fbaba431a 100644 --- a/.github/workflows/dsf_poc_cli_azure.yml +++ b/.github/workflows/dsf_poc_cli_azure.yml @@ -350,13 +350,13 @@ jobs: id: destroy if: always() run: | - if [ '${{ steps.apply.conclusion }}' == 'success' ] || [ "${{ github.event_name }}" != 'schedule' ]; then - if [ -n "${{ env.DEPLOYMENT_VERSION }}" && "${{ env.DEPLOYMENT_VERSION }}" != $'\n' ]; then - terraform -chdir=$EXAMPLE_DIR destroy -var dam_license=license.mprv -var "${{ env.DEPLOYMENT_VERSION }}" -auto-approve - else - terraform -chdir=$EXAMPLE_DIR destroy -var dam_license=license.mprv -auto-approve - fi +# if [ '${{ steps.apply.conclusion }}' == 'success' ] || [ "${{ github.event_name }}" != 'schedule' ]; then + if [ -n "${{ env.DEPLOYMENT_VERSION }}" && "${{ env.DEPLOYMENT_VERSION }}" != $'\n' ]; then + terraform -chdir=$EXAMPLE_DIR destroy -var dam_license=license.mprv -var "${{ env.DEPLOYMENT_VERSION }}" -auto-approve + else + terraform -chdir=$EXAMPLE_DIR destroy -var dam_license=license.mprv -auto-approve fi +# fi From 6c4b8f4db05713153341cba96753892a209401b7 Mon Sep 17 00:00:00 2001 From: Segev Elmalech Date: Wed, 2 Jul 2025 14:29:05 +0300 Subject: [PATCH 11/13] simplify destroy step in Azure CLI workflow by removing unnecessary condition --- .github/workflows/dsf_poc_cli_azure.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/dsf_poc_cli_azure.yml b/.github/workflows/dsf_poc_cli_azure.yml index fbaba431a..0cc0f083c 100644 --- a/.github/workflows/dsf_poc_cli_azure.yml +++ b/.github/workflows/dsf_poc_cli_azure.yml @@ -350,13 +350,11 @@ jobs: id: destroy if: always() run: | -# if [ '${{ steps.apply.conclusion }}' == 'success' ] || [ "${{ github.event_name }}" != 'schedule' ]; then if [ -n "${{ env.DEPLOYMENT_VERSION }}" && "${{ env.DEPLOYMENT_VERSION }}" != $'\n' ]; then terraform -chdir=$EXAMPLE_DIR destroy -var dam_license=license.mprv -var "${{ env.DEPLOYMENT_VERSION }}" -auto-approve else terraform -chdir=$EXAMPLE_DIR destroy -var dam_license=license.mprv -auto-approve fi -# fi From 490e8ac242b9047391116f6622418bcbe64d21d8 Mon Sep 17 00:00:00 2001 From: Christopher Papke Date: Fri, 4 Jul 2025 09:57:27 -0700 Subject: [PATCH 12/13] fix private key --- .../aws/installation/sonar_multi_account_deployment/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/aws/installation/sonar_multi_account_deployment/main.tf b/examples/aws/installation/sonar_multi_account_deployment/main.tf index 9afbb94df..d832949f5 100644 --- a/examples/aws/installation/sonar_multi_account_deployment/main.tf +++ b/examples/aws/installation/sonar_multi_account_deployment/main.tf @@ -432,7 +432,7 @@ module "hub_dr_gw_main_federation" { gw_info = { gw_ip_address = each.value.private_ip gw_federation_ip_address = each.value.private_ip - gw_private_ssh_key_path = local.gw_dr_private_key_file_path + gw_private_ssh_key_path = local.gw_main_private_key_file_path gw_ssh_user = each.value.ssh_user } hub_proxy_info = var.proxy_address != null ? { From 6530e7247b51497435c2283f62db9771152ce0a3 Mon Sep 17 00:00:00 2001 From: Sivan Hajbi Date: Mon, 14 Jul 2025 21:26:43 +0300 Subject: [PATCH 13/13] dsfkit with cm (#472) (#473) * dsfkit with cm (#472) * code review comments * add cte ddc agents * fix ami_id, ciphertrust deplyment based on sonar version and some fixes * fix ciphertrust manager module folder name * add readme * fix sonar version * Commit on behalf of Sivan: In CM integration, temporarily support Sonar version 15 only (not 4.19 or 4.18) until compability issue is resolved * Set default value of enable_ciphertrust to false until dsf_poc_cli action will be handled * Added sonar-with-fam test * fix module paths * disable test_apply in release.yml * Added 'all-permutations' option to AWS POC CLI action + other changes * Changed description of DSF POC CLI action input * Changed description of DSF POC CLI action input * Fixed DSF POC CLI yaml issue + changed enable_ciphertrust default to true --------- Co-authored-by: lindanasredin --- .github/workflows/deploy_module.yml | 12 ++ .github/workflows/dsf_poc_cli.yml | 28 ++-- .github/workflows/dsf_poc_standalone.yml | 6 +- .github/workflows/release.yml | 24 +-- README.md | 10 ++ examples/aws/poc/dsf_deployment/README.md | 34 +++- examples/aws/poc/dsf_deployment/cm.tf | 69 ++++++++ .../aws/poc/dsf_deployment/cte_ddc_agents.tf | 114 ++++++++++++++ examples/aws/poc/dsf_deployment/main.tf | 14 +- examples/aws/poc/dsf_deployment/networking.tf | 28 ++-- examples/aws/poc/dsf_deployment/outputs.tf | 64 ++++++++ examples/aws/poc/dsf_deployment/sonar.tf | 22 ++- examples/aws/poc/dsf_deployment/variables.tf | 149 ++++++++++++++++-- examples/aws/poc/dsf_deployment/versions.tf | 5 + modules/aws/agentless-gw/main.tf | 7 + modules/aws/agentless-gw/variables.tf | 10 ++ modules/aws/ciphertrust-manager/README.md | 104 ++++++++++++ modules/aws/ciphertrust-manager/ami.tf | 39 +++++ modules/aws/ciphertrust-manager/main.tf | 55 +++++++ modules/aws/ciphertrust-manager/outputs.tf | 35 ++++ modules/aws/ciphertrust-manager/sg.tf | 88 +++++++++++ modules/aws/ciphertrust-manager/variables.tf | 140 ++++++++++++++++ modules/aws/ciphertrust-manager/versions.tf | 10 ++ modules/aws/cte-ddc-agent/README.md | 106 +++++++++++++ modules/aws/cte-ddc-agent/ami.tf | 51 ++++++ .../cte-ddc-agent/cte_agent_reg_params.tftpl | 4 + modules/aws/cte-ddc-agent/dummy.txt | 0 modules/aws/cte-ddc-agent/linux.tf | 27 ++++ modules/aws/cte-ddc-agent/main.tf | 120 ++++++++++++++ modules/aws/cte-ddc-agent/outputs.tf | 31 ++++ modules/aws/cte-ddc-agent/sg.tf | 75 +++++++++ modules/aws/cte-ddc-agent/variables.tf | 147 +++++++++++++++++ modules/aws/cte-ddc-agent/versions.tf | 10 ++ modules/aws/cte-ddc-agent/windows.tf | 73 +++++++++ modules/aws/hub/cm_association.tf | 64 ++++++++ modules/aws/hub/variables.tf | 35 ++++ .../README.md | 97 ++++++++++++ .../ddc_active_node_setup.tftpl | 55 +++++++ .../ciphertrust-manager-cluster-setup/main.tf | 36 +++++ .../outputs.tf | 0 .../variables.tf | 36 +++++ .../versions.tf | 10 ++ sed.expr | 3 + 43 files changed, 1992 insertions(+), 55 deletions(-) create mode 100644 examples/aws/poc/dsf_deployment/cm.tf create mode 100644 examples/aws/poc/dsf_deployment/cte_ddc_agents.tf create mode 100644 modules/aws/ciphertrust-manager/README.md create mode 100644 modules/aws/ciphertrust-manager/ami.tf create mode 100644 modules/aws/ciphertrust-manager/main.tf create mode 100644 modules/aws/ciphertrust-manager/outputs.tf create mode 100644 modules/aws/ciphertrust-manager/sg.tf create mode 100644 modules/aws/ciphertrust-manager/variables.tf create mode 100644 modules/aws/ciphertrust-manager/versions.tf create mode 100644 modules/aws/cte-ddc-agent/README.md create mode 100644 modules/aws/cte-ddc-agent/ami.tf create mode 100644 modules/aws/cte-ddc-agent/cte_agent_reg_params.tftpl create mode 100644 modules/aws/cte-ddc-agent/dummy.txt create mode 100644 modules/aws/cte-ddc-agent/linux.tf create mode 100644 modules/aws/cte-ddc-agent/main.tf create mode 100644 modules/aws/cte-ddc-agent/outputs.tf create mode 100644 modules/aws/cte-ddc-agent/sg.tf create mode 100644 modules/aws/cte-ddc-agent/variables.tf create mode 100644 modules/aws/cte-ddc-agent/versions.tf create mode 100644 modules/aws/cte-ddc-agent/windows.tf create mode 100644 modules/aws/hub/cm_association.tf create mode 100644 modules/null/ciphertrust-manager-cluster-setup/README.md create mode 100644 modules/null/ciphertrust-manager-cluster-setup/ddc_active_node_setup.tftpl create mode 100644 modules/null/ciphertrust-manager-cluster-setup/main.tf create mode 100644 modules/null/ciphertrust-manager-cluster-setup/outputs.tf create mode 100644 modules/null/ciphertrust-manager-cluster-setup/variables.tf create mode 100644 modules/null/ciphertrust-manager-cluster-setup/versions.tf diff --git a/.github/workflows/deploy_module.yml b/.github/workflows/deploy_module.yml index 388107699..f1085983f 100644 --- a/.github/workflows/deploy_module.yml +++ b/.github/workflows/deploy_module.yml @@ -127,6 +127,18 @@ jobs: hidden_submodules: "azurerm/statistics null/statistics" begin_tag: 1.7.6 + # CipherTrust + ## aws provider + - source_module: "aws/ciphertrust-manager" + destination_repo: "terraform-aws-dsf-ciphertrust-manager" + begin_tag: 1.7.31 + - source_module: "aws/ciphertrust-manager-cluster-setup" + destination_repo: "terraform-aws-dsf-ciphertrust-manager-cluster-setup" + begin_tag: 1.7.31 + - source_module: "aws/cte-ddc-agent" + destination_repo: "terraform-aws-dsf-cte-ddc-agent" + begin_tag: 1.7.31 + # Globals ## aws provider - source_module: "aws/core/globals" diff --git a/.github/workflows/dsf_poc_cli.yml b/.github/workflows/dsf_poc_cli.yml index c42fc0088..f22067269 100644 --- a/.github/workflows/dsf_poc_cli.yml +++ b/.github/workflows/dsf_poc_cli.yml @@ -12,7 +12,7 @@ on: deployment_type: required: false type: string - default: 'all' + default: 'all-permutations' version: type: string default: 'latest' @@ -44,12 +44,12 @@ on: default: false required: false deployment_type: - description: 'Choose the type of deployments to run: all (default), sonar, dam, dra' + description: 'Choose the type of deployments to run: sonar, sonar-with-fam, dra, dam, all-products (currently does not include FAM) or all-permutations' type: string - default: 'all' + default: 'all-permutations' required: false product_version: - description: 'Product (DAM, DRA, SONAR) version to deploy, default is latest' + description: 'Product version to deploy. Valid for options: sonar, sonar-with-fam, dra and dam. Otherwise latest per product is used.' type: string default: 'latest' required: false @@ -99,14 +99,15 @@ jobs: - name: Set Matrix id: set-matrix env: - VAR: ${{ github.event.inputs.deployment_type || 'all' }} + VAR: ${{ github.event.inputs.deployment_type || 'all-permutations' }} run: | MATRIX=$(jq -n --compact-output --arg var "$VAR" '{ "include": [ - (if $var == "all" then {"name":"DSF POC","workspace":"dsf_cli-all-","enable_sonar":true,"enable_dam":true,"enable_dra":true} else empty end), - (if $var == "all" or $var == "sonar" then {"name":"DSF POC - SONAR","workspace":"dsf_cli-sonar-","enable_sonar":true,"enable_dam":false,"enable_dra":false} else empty end), - (if $var == "all" or $var == "dam" then {"name":"DSF POC - DAM","workspace":"dsf_cli-dam-","enable_sonar":false,"enable_dam":true,"enable_dra":false} else empty end), - (if $var == "all" or $var == "dra" then {"name":"DSF POC - DRA","workspace":"dsf_cli-dra-","enable_sonar":false,"enable_dam":false,"enable_dra":true} else empty end) + (if $var == "all-permutations" or $var == "all-products" then {"name":"DSF POC","workspace":"dsf_cli-all-","enable_sonar":true,"enable_ciphertrust":false,"enable_dam":true,"enable_dra":true} else empty end), + (if $var == "all-permutations" or $var == "sonar" then {"name":"DSF POC - Sonar","workspace":"dsf_cli-sonar-","enable_sonar":true,"enable_ciphertrust":false,"enable_dam":false,"enable_dra":false} else empty end), + (if $var == "all-permutations" or $var == "sonar-with-fam" then {"name":"DSF POC - Sonar with FAM","workspace":"dsf_cli-sonar-with-fam-","enable_sonar":true,"enable_ciphertrust":true,"enable_dam":false,"enable_dra":false} else empty end), + (if $var == "all-permutations" or $var == "dam" then {"name":"DSF POC - DAM","workspace":"dsf_cli-dam-","enable_sonar":false,"enable_ciphertrust":false,"enable_dam":true,"enable_dra":false} else empty end), + (if $var == "all-permutations" or $var == "dra" then {"name":"DSF POC - DRA","workspace":"dsf_cli-dra-","enable_sonar":false,"enable_ciphertrust":false,"enable_dam":false,"enable_dra":true} else empty end) ] }') @@ -123,6 +124,8 @@ jobs: DEPLOYMENT_VERSION="dra_version=${{ github.event.inputs.product_version }}" elif [[ "${{ github.event.inputs.deployment_type }}" == "sonar" ]]; then DEPLOYMENT_VERSION="sonar_version=${{ github.event.inputs.product_version }}" + elif [[ "${{ github.event.inputs.deployment_type }}" == "sonar-with-fam" ]]; then + DEPLOYMENT_VERSION="sonar_version=${{ github.event.inputs.product_version }}" fi fi echo "deployment_version=$DEPLOYMENT_VERSION" >> $GITHUB_OUTPUT @@ -139,6 +142,7 @@ jobs: EXAMPLE_DIR: ./examples/aws/poc/dsf_deployment AWS_REGION: eu-west-2 TF_VAR_enable_sonar: ${{ matrix.enable_sonar }} + TF_VAR_enable_ciphertrust: ${{ matrix.enable_ciphertrust }} TF_VAR_enable_dam: ${{ matrix.enable_dam }} TF_VAR_enable_dra: ${{ matrix.enable_dra }} TF_VAR_allowed_ssh_cidrs: ${{ secrets.ALLOWED_SSH_CIDRS }} @@ -229,6 +233,9 @@ jobs: mv $EXAMPLE_DIR/networking.tf{,_} mv $EXAMPLE_DIR/agentless_sources.tf{,_} mv $EXAMPLE_DIR/agent_sources.tf{,_} + mv $EXAMPLE_DIR/versions.tf{,_} + mv $EXAMPLE_DIR/cm.tf{,_} + mv $EXAMPLE_DIR/cte_ddc_agents.tf{,_} terraform -chdir=$EXAMPLE_DIR destroy -var dam_license=license.mprv -auto-approve mv $EXAMPLE_DIR/main.tf{_,} mv $EXAMPLE_DIR/outputs.tf{_,} @@ -238,6 +245,9 @@ jobs: mv $EXAMPLE_DIR/networking.tf{_,} mv $EXAMPLE_DIR/agentless_sources.tf{_,} mv $EXAMPLE_DIR/agent_sources.tf{_,} + mv $EXAMPLE_DIR/versions.tf{_,} + mv $EXAMPLE_DIR/cm.tf{_,} + mv $EXAMPLE_DIR/cte_ddc_agents.tf{_,} fi - name: Terraform Validate diff --git a/.github/workflows/dsf_poc_standalone.yml b/.github/workflows/dsf_poc_standalone.yml index 56d019f58..c9408b347 100644 --- a/.github/workflows/dsf_poc_standalone.yml +++ b/.github/workflows/dsf_poc_standalone.yml @@ -1,10 +1,10 @@ -name: DSF POC Standalone +name: DSF POC Standalone - AWS and Azure on: workflow_dispatch: # This allows the workflow to be manually triggered from the GitHub UI inputs: deployment_type: - description: "Deployment type to pass, choose: dra, dam or sonar." + description: "Deployment type to pass, choose: sonar, sonar-with-fam, dra or dam." required: true default: "dra" version: @@ -37,7 +37,7 @@ jobs: with: use_modules_from_terraform_registry: true explicit_ref: master - deployment_type: "dra" + deployment_type: ${{ github.event.inputs.deployment_type }} product_full_version: ${{ github.event.inputs.version }} secrets: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8cd675972..89d14e3aa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -206,15 +206,15 @@ jobs: env: GH_TOKEN: ${{ github.token }} - test_apply: - needs: release - uses: ./.github/workflows/sonar_poc_cli.yml - with: - use_modules_from_terraform_registry: true - explicit_ref: master - secrets: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - ALLOWED_SSH_CIDRS: ${{secrets.ALLOWED_SSH_CIDRS }} - DEPLOYMENT_TAGS: ${{ secrets.DEPLOYMENT_TAGS }} +# test_apply: +# needs: release +# uses: ./.github/workflows/sonar_poc_cli.yml +# with: +# use_modules_from_terraform_registry: true +# explicit_ref: master +# secrets: +# AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} +# AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} +# SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} +# ALLOWED_SSH_CIDRS: ${{secrets.ALLOWED_SSH_CIDRS }} +# DEPLOYMENT_TAGS: ${{ secrets.DEPLOYMENT_TAGS }} diff --git a/README.md b/README.md index d198197c8..ffb3b6b18 100644 --- a/README.md +++ b/README.md @@ -595,6 +595,16 @@ The following table lists the _latest_ DSF Kit releases, their release date and
2. Added internal support for DRA version 15.0. Set the variable ‘dra_version’ to 15.0 to use it. + + 15 Jul 2025 + + 1.7.31 + + + 1. Added support for CipherTrust in AWS. +
2. Updated the AWS POC dsf_deployment example. + + diff --git a/examples/aws/poc/dsf_deployment/README.md b/examples/aws/poc/dsf_deployment/README.md index a1c4252e5..bc3a3fc4f 100644 --- a/examples/aws/poc/dsf_deployment/README.md +++ b/examples/aws/poc/dsf_deployment/README.md @@ -1,7 +1,7 @@ # DSF Deployment example [![GitHub tag](https://img.shields.io/github/v/tag/imperva/dsfkit.svg)](https://github.com/imperva/dsfkit/tags) -This example provides a full DSF (Data Security Fabric) deployment with DSF Hub, Agentless Gateways, DAM (Database Activity Monitoring), DRA (Data Risk Analytics) and Agent and Agentless audit sources. +This example provides a full DSF (Data Security Fabric) deployment with DSF Hub, Agentless Gateways, DAM (Database Activity Monitoring), DRA (Data Risk Analytics) and Agent and Agentless audit sources, and also deploys CipherTrust Manager and CipherTrust Transparent Encryption (CTE) and/or Data Discovery and Classification (DDC) agents. ## Modularity The deployment is modular and allows users to deploy one or more of the following modules: @@ -21,6 +21,9 @@ The deployment is modular and allows users to deploy one or more of the followin 5. Audit sources - Agent audit sources (EC2 instances) - Agentless audit sources (RDS instances) +6. CipherTrust Manager +7. CipherTrust Transparent Encryption (CTE) and/or Data Discovery and Classification (DDC) Agents + ### Deploying Specific Modules @@ -33,9 +36,10 @@ To deploy only the DAM module, set the following variables in your Terraform con enable_dam = true enable_sonar = false enable_dra = false +enable_ciphertrust = false ``` -This configuration will enable the DAM module while disabling the DSF Hub and DRA modules. +This configuration will enable the DAM module while disabling the DSF Hub, DRA and CipherTrust modules. #### 2. DRA Only Deployment @@ -44,9 +48,10 @@ To deploy only the DRA module, set the following variables in your Terraform con enable_dam = false enable_sonar = false enable_dra = true +enable_ciphertrust = false ``` -This configuration will enable the DRA module while disabling the DSF Hub and DAM modules. +This configuration will enable the DRA module while disabling the DSF Hub, DAM and CipherTrust modules. #### 3. Sonar Only Deployment @@ -55,9 +60,22 @@ To deploy only the Sonar module, set the following variables in your Terraform c enable_dam = false enable_sonar = true enable_dra = false +enable_ciphertrust = false +``` + +This configuration will enable the Sonar module, including the DSF Hub, while disabling the DAM, DRA and CipherTrust modules. + +#### 4. CipherTrust Only Deployment + +To deploy only the Sonar module, set the following variables in your Terraform configuration: +``` +enable_dam = false +enable_sonar = false +enable_dra = false +enable_ciphertrust = true ``` -This configuration will enable the Sonar module, including the DSF Hub, while disabling the DAM and DRA modules. +This configuration will enable the CipherTrust module, including the CipherTrust Manager and the CTE and/or DDC agents, while disabling the DAM, DRA and Sonar modules. Feel free to customize your deployment by setting the appropriate variables based on your requirements. @@ -68,11 +86,19 @@ Several variables in the `variables.tf` file are important for configuring the d - `enable_sonar`: Enable Sonar sub-product - `enable_dam`: Enable DAM sub-product - `enable_dra`: Enable DRA sub-product +- `enable_ciphertrust`: Enable CipherTrust sub-product ### Server Count - `dra_analytics_count`: Number of DRA Analytics servers - `agentless_gw_count`: Number of Agentless Gateways - `agent_gw_count`: Number of Agent Gateways +- `ciphertrust_manager_count`: Number of CipherTrust Manager servers (if more than one, configured as a cluster) +- `cte_ddc_agents_linux_count`: Number of CTE-DDC agent Linux servers +- `cte_agents_linux_count`: Number of CTE agent Linux servers +- `ddc_agents_linux_count`: Number of DDC agent Linux servers +- `cte_ddc_agents_windows_count`: Number of CTE-DDC agent Windows servers +- `cte_agents_windows_count`: Number of CTE agent Windows servers +- `ddc_agents_windows_count`: Number of DDC agent Windows servers ### High Availability (HADR) - `hub_hadr`: Enable DSF Hub High Availability Disaster Recovery (HADR) diff --git a/examples/aws/poc/dsf_deployment/cm.tf b/examples/aws/poc/dsf_deployment/cm.tf new file mode 100644 index 000000000..29b370c1f --- /dev/null +++ b/examples/aws/poc/dsf_deployment/cm.tf @@ -0,0 +1,69 @@ +locals { + ciphertrust_manager_count = var.enable_ciphertrust ? var.ciphertrust_manager_count : 0 + ciphertrust_cidr_list = [data.aws_subnet.ciphertrust_manager.cidr_block] + ciphertrust_manager_web_console_username = "admin" +} + +module "ciphertrust_manager" { + source = "../../../../modules/aws/ciphertrust-manager" + # source = "imperva/dsf-ciphertrust-manager/aws" + # version = "1.7.17" # latest release tag + count = local.ciphertrust_manager_count + ami = var.ciphertrust_manager_ami_id == null ? null : { + id = var.ciphertrust_manager_ami_id + name_regex = null + product_code = null + owner_account_id = null + } + friendly_name = join("-", [local.deployment_name_salted, "ciphertrust", "manager", count.index]) + ebs = var.ciphertrust_manager_ebs_details + subnet_id = local.ciphertrust_manager_subnet_id + attach_persistent_public_ip = true + key_pair = module.key_pair.key_pair.key_pair_name + allowed_web_console_and_api_cidrs = var.web_console_cidr + allowed_ssh_cidrs = concat(local.workstation_cidr, var.allowed_ssh_cidrs) + allowed_cluster_nodes_cidrs = [data.aws_subnet.ciphertrust_manager.cidr_block] + allowed_ddc_agents_cidrs = [data.aws_subnet.cte_ddc_agent.cidr_block] + allowed_all_cidrs = local.workstation_cidr + tags = local.tags + depends_on = [ + module.vpc + ] +} + +provider "ciphertrust" { + address = local.ciphertrust_manager_count > 0 ? "https://${module.ciphertrust_manager[0].public_ip}" : null + username = local.ciphertrust_manager_web_console_username + password = local.ciphertrust_manager_password + // destroy cluster can take almost a minute so give us a bit of a buffer + rest_api_timeout = 720 +} + +resource "ciphertrust_trial_license" "trial_license" { + count = local.ciphertrust_manager_count > 0 ? 1 : 0 + flag = "activate" +} + +module "ciphertrust_manager_cluster_setup" { + source = "../../../../modules/null/ciphertrust-manager-cluster-setup" + # source = "imperva/dsf-ciphertrust-manager-cluster-setup/aws" + # version = "1.7.17" # latest release tag + count = local.ciphertrust_manager_count > 1 ? 1 : 0 + nodes = [ + for i in range(length(module.ciphertrust_manager)) : { + host = module.ciphertrust_manager[i].private_ip + public_address = coalesce(module.ciphertrust_manager[i].public_ip, module.ciphertrust_manager[i].private_ip) + } + ] + credentials = { + user = local.ciphertrust_manager_web_console_username + password = local.ciphertrust_manager_password + } + ddc_node_setup = { + enabled = true + node_address = coalesce(module.ciphertrust_manager[0].public_ip, module.ciphertrust_manager[0].private_ip) + } + depends_on = [ + module.ciphertrust_manager + ] +} \ No newline at end of file diff --git a/examples/aws/poc/dsf_deployment/cte_ddc_agents.tf b/examples/aws/poc/dsf_deployment/cte_ddc_agents.tf new file mode 100644 index 000000000..39fb5e22c --- /dev/null +++ b/examples/aws/poc/dsf_deployment/cte_ddc_agents.tf @@ -0,0 +1,114 @@ +locals { + cte_ddc_linux_count = local.ciphertrust_manager_count > 0 ? var.cte_ddc_agents_linux_count : 0 + cte_linux_count = local.ciphertrust_manager_count > 0 ? var.cte_agents_linux_count : 0 + ddc_linux_count = local.ciphertrust_manager_count > 0 ? var.ddc_agents_linux_count : 0 + cte_ddc_windows_count = local.ciphertrust_manager_count > 0 ? var.cte_ddc_agents_windows_count : 0 + cte_windows_count = local.ciphertrust_manager_count > 0 ? var.cte_agents_windows_count : 0 + ddc_windows_count = local.ciphertrust_manager_count > 0 ? var.ddc_agents_windows_count : 0 + + installation_map = { + "Red Hat" = { + cte_installation_path = var.cte_agent_linux_installation_file + ddc_installation_path = var.ddc_agent_linux_installation_file + }, + "Windows" = { + cte_installation_path = var.cte_agent_windows_installation_file + ddc_installation_path = var.ddc_agent_windows_installation_file + } + } + + # Prepare Linux Agent Instances + linux_cte_ddc_instances = [for i in range(local.cte_ddc_linux_count) : { + id = "cte-ddc-agent-linux-${i}" + os_type = "Red Hat" + install_cte = true + install_ddc = true + }] + linux_cte_only_instances = [for i in range(local.cte_linux_count) : { + id = "cte-agent-linux-${i}" + os_type = "Red Hat" + install_cte = true + install_ddc = false + }] + linux_ddc_only_instances = [for i in range(local.ddc_linux_count) : { + id = "ddc-agent-linux-${i}" + os_type = "Red Hat" + install_cte = false + install_ddc = true + }] + # Prepare Windows Agent Instances + windows_cte_ddc_instances = [for i in range(local.cte_ddc_windows_count) : { + id = "cte-ddc-agent-windows-${i}" + os_type = "Windows" + install_cte = true + install_ddc = true + }] + windows_cte_only_instances = [for i in range(local.cte_windows_count) : { + id = "cte-agent-windows-${i}" + os_type = "Windows" + install_cte = true + install_ddc = false + }] + windows_ddc_only_instances = [for i in range(local.ddc_windows_count) : { + id = "ddc-agent-windows-${i}" + os_type = "Windows" + install_cte = false + install_ddc = true + }] + + + # Concatenate all agent lists and convert to a map for for_each + all_agent_instances_map = { + for instance in concat( + local.linux_cte_ddc_instances, + local.linux_cte_only_instances, + local.linux_ddc_only_instances, + local.windows_cte_ddc_instances, + local.windows_cte_only_instances, + local.windows_ddc_only_instances + ) : instance.id => instance + } +} + +resource "ciphertrust_cte_registration_token" "reg_token" { + count = length(local.all_agent_instances_map) > 0 ? 1 : 0 + lifetime = "24h" + max_clients = 100 + name_prefix = "dsf-agent" +} + +module "cte_ddc_agents" { + source = "../../../../modules/aws/cte-ddc-agent" +# source = "imperva/dsf-cte-ddc-agent/aws" +# version = "1.7.17" # latest release tag +# count = local.cte_ddc_linux_count + for_each = local.all_agent_instances_map + friendly_name = join("-", [local.deployment_name_salted, each.value.id]) + subnet_id = local.cte_ddc_agent_subnet_id + ssh_key_pair = { + ssh_private_key_file_path = module.key_pair.private_key_file_path + ssh_public_key_name = module.key_pair.key_pair.key_pair_name + } + os_type = each.value.os_type + attach_persistent_public_ip = true + use_public_ip = true + allowed_ssh_cidrs = concat(local.workstation_cidr, var.allowed_ssh_cidrs) + allowed_rdp_cidrs = each.value.os_type == "Windows" ? concat(local.workstation_cidr, var.allowed_ssh_cidrs) : null + cipher_trust_manager_address = module.ciphertrust_manager[0].private_ip + agent_installation = { + registration_token = ciphertrust_cte_registration_token.reg_token[0].token + install_cte = each.value.install_cte + install_ddc = each.value.install_ddc + cte_agent_installation_file = each.value.install_cte ? local.installation_map[each.value.os_type].cte_installation_path : null + ddc_agent_installation_file = each.value.install_ddc ? local.installation_map[each.value.os_type].ddc_installation_path : null + } + tags = local.tags + depends_on = [ + module.vpc, + module.ciphertrust_manager, + ciphertrust_trial_license.trial_license, + module.ciphertrust_manager_cluster_setup + ] +} + + diff --git a/examples/aws/poc/dsf_deployment/main.tf b/examples/aws/poc/dsf_deployment/main.tf index 80615a7e4..a97e7cbf9 100644 --- a/examples/aws/poc/dsf_deployment/main.tf +++ b/examples/aws/poc/dsf_deployment/main.tf @@ -15,6 +15,7 @@ module "globals" { sonar_version = var.sonar_version dra_version = var.dra_version + installation_s3_key = var.tarball_location != null ? var.tarball_location.s3_key : null } module "key_pair" { @@ -27,10 +28,11 @@ module "key_pair" { } locals { - workstation_cidr_24 = [format("%s.0/24", regex("\\d*\\.\\d*\\.\\d*", module.globals.my_ip))] - deployment_name_salted = join("-", [var.deployment_name, module.globals.salt]) - password = var.password != null ? var.password : module.globals.random_password - workstation_cidr = var.workstation_cidr != null ? var.workstation_cidr : local.workstation_cidr_24 - tags = merge(module.globals.tags, var.additional_tags, { "deployment_name" = local.deployment_name_salted }) - private_key_file_path = module.key_pair.private_key_file_path + workstation_cidr_24 = [format("%s.0/24", regex("\\d*\\.\\d*\\.\\d*", module.globals.my_ip))] + deployment_name_salted = join("-", [var.deployment_name, module.globals.salt]) + password = var.password != null ? var.password : module.globals.random_password + ciphertrust_manager_password = var.ciphertrust_manager_password != null ? var.ciphertrust_manager_password : module.globals.random_password + workstation_cidr = var.workstation_cidr != null ? var.workstation_cidr : local.workstation_cidr_24 + tags = merge(module.globals.tags, var.additional_tags, { "deployment_name" = local.deployment_name_salted }) + private_key_file_path = module.key_pair.private_key_file_path } diff --git a/examples/aws/poc/dsf_deployment/networking.tf b/examples/aws/poc/dsf_deployment/networking.tf index 689ef3678..d40e5e277 100644 --- a/examples/aws/poc/dsf_deployment/networking.tf +++ b/examples/aws/poc/dsf_deployment/networking.tf @@ -1,13 +1,15 @@ locals { - hub_subnet_id = var.subnet_ids != null ? var.subnet_ids.hub_subnet_id : module.vpc[0].public_subnets[0] - hub_dr_subnet_id = var.subnet_ids != null ? var.subnet_ids.hub_dr_subnet_id : module.vpc[0].public_subnets[1] - agentless_gw_subnet_id = var.subnet_ids != null ? var.subnet_ids.agentless_gw_subnet_id : module.vpc[0].private_subnets[0] - agentless_gw_dr_subnet_id = var.subnet_ids != null ? var.subnet_ids.agentless_gw_dr_subnet_id : module.vpc[0].private_subnets[1] - db_subnet_ids = var.subnet_ids != null ? var.subnet_ids.db_subnet_ids : module.vpc[0].public_subnets - mx_subnet_id = var.subnet_ids != null ? var.subnet_ids.mx_subnet_id : module.vpc[0].public_subnets[0] - dra_admin_subnet_id = var.subnet_ids != null ? var.subnet_ids.dra_admin_subnet_id : module.vpc[0].public_subnets[0] - dra_analytics_subnet_id = var.subnet_ids != null ? var.subnet_ids.dra_analytics_subnet_id : module.vpc[0].private_subnets[0] - agent_gw_subnet_id = var.subnet_ids != null ? var.subnet_ids.agent_gw_subnet_id : module.vpc[0].private_subnets[0] + hub_subnet_id = var.subnet_ids != null ? var.subnet_ids.hub_subnet_id : module.vpc[0].public_subnets[0] + hub_dr_subnet_id = var.subnet_ids != null ? var.subnet_ids.hub_dr_subnet_id : module.vpc[0].public_subnets[1] + agentless_gw_subnet_id = var.subnet_ids != null ? var.subnet_ids.agentless_gw_subnet_id : module.vpc[0].private_subnets[0] + agentless_gw_dr_subnet_id = var.subnet_ids != null ? var.subnet_ids.agentless_gw_dr_subnet_id : module.vpc[0].private_subnets[1] + db_subnet_ids = var.subnet_ids != null ? var.subnet_ids.db_subnet_ids : module.vpc[0].public_subnets + mx_subnet_id = var.subnet_ids != null ? var.subnet_ids.mx_subnet_id : module.vpc[0].public_subnets[0] + dra_admin_subnet_id = var.subnet_ids != null ? var.subnet_ids.dra_admin_subnet_id : module.vpc[0].public_subnets[0] + dra_analytics_subnet_id = var.subnet_ids != null ? var.subnet_ids.dra_analytics_subnet_id : module.vpc[0].private_subnets[0] + agent_gw_subnet_id = var.subnet_ids != null ? var.subnet_ids.agent_gw_subnet_id : module.vpc[0].private_subnets[0] + ciphertrust_manager_subnet_id = var.subnet_ids != null ? var.subnet_ids.ciphertrust_subnet_id : module.vpc[0].public_subnets[0] + cte_ddc_agent_subnet_id = var.subnet_ids != null ? var.subnet_ids.cte_ddc_agent_subnet_id : module.vpc[0].public_subnets[0] } module "vpc" { @@ -62,3 +64,11 @@ data "aws_subnet" "dra_admin" { data "aws_subnet" "dra_analytics" { id = local.dra_analytics_subnet_id } + +data "aws_subnet" "ciphertrust_manager" { + id = local.ciphertrust_manager_subnet_id +} + +data "aws_subnet" "cte_ddc_agent" { + id = local.cte_ddc_agent_subnet_id +} diff --git a/examples/aws/poc/dsf_deployment/outputs.tf b/examples/aws/poc/dsf_deployment/outputs.tf index 5a761380b..d7ccb1660 100644 --- a/examples/aws/poc/dsf_deployment/outputs.tf +++ b/examples/aws/poc/dsf_deployment/outputs.tf @@ -121,6 +121,61 @@ output "dra" { } : null } +output "ciphertrust" { + value = var.enable_ciphertrust ? { + ciphertrust_manager = [ + for idx, val in module.ciphertrust_manager : { + private_ip = try(val.private_ip, null) + private_dns = try(val.private_dns, null) + public_ip = try(val.public_ip, null) + public_dns = try(val.public_dns, null) + public_url = try(join("", ["https://", val.public_dns]), null) + private_url = try(join("", ["https://", val.private_dns]), null) + display_name = try(val.display_name, null) + ssh_command = try("ssh -i ${local.private_key_file_path} ${val.ssh_user}@${val.public_dns}", null) + } + ] + } : null +} + +output "cte_ddc_agents" { + value = var.enable_ciphertrust ? { + cte_agents = [ + for val in concat(local.linux_cte_only_instances, local.windows_cte_only_instances) : + { + private_ip = module.cte_ddc_agents[val.id].private_ip + private_dns = module.cte_ddc_agents[val.id].private_dns + public_ip = module.cte_ddc_agents[val.id].public_ip + public_dns = module.cte_ddc_agents[val.id].public_dns + display_name = try(module.cte_ddc_agents[val.id].display_name, null) + ssh_command = try("ssh -i ${local.private_key_file_path} ${module.cte_ddc_agents[val.id].ssh_user}@${module.cte_ddc_agents[val.id].public_ip}", null) + } + ] + ddc_agents = [ + for val in concat(local.linux_ddc_only_instances, local.windows_ddc_only_instances) : + { + private_ip = module.cte_ddc_agents[val.id].private_ip + private_dns = module.cte_ddc_agents[val.id].private_dns + public_ip = module.cte_ddc_agents[val.id].public_ip + public_dns = module.cte_ddc_agents[val.id].public_dns + display_name = try(module.cte_ddc_agents[val.id].display_name, null) + ssh_command = try("ssh -i ${local.private_key_file_path} ${module.cte_ddc_agents[val.id].ssh_user}@${module.cte_ddc_agents[val.id].public_ip}", null) + } + ] + cte_ddc_windows_agents = [ + for val in concat(local.linux_cte_ddc_instances, local.windows_cte_ddc_instances) : + { + private_ip = module.cte_ddc_agents[val.id].private_ip + private_dns = module.cte_ddc_agents[val.id].private_dns + public_ip = module.cte_ddc_agents[val.id].public_ip + public_dns = module.cte_ddc_agents[val.id].public_dns + display_name = try(module.cte_ddc_agents[val.id].display_name, null) + ssh_command = try("ssh -i ${local.private_key_file_path} ${module.cte_ddc_agents[val.id].ssh_user}@${module.cte_ddc_agents[val.id].public_ip}", null) + } + ] + } : null +} + output "audit_sources" { value = { agent_sources = [ @@ -164,4 +219,13 @@ output "web_console_dam" { password = nonsensitive(local.password) user = module.mx[0].web_console_user }, null) +} + +output "web_console_ciphertrust" { + value = try({ + public_url = join("", ["https://", module.ciphertrust_manager[0].public_dns]) + private_url = join("", ["https://", module.ciphertrust_manager[0].private_dns]) + password = nonsensitive(local.ciphertrust_manager_password) + user = local.ciphertrust_manager_web_console_username + }, null) } \ No newline at end of file diff --git a/examples/aws/poc/dsf_deployment/sonar.tf b/examples/aws/poc/dsf_deployment/sonar.tf index 8cbaa6960..ab61cec0c 100644 --- a/examples/aws/poc/dsf_deployment/sonar.tf +++ b/examples/aws/poc/dsf_deployment/sonar.tf @@ -2,10 +2,14 @@ locals { tarball_location = var.tarball_location != null ? var.tarball_location : module.globals.tarball_location agentless_gw_count = var.enable_sonar ? var.agentless_gw_count : 0 + # Minimal sonar version that supports CipherTrust Manager is 4.18 + is_sonar_supports_cm_integration = !contains(["4.19", "4.18", "4.17", "4.16", "4.15", "4.14", "4.13", "4.12", "4.11", "4.10", "4.9"], module.globals.tarball_location.version) + hub_public_ip = var.enable_sonar ? (length(module.hub_main[0].public_ip) > 0 ? format("%s/32", module.hub_main[0].public_ip) : null) : null hub_dr_public_ip = var.enable_sonar && var.hub_hadr ? (length(module.hub_dr[0].public_ip) > 0 ? format("%s/32", module.hub_dr[0].public_ip) : null) : null hub_cidr_list = compact([data.aws_subnet.hub.cidr_block, data.aws_subnet.hub_dr.cidr_block, local.hub_public_ip, local.hub_dr_public_ip]) agentless_gw_cidr_list = [data.aws_subnet.agentless_gw.cidr_block, data.aws_subnet.agentless_gw_dr.cidr_block] + cte_agents_cidr_list = var.enable_ciphertrust && local.is_sonar_supports_cm_integration ? [data.aws_subnet.cte_ddc_agent.cidr_block] : [] } module "hub_main" { @@ -45,9 +49,23 @@ module "hub_main" { archiver_username = module.dra_analytics[0].archiver_user archiver_password = module.dra_analytics[0].archiver_password } : null + cm_details = var.enable_ciphertrust && local.is_sonar_supports_cm_integration ? { + name = "CipherTrust Manager" + is_load_balancer = false + hostname = coalesce(module.ciphertrust_manager[0].public_ip, module.ciphertrust_manager[0].private_ip) + port = 443 + ddc_enabled = true + ddc_connection_hostname = null + ddc_connection_port = null + username = local.ciphertrust_manager_web_console_username + password = local.ciphertrust_manager_password + registration_method = "password" + registration_token = null + } : null tags = local.tags depends_on = [ - module.vpc + module.vpc, + ciphertrust_trial_license.trial_license ] } @@ -120,6 +138,7 @@ module "agentless_gw_main" { } allowed_agentless_gw_cidrs = [data.aws_subnet.agentless_gw_dr.cidr_block] allowed_hub_cidrs = [data.aws_subnet.hub.cidr_block, data.aws_subnet.hub_dr.cidr_block] + allowed_cte_agents_cidrs = local.cte_agents_cidr_list allowed_all_cidrs = local.workstation_cidr allowed_ssh_cidrs = var.allowed_ssh_cidrs ingress_communication_via_proxy = { @@ -154,6 +173,7 @@ module "agentless_gw_dr" { } allowed_agentless_gw_cidrs = [data.aws_subnet.agentless_gw.cidr_block] allowed_hub_cidrs = [data.aws_subnet.hub.cidr_block, data.aws_subnet.hub_dr.cidr_block] + allowed_cte_agents_cidrs = local.cte_agents_cidr_list allowed_all_cidrs = local.workstation_cidr allowed_ssh_cidrs = var.allowed_ssh_cidrs ingress_communication_via_proxy = { diff --git a/examples/aws/poc/dsf_deployment/variables.tf b/examples/aws/poc/dsf_deployment/variables.tf index cc1dc727d..1cdb70da5 100644 --- a/examples/aws/poc/dsf_deployment/variables.tf +++ b/examples/aws/poc/dsf_deployment/variables.tf @@ -28,6 +28,12 @@ variable "enable_dra" { description = "Provision DRA Admin and Analytics" } +variable "enable_ciphertrust" { + type = bool + default = true + description = "Provision CipherTrust Manager" +} + variable "agentless_gw_count" { type = number default = 1 @@ -46,6 +52,12 @@ variable "dra_analytics_count" { description = "Number of DRA Analytics servers. Provisioning Analytics servers requires the enable_dra variable to be set to 'true'." } +variable "ciphertrust_manager_count" { + type = number + default = 2 # Minimum count for a cluster + description = "Number of CipherTrust Manager servers. If more than one server is specified, they will be configured as a cluster. Provisioning CipherTrust Manager servers requires the enable_ciphertrust variable to be set to 'true'." +} + variable "password" { sensitive = true type = string @@ -59,7 +71,7 @@ variable "password" { variable "web_console_cidr" { type = list(string) default = ["0.0.0.0/0"] - description = "DSF Hub, MX and DRA Admin web consoles IPs range. Specify IPs in the following format - [\"x.x.x.x/x\", \"y.y.y.y/y\"]. The default configuration opens the DSF Hub web console as a public website. It is recommended to specify a more restricted IP and CIDR range." + description = "DSF Hub, MX, DRA Admin and CipherTrust Manager web consoles IPs range. Specify IPs in the following format - [\"x.x.x.x/x\", \"y.y.y.y/y\"]. The default configuration opens the DSF Hub web console as a public website. It is recommended to specify a more restricted IP and CIDR range." } variable "workstation_cidr" { @@ -94,20 +106,22 @@ variable "public_subnets" { variable "subnet_ids" { type = object({ - hub_subnet_id = string - hub_dr_subnet_id = string - agentless_gw_subnet_id = string - agentless_gw_dr_subnet_id = string - mx_subnet_id = string - agent_gw_subnet_id = string - dra_admin_subnet_id = string - dra_analytics_subnet_id = string - db_subnet_ids = list(string) + hub_subnet_id = string + hub_dr_subnet_id = string + agentless_gw_subnet_id = string + agentless_gw_dr_subnet_id = string + mx_subnet_id = string + agent_gw_subnet_id = string + dra_admin_subnet_id = string + dra_analytics_subnet_id = string + ciphertrust_manager_subnet_id = string + cte_ddc_agent_subnet_id = string + db_subnet_ids = list(string) }) default = null description = "The IDs of existing subnets to deploy resources in. Keep empty if you wish to provision new VPC and subnets. db_subnet_ids can be an empty list only if no databases should be provisioned" validation { - condition = var.subnet_ids == null || try(var.subnet_ids.hub_subnet_id != null && var.subnet_ids.hub_dr_subnet_id != null && var.subnet_ids.agentless_gw_subnet_id != null && var.subnet_ids.agentless_gw_dr_subnet_id != null && var.subnet_ids.mx_subnet_id != null && var.subnet_ids.agent_gw_subnet_id != null && var.subnet_ids.dra_admin_subnet_id != null && var.subnet_ids.dra_analytics_subnet_id != null && var.subnet_ids.db_subnet_ids != null, false) + condition = var.subnet_ids == null || try(var.subnet_ids.hub_subnet_id != null && var.subnet_ids.hub_dr_subnet_id != null && var.subnet_ids.agentless_gw_subnet_id != null && var.subnet_ids.agentless_gw_dr_subnet_id != null && var.subnet_ids.mx_subnet_id != null && var.subnet_ids.agent_gw_subnet_id != null && var.subnet_ids.dra_admin_subnet_id != null && var.subnet_ids.dra_analytics_subnet_id != null && var.subnet_ids.ciphertrust_manager_subnet_id != null && var.subnet_ids.cte_ddc_agent_subnet_id != null && var.subnet_ids.db_subnet_ids != null, false) error_message = "Value must either be null or specified for all" } validation { @@ -338,3 +352,116 @@ variable "dra_analytics_ebs_details" { volume_type = "gp3" } } + +############################### +#### CipherTrust variables #### +############################### + +variable "ciphertrust_manager_ebs_details" { + type = object({ + volume_size = number + volume_type = string + }) + description = "CipherTrust Manager compute instance volume attributes" + default = { + volume_size = 256 + volume_type = "gp2" + } +} + +variable "ciphertrust_manager_password" { + sensitive = true + type = string + default = null # Random + description = "Ciphertrust manager web console password" + validation { + condition = var.ciphertrust_manager_password == null || try(length(var.ciphertrust_manager_password) >= 8 && length(var.ciphertrust_manager_password) <= 30, false) + error_message = "Password must be between 8 and 30 characters" + } + + validation { + condition = var.ciphertrust_manager_password == null || can(regex("[A-Z]+", var.ciphertrust_manager_password)) + error_message = "Password must include at least 1 upper-case letter.\n" + } + + validation { + condition = var.ciphertrust_manager_password == null || can(regex("[a-z]+", var.ciphertrust_manager_password)) + error_message = "Password must include at least 1 lower-case letter.\n" + } + + validation { + condition = var.ciphertrust_manager_password == null || can(regex("[0-9]+", var.ciphertrust_manager_password)) + error_message = "Password must include at least 1 decimal digit.\n" + } + + validation { + condition = var.ciphertrust_manager_password == null || can(regex("[!@#$%^&*(),.?\":{}|<>]+", var.ciphertrust_manager_password)) + error_message = "Password must include at least 1 special character.\n" + } +} + +variable "ciphertrust_manager_ami_id" { + type = string + description = "Ciphertrust Manager AMI id. If set to null, the latest AMI will be taken from AWS marketplace" + default = null +} + +variable "cte_agent_linux_installation_file" { + type = string + description = "Path to the CTE agent linux installation file" + default = null +} + +variable "ddc_agent_linux_installation_file" { + type = string + description = "Path to the DDC agent linux installation file" + default = null +} + +variable "cte_agent_windows_installation_file" { + type = string + description = "Path to the CTE agent windows installation file" + default = null +} + +variable "ddc_agent_windows_installation_file" { + type = string + description = "Path to the DDC agent windows installation file" + default = null +} + +variable "cte_ddc_agents_linux_count" { + type = number + default = 0 + description = "Number of CTE-DDC agent linux servers. Provisioning CTE-DDC agent servers requires the enable_ciphertrust variable to be set to 'true'." +} + +variable "cte_agents_linux_count" { + type = number + default = 0 + description = "Number of CTE agent linux servers. Provisioning CTE-DDC agent servers requires the enable_ciphertrust variable to be set to 'true'." +} + +variable "ddc_agents_linux_count" { + type = number + default = 0 + description = "Number of DDC agent linux servers. Provisioning CTE-DDC agent servers requires the enable_ciphertrust variable to be set to 'true'." +} + +variable "cte_ddc_agents_windows_count" { + type = number + default = 0 + description = "Number of CTE-DDC agent windows servers. Provisioning CTE-DDC agent servers requires the enable_ciphertrust variable to be set to 'true'." +} + +variable "cte_agents_windows_count" { + type = number + default = 0 + description = "Number of CTE agent windows servers. Provisioning CTE-DDC agent servers requires the enable_ciphertrust variable to be set to 'true'." +} + +variable "ddc_agents_windows_count" { + type = number + default = 0 + description = "Number of DDC agent windows servers. Provisioning CTE-DDC agent servers requires the enable_ciphertrust variable to be set to 'true'." +} \ No newline at end of file diff --git a/examples/aws/poc/dsf_deployment/versions.tf b/examples/aws/poc/dsf_deployment/versions.tf index 8ed85317d..f9892b80e 100644 --- a/examples/aws/poc/dsf_deployment/versions.tf +++ b/examples/aws/poc/dsf_deployment/versions.tf @@ -6,6 +6,11 @@ terraform { source = "hashicorp/aws" version = ">= 4.23.0" } + ciphertrust = { + source = "ThalesGroup/ciphertrust" +# version = "1.0.0-pre3" + version = "~> 0.11.1" + } local = { version = "~> 2.1" } diff --git a/modules/aws/agentless-gw/main.tf b/modules/aws/agentless-gw/main.tf index 191ba93a2..f14aca0d1 100644 --- a/modules/aws/agentless-gw/main.tf +++ b/modules/aws/agentless-gw/main.tf @@ -20,6 +20,13 @@ locals { udp = [] tcp = [3030, 27117, 22] cidrs = concat(var.allowed_agentless_gw_cidrs, var.allowed_all_cidrs) + }, + { + name = ["cte", "agents"] + internet_access = false + udp = [] + tcp = [11570, 10570] # syslog TLS port 11570, TCP is 10570 + cidrs = concat(var.allowed_cte_agents_cidrs, var.allowed_all_cidrs) } ] } diff --git a/modules/aws/agentless-gw/variables.tf b/modules/aws/agentless-gw/variables.tf index 4d94f42d7..570dd85b8 100644 --- a/modules/aws/agentless-gw/variables.tf +++ b/modules/aws/agentless-gw/variables.tf @@ -53,6 +53,16 @@ variable "allowed_hub_cidrs" { default = [] } +variable "allowed_cte_agents_cidrs" { + type = list(string) + description = "List of ingress CIDR patterns allowing CTE agents to access the Agentless Gateway instance" + validation { + condition = alltrue([for item in var.allowed_cte_agents_cidrs : can(cidrnetmask(item))]) + error_message = "Each item of this list must be in a valid CIDR block format. For example: [\"10.106.108.0/25\"]" + } + default = [] +} + variable "allowed_ssh_cidrs" { type = list(string) description = "List of ingress CIDR patterns allowing ssh access" diff --git a/modules/aws/ciphertrust-manager/README.md b/modules/aws/ciphertrust-manager/README.md new file mode 100644 index 000000000..f313430db --- /dev/null +++ b/modules/aws/ciphertrust-manager/README.md @@ -0,0 +1,104 @@ +# DSF CipherTrust Manager +[![GitHub tag](https://img.shields.io/github/v/tag/imperva/dsfkit.svg)](https://github.com/imperva/dsfkit/tags) + +This Terraform module provisions a CipherTrust Manager on AWS as an EC2 instance. + +## CipherTrust Manager Versions +2.19 and up + +## Requirements +* Terraform — refer to [versions.tf](https://github.com/imperva/dsfkit/blob/master/modules/aws/ciphertrust-manager/versions.tf) for supported versions. +* An AWS account. +* Access to the CipherTrust AMI from AWS Marketplace (product code: `a5j8w8j2tn9crtnai795fkf6o`). + +**NOTE:** For CipherTrust licensing or access questions, contact your Thales representative. + +## Resources Provisioned +This Terraform module provisions several resources on AWS to create the CipherTrust Manager instance. These resources include: +* An EC2 instance running the CipherTrust Manager software. +* An EBS volume for storage. +* A network interface attached to the specified subnet and security groups. +* Optional Elastic IP and EIP association if `attach_persistent_public_ip` is enabled. +* A security group (if not provided) to allow the required network access to and from the CipherTrust Manager instance. + +The EC2 instance and EBS volume provide the computing and storage resources needed to run the CipherTrust Manager software. The security group controls the inbound and outbound traffic to the instance. + +## Inputs + +The following input variables are **required**: + +* `subnet_id`: The subnet ID to attach the CipherTrust instance to. +* `key_pair`: Name of the AWS EC2 key pair used for SSH access. +* `ebs`: AWS EBS details. + +Additionally, the following variables are often **required unless defaults suffice**: + +* `allowed_web_console_and_api_cidrs`: CIDRs for web console and API access (ports 443, 80). +* `allowed_ssh_cidrs`: CIDRs allowed to SSH into the instance (port 22). +* `allowed_cluster_nodes_cidrs`: CIDRs for cluster communication (port 5432). +* `allowed_ddc_agents_cidrs`: CIDRs for DDC agent access (port 11117). +* `allowed_all_cidrs`: Additional CIDRs applied to all types of access (optional). +* `ami`: Optional override for selecting a specific AMI using filters or ID. +* `instance_type`: EC2 instance type (default: `t2.xlarge`). +* `attach_persistent_public_ip`: Whether to allocate and attach an Elastic IP (default: `false`). + +Refer to [inputs](https://registry.terraform.io/modules/imperva/dsf-ciphertrust-manager/aws/latest?tab=inputs) for additional variables with default values and additional info. + +## Outputs + +Refer to [outputs](https://registry.terraform.io/modules/imperva/dsf-ciphertrust-manager/aws/latest?tab=outputs). + +## Usage + +To utilize this module with a minimal configuration, include the following in your Terraform setup: + +```hcl +provider "aws" {} + +module "dsf_ciphertrust_manager" { + source = "imperva/dsf-ciphertrust-manager/aws" + + subnet_id = "subnet-xxxxxxxxxxxxxxx" + key_pair = "my-keypair-name" + + ebs = { + volume_size = 256 + volume_type = "gp2" + } + + allowed_web_console_and_api_cidrs = ["10.0.0.0/24"] + allowed_ssh_cidrs = ["10.0.0.0/24"] + allowed_cluster_nodes_cidrs = ["10.0.1.0/24"] + allowed_ddc_agents_cidrs = ["10.0.2.0/24"] +} +``` + +To see a complete example of how to use this module in a DSF deployment with other modules, check out the [examples](https://github.com/imperva/dsfkit/tree/master/examples/aws) directory. + +We recommend using a specific version of the module (and not the latest). +See available released versions in the main repo README [here](https://github.com/imperva/dsfkit#version-history). + +Specify the module's version by adding the version parameter. For example: + +``` +module "dsf_ciphertrust_manager" { + source = "imperva/dsf-ciphertrust-manager/aws" + version = "x.y.z" + + # The rest of arguments are omitted for brevity +} +``` + +## CipherTrust Manager High Availability + +To ensure high availability and disaster recovery, deploying multiple CipherTrust Manager instances. + +To finalize the cluster nodes setup, refer to the dsf-ciphertrust-manager-cluster-setup Terraform module [here](https://registry.terraform.io/modules/imperva/ciphertrust-manager-cluster-setup/null/latest) + +## Additional Information + +For more information about the CipherTrust Manager and its features, refer to the official documentation [here](https://thalesdocs.com/ctp/cm/2.19/). + +For additional information about DSF deployment using terraform, refer to the main repo README [here](https://github.com/imperva/dsfkit/tree/1.7.29). + + diff --git a/modules/aws/ciphertrust-manager/ami.tf b/modules/aws/ciphertrust-manager/ami.tf new file mode 100644 index 000000000..c6c8232b5 --- /dev/null +++ b/modules/aws/ciphertrust-manager/ami.tf @@ -0,0 +1,39 @@ +locals { + ami_default = { + id = null + owner_account_id = "679593333241" // aws marketplace + name_regex = "k170v-2.19.*" + product_code = "a5j8w8j2tn9crtnai795fkf6o" + } + + ami = var.ami != null ? var.ami : local.ami_default + + ami_owner = local.ami.owner_account_id != null ? local.ami.owner_account_id : "self" + ami_name_regex = local.ami.name_regex != null ? local.ami.name_regex : ".*" + ami_product_code = local.ami.product_code != null ? local.ami.product_code : "*" + + ami_id = local.ami.id != null ? local.ami.id : data.aws_ami.selected-ami[0].image_id +} + +data "aws_ami" "selected-ami" { + count = local.ami.id == null ? 1 : 0 + most_recent = true + name_regex = local.ami_name_regex + + filter { + name = "product-code" + values = [local.ami_product_code] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } + + filter { + name = "architecture" + values = ["x86_64"] + } + + owners = [local.ami_owner] +} \ No newline at end of file diff --git a/modules/aws/ciphertrust-manager/main.tf b/modules/aws/ciphertrust-manager/main.tf new file mode 100644 index 000000000..ccfb9c6e6 --- /dev/null +++ b/modules/aws/ciphertrust-manager/main.tf @@ -0,0 +1,55 @@ +locals { + web_console_username = "admin" + + security_group_ids = concat( + [for sg in aws_security_group.sg : sg.id], + var.security_group_ids) + + public_ip = var.attach_persistent_public_ip ? aws_eip.dsf_instance_eip[0].public_ip : aws_instance.cipthertrust_manager_instance.public_ip + public_dns = var.attach_persistent_public_ip ? aws_eip.dsf_instance_eip[0].public_dns : aws_instance.cipthertrust_manager_instance.public_dns + private_ip = length(aws_network_interface.eni.private_ips) > 0 ? tolist(aws_network_interface.eni.private_ips)[0] : null +} + +resource "aws_eip" "dsf_instance_eip" { + count = var.attach_persistent_public_ip ? 1 : 0 + domain = "vpc" + tags = merge(var.tags, { Name = var.friendly_name }) +} + +resource "aws_eip_association" "eip_assoc" { + count = var.attach_persistent_public_ip ? 1 : 0 + instance_id = aws_instance.cipthertrust_manager_instance.id + allocation_id = aws_eip.dsf_instance_eip[0].id +} + +resource "aws_instance" "cipthertrust_manager_instance" { + ami = local.ami_id + instance_type = var.instance_type + key_name = var.key_pair + root_block_device { + volume_size = var.ebs.volume_size + volume_type = var.ebs.volume_type + delete_on_termination = true + } + network_interface { + network_interface_id = aws_network_interface.eni.id + device_index = 0 + } + disable_api_termination = true + metadata_options { + http_endpoint = "enabled" + http_tokens = "required" + } + tags = merge(var.tags, { Name : var.friendly_name }) + volume_tags = merge(var.tags, { Name : var.friendly_name }) + + lifecycle { + ignore_changes = [ami] + } +} + +resource "aws_network_interface" "eni" { + subnet_id = var.subnet_id + security_groups = local.security_group_ids + tags = var.tags +} \ No newline at end of file diff --git a/modules/aws/ciphertrust-manager/outputs.tf b/modules/aws/ciphertrust-manager/outputs.tf new file mode 100644 index 000000000..0f10cee16 --- /dev/null +++ b/modules/aws/ciphertrust-manager/outputs.tf @@ -0,0 +1,35 @@ +output "public_ip" { + description = "Public elastic IP address of the CipherTrust Manager instance" + value = local.public_ip +} + +output "private_ip" { + description = "Private IP address of the CipherTrust Manager instance" + value = local.private_ip +} + +output "public_dns" { + description = "Public DNS of the elastic IP address of the CipherTrust Manager instance" + value = local.public_dns +} + +output "private_dns" { + description = "Private DNS of the IP address of the CipherTrust Manager instance" + value = aws_network_interface.eni.private_dns_name +} + +output "instance_id" { + value = aws_instance.cipthertrust_manager_instance.id +} + +output "display_name" { + value = aws_instance.cipthertrust_manager_instance.tags.Name +} + +output "ssh_user" { + value = var.ssh_user +} + +output "web_console_user" { + value = local.web_console_username +} \ No newline at end of file diff --git a/modules/aws/ciphertrust-manager/sg.tf b/modules/aws/ciphertrust-manager/sg.tf new file mode 100644 index 000000000..7d91ce372 --- /dev/null +++ b/modules/aws/ciphertrust-manager/sg.tf @@ -0,0 +1,88 @@ +locals { + # Skip sg creation if external sg list is given + _security_groups_config = length(var.security_group_ids) == 0 ? local.security_groups_config : [] + + security_groups_config = [ // https://thalesdocs.com/ctp/cm/2.19/get_started/deployment/hardening-guidelines/index.html + { + name = ["web", "console", "and", "api"] + internet_access = false + udp = [] + tcp = [443, 80] + cidrs = concat(var.allowed_web_console_and_api_cidrs, var.allowed_all_cidrs) + }, + { + name = ["ssh"] + internet_access = true + udp = [] + tcp = [22] + cidrs = concat(var.allowed_ssh_cidrs, var.allowed_all_cidrs) + }, + { + name = ["cluster", "nodes"] + internet_access = false + udp = [] + tcp = [5432] + cidrs = concat(var.allowed_cluster_nodes_cidrs, var.allowed_all_cidrs) + }, + { + name = ["ddc", "agents"] + internet_access = false + udp = [] + tcp = [11117] + cidrs = concat(var.allowed_ddc_agents_cidrs, var.allowed_all_cidrs) + }, + { + name = ["other"] + internet_access = false + udp = [] + tcp = [5696, 9000] + cidrs = concat(var.allowed_all_cidrs) + } + ] +} + +data "aws_subnet" "subnet" { + id = var.subnet_id +} + +############################################################################## +### Ingress security group +############################################################################## + +resource "aws_security_group" "sg" { + for_each = { for idx, config in local._security_groups_config : idx => config } + name = join("-", [var.friendly_name, join("-", each.value.name)]) + vpc_id = data.aws_subnet.subnet.vpc_id + description = format("%s - %s ingress access", var.friendly_name, join(" ", each.value.name)) + + dynamic "ingress" { + for_each = { for idx, port in each.value.tcp : idx => port } + content { + from_port = ingress.value + to_port = ingress.value + protocol = "tcp" + cidr_blocks = each.value.cidrs + } + } + + dynamic "ingress" { + for_each = { for idx, port in each.value.udp : idx => port } + content { + from_port = ingress.value + to_port = ingress.value + protocol = "udp" + cidr_blocks = each.value.cidrs + } + } + + # Conditionally assign egress rules based on a "internet_access" memeber + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = each.value.internet_access ? ["0.0.0.0/0"] : [] + ipv6_cidr_blocks = each.value.internet_access ? ["::/0"] : [] + } + + tags = merge(var.tags, { Name = join("-", [var.friendly_name, join("-", each.value.name)]) }) +} \ No newline at end of file diff --git a/modules/aws/ciphertrust-manager/variables.tf b/modules/aws/ciphertrust-manager/variables.tf new file mode 100644 index 000000000..011cd3d54 --- /dev/null +++ b/modules/aws/ciphertrust-manager/variables.tf @@ -0,0 +1,140 @@ +variable "tags" { + description = "A map of tags to add to all resources" + type = map(string) + default = {} +} + +variable "friendly_name" { + type = string + description = "Friendly name to identify all resources" + default = "imperva-dsf-ciphertrust-manager" + validation { + condition = length(var.friendly_name) >= 3 + error_message = "Must be at least 3 characters long" + } + validation { + condition = can(regex("^\\p{L}.*", var.friendly_name)) + error_message = "Must start with a letter" + } +} + +variable "subnet_id" { + type = string + description = "Subnet id for the CipherTrust Manager instances" + validation { + condition = length(var.subnet_id) >= 15 && substr(var.subnet_id, 0, 7) == "subnet-" + error_message = "Subnet id is invalid. Must be subnet-********" + } +} + +variable "security_group_ids" { + type = list(string) + description = "AWS security group Ids to attach to the instance. If provided, no security groups are created and all allowed_*_cidrs variables are ignored." + validation { + condition = alltrue([for item in var.security_group_ids : substr(item, 0, 3) == "sg-"]) + error_message = "One or more of the security group Ids list is invalid. Each item should be in the format of 'sg-xx..xxx'" + } + default = [] +} + +variable "allowed_web_console_and_api_cidrs" { + type = list(string) + description = "List of ingress CIDR patterns allowing web console and api access" + validation { + condition = alltrue([for item in var.allowed_web_console_and_api_cidrs : can(cidrnetmask(item))]) + error_message = "Each item of this list must be in a valid CIDR block format. For example: [\"10.106.108.0/25\"]" + } + default = [] +} + +variable "allowed_ssh_cidrs" { + type = list(string) + description = "List of ingress CIDR patterns allowing ssh access" + validation { + condition = alltrue([for item in var.allowed_ssh_cidrs : can(cidrnetmask(item))]) + error_message = "Each item of this list must be in a valid CIDR block format. For example: [\"10.106.108.0/25\"]" + } + default = [] +} + +variable "allowed_cluster_nodes_cidrs" { + type = list(string) + description = "List of ingress CIDR patterns allowing other CipherTrust Manager cluster nodes to access the CipherTrust Manager instance" + validation { + condition = alltrue([for item in var.allowed_cluster_nodes_cidrs : can(cidrnetmask(item))]) + error_message = "Each item of this list must be in a valid CIDR block format. For example: [\"10.106.108.0/25\"]" + } + default = [] +} + +variable "allowed_ddc_agents_cidrs" { + type = list(string) + description = "List of ingress CIDR patterns allowing DDC agents to access the CipherTrust Manager instance" + validation { + condition = alltrue([for item in var.allowed_ddc_agents_cidrs : can(cidrnetmask(item))]) + error_message = "Each item of this list must be in a valid CIDR block format. For example: [\"10.106.108.0/25\"]" + } + default = [] +} + +variable "allowed_all_cidrs" { + type = list(string) + description = "List of ingress CIDR patterns allowing all types of access: ssh, API, web console, etc." + validation { + condition = alltrue([for item in var.allowed_all_cidrs : can(cidrnetmask(item))]) + error_message = "Each item of this list must be in a valid CIDR block format. For example: [\"10.106.108.0/25\"]" + } + default = [] +} + +variable "instance_type" { + type = string + default = "t2.xlarge" + description = "EC2 instance type for the CipherTrust Manager" +} + +variable "ebs" { + type = object({ + volume_size = number + volume_type = string + }) + description = "Compute instance volume attributes for the CipherTrust Manager" +} + +variable "attach_persistent_public_ip" { + type = bool + default = false + description = "Create public elastic IP for the instance" +} + +variable "key_pair" { + type = string + description = "Key pair for the CipherTrust Manager instance" +} + +variable "ssh_user" { + type = string + default = "ksadmin" +} + +variable "ami" { + type = object({ + id = string + name_regex = string + product_code = string + owner_account_id = string + }) + description = < 0 ? tolist(aws_network_interface.eni.private_ips)[0] : null + instance_address = var.use_public_ip ? local.public_ip : local.private_ip + + security_group_ids = concat( + [for sg in aws_security_group.dsf_agent_sg : sg.id], + var.security_group_ids) + + # Determine the values based on the OS type + ami_id = var.os_type == "Windows" ? data.aws_ami.agent_ami_windows[0].id : data.aws_ami.agent_ami_linux[0].id + user_data = var.os_type == "Windows" ? local.user_data_windows : null + reboot_commands = var.os_type == "Windows" ? local.reboot_inline_commands_windows : local.reboot_inline_commands_linux + ddc_agent_inline_commands = var.os_type == "Windows" ? local.ddc_agent_inline_commands_windows : local.ddc_agent_inline_commands_linux + cte_agent_inline_commands = var.os_type == "Windows" ? local.cte_agent_inline_commands_windows : local.cte_agent_inline_commands_linux + target_platform = var.os_type == "Windows" ? "windows" : null + + dummy_file_path = "${path.module}/dummy.txt" +} + +resource "aws_eip" "dsf_instance_eip" { + count = var.attach_persistent_public_ip ? 1 : 0 + domain = "vpc" + tags = merge(var.tags, { Name = var.friendly_name }) +} + +resource "aws_eip_association" "eip_assoc" { + count = var.attach_persistent_public_ip ? 1 : 0 + instance_id = aws_instance.cte_ddc_agent.id + allocation_id = aws_eip.dsf_instance_eip[0].id +} + +resource "aws_network_interface" "eni" { + subnet_id = var.subnet_id + security_groups = local.security_group_ids + tags = var.tags +} + +resource "aws_instance" "cte_ddc_agent" { + ami = local.ami_id + instance_type = var.instance_type + key_name = var.ssh_key_pair.ssh_public_key_name + user_data = local.user_data + network_interface { + network_interface_id = aws_network_interface.eni.id + device_index = 0 + } + metadata_options { + http_endpoint = "enabled" + http_tokens = "required" + } + tags = merge(var.tags, { Name : var.friendly_name }) + volume_tags = merge(var.tags, { Name : var.friendly_name }) + depends_on = [aws_eip.dsf_instance_eip] + + lifecycle { + ignore_changes = [ami] + } +} + +resource "null_resource" "cte_ddc_copy_file" { + provisioner "file" { + source = var.agent_installation.install_cte ? var.agent_installation.cte_agent_installation_file : local.dummy_file_path + destination = basename(var.agent_installation.install_cte ? var.agent_installation.cte_agent_installation_file : local.dummy_file_path) + } + + provisioner "file" { + content = var.agent_installation.install_cte ? templatefile("${path.module}/${local.reg_params_template_name}", { + server_hostname = var.cipher_trust_manager_address + registration_token = var.agent_installation.registration_token + enable_ldt = local.enable_ldt + enable_fam = local.enable_fam + }) : "" + destination = var.agent_installation.install_cte ? local.reg_params_template_name : basename(local.dummy_file_path) + } + + provisioner "remote-exec" { + inline = var.agent_installation.install_cte ? local.cte_agent_inline_commands : ["echo 'No CTE agent installation required'"] + } + + provisioner "file" { + source = var.agent_installation.install_ddc ? var.agent_installation.ddc_agent_installation_file : local.dummy_file_path + destination = basename(var.agent_installation.install_ddc ? var.agent_installation.ddc_agent_installation_file : local.dummy_file_path) + } + + provisioner "remote-exec" { + inline = var.agent_installation.install_ddc ? local.ddc_agent_inline_commands : ["echo 'No DDC agent installation required'"] + } + + # reboot the host to activate the FAM feature + provisioner "remote-exec" { + inline = local.reboot_commands + } + + connection { + type = "ssh" + user = local.agent_ami_ssh_user + private_key = file(var.ssh_key_pair.ssh_private_key_file_path) + host = local.instance_address + target_platform = local.target_platform + + bastion_host = local.bastion_host + bastion_private_key = local.bastion_private_key + bastion_user = local.bastion_user + + script_path = local.script_path + } + depends_on = [aws_instance.cte_ddc_agent, aws_eip_association.eip_assoc] +} diff --git a/modules/aws/cte-ddc-agent/outputs.tf b/modules/aws/cte-ddc-agent/outputs.tf new file mode 100644 index 000000000..09f78d0ad --- /dev/null +++ b/modules/aws/cte-ddc-agent/outputs.tf @@ -0,0 +1,31 @@ +output "public_ip" { + description = "Public elastic IP address of the agent instance" + value = local.public_ip +} + +output "private_ip" { + description = "Private IP address of the agent instance" + value = local.private_ip +} + +output "public_dns" { + description = "Public DNS of the elastic IP address of the agent instance" + value = local.public_dns +} + +output "private_dns" { + description = "Private DNS of the IP address of the agent instance" + value = aws_network_interface.eni.private_dns_name +} + +output "instance_id" { + value = aws_instance.cte_ddc_agent.id +} + +output "display_name" { + value = aws_instance.cte_ddc_agent.tags.Name +} + +output "ssh_user" { + value = local.agent_ami_ssh_user +} diff --git a/modules/aws/cte-ddc-agent/sg.tf b/modules/aws/cte-ddc-agent/sg.tf new file mode 100644 index 000000000..5a2bca8cf --- /dev/null +++ b/modules/aws/cte-ddc-agent/sg.tf @@ -0,0 +1,75 @@ +locals { + # Skip sg creation if external sg list is given + _security_groups_config = length(var.security_group_ids) == 0 ? local.security_groups_config : [] + + security_groups_config = var.os_type == "Windows" ? [ + { + name = ["ssh"] + internet_access = true + udp = [] + tcp = [22] + cidrs = concat(var.allowed_ssh_cidrs) + }, + { + name = ["rdp"] + internet_access = false + udp = [] + tcp = [3389] + cidrs = concat(var.allowed_rdp_cidrs) + } + ] : [ + { + name = ["ssh"] + internet_access = true + udp = [] + tcp = [22] + cidrs = concat(var.allowed_ssh_cidrs) + } + ] +} + +data "aws_subnet" "subnet" { + id = var.subnet_id +} + +############################################################################## +### Ingress security group +############################################################################## + +resource "aws_security_group" "dsf_agent_sg" { + for_each = { for idx, config in local._security_groups_config : idx => config } + name = join("-", [var.friendly_name, join("-", each.value.name)]) + vpc_id = data.aws_subnet.subnet.vpc_id + description = format("%s - %s ingress access", var.friendly_name, join(" ", each.value.name)) + + dynamic "ingress" { + for_each = { for idx, port in each.value.tcp : idx => port } + content { + from_port = ingress.value + to_port = ingress.value + protocol = "tcp" + cidr_blocks = each.value.cidrs + } + } + + dynamic "ingress" { + for_each = { for idx, port in each.value.udp : idx => port } + content { + from_port = ingress.value + to_port = ingress.value + protocol = "udp" + cidr_blocks = each.value.cidrs + } + } + + # Conditionally assign egress rules based on a "internet_access" memeber + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = each.value.internet_access ? ["0.0.0.0/0"] : [] + ipv6_cidr_blocks = each.value.internet_access ? ["::/0"] : [] + } + + tags = merge(var.tags, { Name = join("-", [var.friendly_name, join("-", each.value.name)]) }) +} \ No newline at end of file diff --git a/modules/aws/cte-ddc-agent/variables.tf b/modules/aws/cte-ddc-agent/variables.tf new file mode 100644 index 000000000..2d388df9d --- /dev/null +++ b/modules/aws/cte-ddc-agent/variables.tf @@ -0,0 +1,147 @@ +variable "tags" { + description = "A map of tags to add to all resources" + type = map(string) + default = {} +} + +variable "friendly_name" { + type = string + description = "Friendly name to identify all resources" + default = "imperva-dsf-cte-ddc-agent" + validation { + condition = length(var.friendly_name) >= 3 + error_message = "Must be at least 3 characters long" + } + validation { + condition = can(regex("^\\p{L}.*", var.friendly_name)) + error_message = "Must start with a letter" + } +} + +variable "subnet_id" { + type = string + description = "Subnet id for the DSF MX instance" + validation { + condition = length(var.subnet_id) >= 15 && substr(var.subnet_id, 0, 7) == "subnet-" + error_message = "Subnet id is invalid. Must be subnet-********" + } +} + +variable "security_group_ids" { + type = list(string) + description = "AWS security group Ids to attach to the instance. If provided, no security groups are created and all allowed_*_cidrs variables are ignored." + validation { + condition = alltrue([for item in var.security_group_ids : substr(item, 0, 3) == "sg-"]) + error_message = "One or more of the security group Ids list is invalid. Each item should be in the format of 'sg-xx..xxx'" + } + default = [] +} + +variable "attach_persistent_public_ip" { + type = bool + default = false + description = "Create and attach elastic public IP for the instance" +} + +variable "allowed_ssh_cidrs" { + type = list(string) + description = "List of allowed ingress CIDR patterns allowing ssh protocols to the ec2 instance" + default = [] +} + +variable "allowed_rdp_cidrs" { + type = list(string) + description = "List of allowed ingress CIDR patterns allowing rdp protocols to the ec2 instance" + default = [] +} + +variable "ssh_key_pair" { + type = object({ + ssh_public_key_name = string + ssh_private_key_file_path = string + }) + description = "SSH materials to access machine" + + nullable = false +} + +variable "cipher_trust_manager_address" { + type = string + description = "CipherTrust Manager address to register to" + nullable = false +} + +variable "os_type" { + type = string + description = "Os type to provision as EC2, available types are: ['Red Hat', 'Windows']" + nullable = false + validation { + condition = var.os_type == null || try(contains(["Red Hat", "Windows"], var.os_type), false) + error_message = "Valid values should contain at least one of the following: 'Red Hat', 'Windows']" + } +} + +variable "agent_installation" { + type = object({ + registration_token = string + install_cte = bool + install_ddc = bool + cte_agent_installation_file = string + ddc_agent_installation_file = string + }) + description = "Agent installation files to use for the agent installation and registration token for the CTE agent. The files should be accessible from the machine where Terraform is running." + nullable = false + validation { + condition = var.agent_installation.install_cte || var.agent_installation.install_ddc + error_message = "At least one of install_cte or install_ddc must be true" + } + validation { + condition = var.agent_installation.install_cte == false || var.agent_installation.cte_agent_installation_file != null + error_message = "CTE agent installation file must be provided if install_cte is true" + } + validation { + condition = var.agent_installation.install_ddc == false || var.agent_installation.ddc_agent_installation_file != null + error_message = "DDC agent installation file must be provided if install_ddc is true" + } + validation { + condition = var.agent_installation.cte_agent_installation_file == null || try(fileexists(var.agent_installation.cte_agent_installation_file), false) + error_message = "CTE agent installation file does not exist at the specified path." + } + validation { + condition = var.agent_installation.ddc_agent_installation_file == null || try(fileexists(var.agent_installation.ddc_agent_installation_file), false) + error_message = "DDC agent installation file does not exist at the specified path" + } +} + +variable "instance_type" { + type = string + description = "Instance type to use for the agent instances" + default = "t2.large" + nullable = false +} + +variable "use_public_ip" { + type = bool + default = false + description = "Whether to use the agent instance's public or private IP for ssh access" +} + +variable "ingress_communication_via_proxy" { + type = object({ + proxy_address = string + proxy_private_ssh_key_path = string + proxy_ssh_user = string + }) + description = "Proxy address used for ssh for private CTE-DDC agent (Usually hub address), Proxy ssh key file path and Proxy ssh user. Keep empty if no proxy is in use" + default = null +} + +variable "terraform_script_path_folder" { + type = string + description = "Terraform script path folder to create terraform temporary script files on the CTE-DDC agent instance. Use '.' to represent the instance home directory" + default = null + validation { + condition = var.terraform_script_path_folder != "" + error_message = "Terraform script path folder cannot be an empty string" + } +} \ No newline at end of file diff --git a/modules/aws/cte-ddc-agent/versions.tf b/modules/aws/cte-ddc-agent/versions.tf new file mode 100644 index 000000000..272db1955 --- /dev/null +++ b/modules/aws/cte-ddc-agent/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.3.1, < 1.8.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.23.0" + } + } +} diff --git a/modules/aws/cte-ddc-agent/windows.tf b/modules/aws/cte-ddc-agent/windows.tf new file mode 100644 index 000000000..bffae7ef0 --- /dev/null +++ b/modules/aws/cte-ddc-agent/windows.tf @@ -0,0 +1,73 @@ +locals { + user_data_windows = <<-EOF + + # Install OpenSSH Server + Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 + + # Start and configure OpenSSH Server + Start-Service sshd + Set-Service -Name sshd -StartupType 'Automatic' + + # Configure firewall to allow SSH + if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue | Select-Object Name, Enabled)) { + Write-Output "Firewall Rule 'OpenSSH-Server-In-TCP' does not exist, creating it..." + New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 + } else { + Write-Output "Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists." + } + + # Create .ssh directory for Administrator + $adminSshDir = "$env:SystemDrive\\Users\\Administrator\\.ssh" + New-Item -ItemType Directory -Force -Path $adminSshDir + + # Retrieve the SSH public key from instance metadata + $token = Invoke-RestMethod -Uri "http://169.254.169.254/latest/api/token" -Method PUT -Headers @{ "X-aws-ec2-metadata-token-ttl-seconds" = "21600" } + $instanceMetadataUri = "http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key" + $authorizedKey = Invoke-RestMethod -Uri $instanceMetadataUri -Method Get -Headers @{ "X-aws-ec2-metadata-token" = $token } + + # Add the SSH key to administrators_authorized_keys + $authorizedKeysPath = "$env:ProgramData\\ssh\\administrators_authorized_keys" + Add-Content -Force -Path $authorizedKeysPath -Value $authorizedKey + icacls $authorizedKeysPath /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F" + + # Restart SSH service to apply changes + Restart-Service sshd + + EOF + + cte_agent_install_command_windows_msi = ( + var.agent_installation.cte_agent_installation_file != null + ? + "msiexec.exe /i \"${basename(var.agent_installation.cte_agent_installation_file)}\" /qn /norestart registerhostopts=\"${var.cipher_trust_manager_address} -fam -token=${var.agent_installation.registration_token}\"" + : "" + ) + cte_agent_install_command_windows_exe = ( + var.agent_installation.cte_agent_installation_file != null + ? + "${basename(var.agent_installation.cte_agent_installation_file)} /s /v\" /qn /norestart registerhostopts=\\\"${var.cipher_trust_manager_address} -fam -token=${var.agent_installation.registration_token}\\\"\"" + : "" + ) + cte_agent_install_command_windows = ( + var.agent_installation.cte_agent_installation_file != null + ? ( + can(regex(".*\\.exe$", var.agent_installation.cte_agent_installation_file)) + ? local.cte_agent_install_command_windows_exe + : local.cte_agent_install_command_windows_msi + ) + : "" + ) + + ddc_agent_inline_commands_windows = var.agent_installation.ddc_agent_installation_file != null ? [ + "msiexec.exe /i \"${basename(var.agent_installation.ddc_agent_installation_file)}\" /qn /norestart TARGETIP=${var.cipher_trust_manager_address}", + "\"C:\\Program Files (x86)\\Ground Labs\\Enterprise Recon 2\\er2_config_cmd.exe\" -t" # test connection from DDC to CM + ] : [] + cte_agent_inline_commands_windows = var.agent_installation.cte_agent_installation_file != null ? [ + local.cte_agent_install_command_windows, + "if %ERRORLEVEL% EQU 3010 (echo Reboot required, resetting errorlevel && exit /b 0) else (exit /b %ERRORLEVEL%)" + ] : [] + reboot_inline_commands_windows = [ + "echo 'About to reboot the host'", + "shutdown /r /t 0" + ] +} + diff --git a/modules/aws/hub/cm_association.tf b/modules/aws/hub/cm_association.tf new file mode 100644 index 000000000..d4413cec0 --- /dev/null +++ b/modules/aws/hub/cm_association.tf @@ -0,0 +1,64 @@ +locals { + cm_association_log_path_in_hub = "/tmp/dsfkit_cm_association.log" + + cm_payload = var.cm_details == null ? null : jsonencode({ + data = { + cm_name = var.cm_details.name + hostname = var.cm_details.hostname + port = var.cm_details.port + auth_method = var.cm_details.registration_method + ddc_active_node_hostname = var.cm_details.ddc_connection_hostname + ddc_active_node_port = var.cm_details.ddc_connection_port + username = var.cm_details.username + password = var.cm_details.password + registration_token = var.cm_details.registration_token + is_load_balancer = var.cm_details.is_load_balancer + ddc_enabled = var.cm_details.ddc_enabled + } + }) + + cm_association_commands = var.cm_details == null ? "" : <<-EOF + #!/bin/bash + exec > ${local.cm_association_log_path_in_hub} 2>&1 + set -e + response=$(curl -k -s -w "\n%%{http_code}" -X POST 'https://127.0.0.1:8443/integrations/api/v1/ciphertrust' --header "Content-Type: application/json" --header "Authorization: Bearer ${module.hub_instance.access_tokens.usc.token}" --data '${replace(local.cm_payload, "'", "'\\''")}') + BODY=$(echo "$response" | sed '$d') + STATUS=$(echo "$response" | tail -n1) + if [ "$STATUS" -ge 200 ] && [ "$STATUS" -lt 300 ]; then + echo "CipherTrust Manager successfully associated with the DSF Hub." + else + echo "Request failed with HTTP status $STATUS" + echo "$BODY" + exit 1 + fi + EOF +} + +resource "null_resource" "cm_association" { + count = var.cm_details != null ? 1 : 0 + connection { + type = "ssh" + user = module.hub_instance.ssh_user + private_key = file(var.ssh_key_pair.ssh_private_key_file_path) + host = var.use_public_ip ? module.hub_instance.public_ip : module.hub_instance.private_ip + + bastion_host = local.bastion_host + bastion_private_key = local.bastion_private_key + bastion_user = local.bastion_user + + script_path = local.script_path + } + + provisioner "local-exec" { + command = "echo 'Starting association of CipherTrust Manager with the DSF Hub. Logs will be written on the DSF Hub machine at ${local.cm_association_log_path_in_hub}'" + } + provisioner "remote-exec" { + inline = concat([local.cm_association_commands]) + } + depends_on = [ + module.hub_instance.ready + ] + triggers = { + command = local.cm_association_commands + } +} diff --git a/modules/aws/hub/variables.tf b/modules/aws/hub/variables.tf index 80e81ef07..1d96caa9b 100644 --- a/modules/aws/hub/variables.tf +++ b/modules/aws/hub/variables.tf @@ -311,6 +311,41 @@ variable "dra_details" { default = null } +variable "cm_details" { + sensitive = true + description = "DSF CipherTrust Manager to onboard with the Sonar Hub. Supported in Sonar Hub version 4.18 and above. If null, no CipherTrust Manager will be onboarded." + type = object({ + name = string + is_load_balancer = bool + hostname = string + port = string + ddc_enabled = string + ddc_connection_hostname = string + ddc_connection_port = string + registration_method = string + username = string + password = string + registration_token = string + }) + validation { + condition = (var.cm_details == null || (can(var.cm_details.name) && can(var.cm_details.hostname))) + error_message = "CipherTrust Manager must specify name and hostname" + } + validation { + condition = (var.cm_details == null || try(var.cm_details.registration_method == "password" || var.cm_details.registration_method == "token", false)) + error_message = "CipherTrust Manager registration_method must be either 'password' or 'token'" + } + validation { + condition = (var.cm_details == null || try(var.cm_details.registration_method != "password" || (var.cm_details.registration_method == "password" && can(var.cm_details.username) && can(var.cm_details.password)), false)) + error_message = "CipherTrust Manager must specify username and password for 'password' registration method" + } + validation { + condition = (var.cm_details == null || try(var.cm_details.registration_method != "token" || (var.cm_details.registration_method == "token" && can(var.cm_details.registration_token)), false)) + error_message = "CipherTrust Manager must specify registration_token for 'token' registration method" + } + default = null +} + variable "volume_attachment_device_name" { type = string default = null diff --git a/modules/null/ciphertrust-manager-cluster-setup/README.md b/modules/null/ciphertrust-manager-cluster-setup/README.md new file mode 100644 index 000000000..6508a7d62 --- /dev/null +++ b/modules/null/ciphertrust-manager-cluster-setup/README.md @@ -0,0 +1,97 @@ +# DSF CipherTrust Manager Cluster Setup +[![GitHub tag](https://img.shields.io/github/v/tag/imperva/dsfkit.svg)](https://github.com/imperva/dsfkit/tags) + +This Terraform module configures a CipherTrust Manager Cluster, connecting multiple CipherTrust Manager nodes into a single secure cluster. + +## Requirements +* Terraform — refer to [versions.tf](https://github.com/imperva/dsfkit/blob/master/modules/null/ciphertrust-manager-cluster-setup/versions.tf) for supported versions. +* Two or more running CipherTrust Manager servers. +* Network access between the cluster nodes on the required ports (e.g., 5432). +* API credentials (username & password) with permissions to manage DDC settings. +* `jq` utility installed on the system executing Terraform (used in DDC node activation). + +## Resources Provisioned + +This module provisions the following: + +* A `ciphertrust_cluster` resource defining and forming the CipherTrust cluster. +* Optional activation of the DDC Active Node using the CipherTrust REST API. + +The module does **not** provision CipherTrust Manager instances — it assumes the CipherTrust Manager instances already exist and are accessible. + +## Inputs + +The following input variables are **required**: + +* `nodes`: A list of CipherTrust Manager instances to form the cluster. Each entry must include: + * `host` – Internal hostname or IP used for cluster joining. + * `public_address` – Public DNS/IP used for reaching the node externally. + +Additionally, the following variables are often **required unless defaults suffice**: + +* `ddc_node_setup`: Configuration for registering a DDC Active Node in the cluster. + * `enabled`: If `true`, will attempt to activate the DDC node. + * `node_address`: The node address (typically the same as `public_address`) to register as the active node. + +* `credentials`: A sensitive object containing: + * `user`: CipherTrust API user. + * `password`: CipherTrust API password. + +Refer to [inputs](https://registry.terraform.io/modules/imperva/dsf-ciphertrust-manager-cluster-setup/null/latest?tab=inputs) for additional variables with default values and additional info. + +## Outputs + +This module currently defines no outputs. + +## Usage + +To utilize this module with a minimal configuration, include the following in your Terraform setup: + +```hcl +module "ciphertrust_cluster" { + source = "imperva/dsf-ciphertrust-manager-cluster/null" + + nodes = [ + { + host = "10.0.0.10" + public_address = "3.91.122.10" + }, + { + host = "10.0.0.11" + public_address = "3.91.122.11" + } + ] + + ddc_node_setup = { + enabled = true + node_address = "3.91.122.10" + } + + credentials = { + user = "admin" + password = "password" + } +} +``` + +To see a complete example of how to use this module in a DSF deployment with other modules, check out the [examples](https://github.com/imperva/dsfkit/tree/master/examples/aws) directory. + +We recommend using a specific version of the module (and not the latest). +See available released versions in the main repo README [here](https://github.com/imperva/dsfkit#version-history). + +Specify the module's version by adding the version parameter. For example: + +``` +module "dsf_ciphertrust_manager_cluster_setup" { + source = "imperva/dsf-ciphertrust-manager-cluster-setup/aws" + version = "x.y.z" + + # The rest of arguments are omitted for brevity +} +``` + +## Additional Information + +For more information about the DSF Hub and its features, refer to the official documentation [here](https://docs.imperva.com/bundle/v4.13-sonar-user-guide/page/80401.htm). + +For additional information about DSF deployment using terraform, refer to the main repo README [here](https://github.com/imperva/dsfkit/tree/1.7.29). diff --git a/modules/null/ciphertrust-manager-cluster-setup/ddc_active_node_setup.tftpl b/modules/null/ciphertrust-manager-cluster-setup/ddc_active_node_setup.tftpl new file mode 100644 index 000000000..0a412fe36 --- /dev/null +++ b/modules/null/ciphertrust-manager-cluster-setup/ddc_active_node_setup.tftpl @@ -0,0 +1,55 @@ +#!/bin/bash +# set -x +set -e + +if ! command -v jq; then + echo "jq utility is required to run this module. Refer to jq installation steps in your workstation platform." + exit 1 +fi + +TOKEN="" + +# Step 1: Get an auth token +response=$(curl -k -s -w "\n%%{http_code}" -X POST "https://${cm_node_address}/api/v1/auth/tokens" \ + --header "Content-Type: application/json" \ + --data '{ + "grant_type": "password", + "labels": [ + "terraform" + ], + "username": "'$CM_USER'", + "password": "'$CM_PASSWORD'", + "refresh_token_lifetime": 1 + }') + +BODY=$(echo "$response" | sed '$d') +STATUS=$(echo "$response" | tail -n1) + +if [ "$STATUS" -lt 200 ] || [ "$STATUS" -ge 300 ]; then + echo "Failed to retrieve token. HTTP $STATUS" + echo "$BODY" + exit 1 +fi + +TOKEN=$(echo "$BODY" | jq -r '.jwt') + +if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then + echo "Failed to extract token" + exit 1 +fi + +# Step 2: Activate the DDC active node +echo "Activating DDC active node..." +response=$(curl -k -s -w "\n%%{http_code}" -X POST "https://${cm_node_address}/api/v1/ddc/active-node/register" \ + --header "Authorization: Bearer $TOKEN" \ + --header "Content-Type: application/json") + +BODY=$(echo "$response" | sed '$d') +STATUS=$(echo "$response" | tail -n1) + +if [ "$STATUS" -ge 200 ] && [ "$STATUS" -lt 300 ]; then + echo "CipherTrust DDC active node setup completed successfully." +else + echo "Failed to activate DDC active node. Status: $STATUS, Response: $BODY" + exit 1 +fi diff --git a/modules/null/ciphertrust-manager-cluster-setup/main.tf b/modules/null/ciphertrust-manager-cluster-setup/main.tf new file mode 100644 index 000000000..251eb3067 --- /dev/null +++ b/modules/null/ciphertrust-manager-cluster-setup/main.tf @@ -0,0 +1,36 @@ +locals { + ddc_active_node_commands = var.ddc_node_setup.enabled ? templatefile("${path.module}/ddc_active_node_setup.tftpl", { + cm_node_address = var.ddc_node_setup.node_address + }) : null +} + +resource "ciphertrust_cluster" "cluster" { + count = length(var.nodes)> 1 ? 1 : 0 + dynamic "node" { + for_each = { for index, instance in var.nodes : index => instance } + content { + host = node.value.host + public_address = node.value.public_address + original = node.value.host == var.nodes[0].host && node.value.public_address == var.nodes[0].public_address + } + } +} + +resource "null_resource" "ddc_active_node_setup" { + count = var.ddc_node_setup.enabled ? 1 : 0 + provisioner "local-exec" { + interpreter = ["bash", "-c"] + command = local.ddc_active_node_commands + # Using env vars for credentials instead of template vars for security reasons + environment = { + CM_USER = nonsensitive(var.credentials.user) + CM_PASSWORD = nonsensitive(var.credentials.password) + } + } + triggers = { + content = local.ddc_active_node_commands + } + depends_on = [ + ciphertrust_cluster.cluster + ] +} diff --git a/modules/null/ciphertrust-manager-cluster-setup/outputs.tf b/modules/null/ciphertrust-manager-cluster-setup/outputs.tf new file mode 100644 index 000000000..e69de29bb diff --git a/modules/null/ciphertrust-manager-cluster-setup/variables.tf b/modules/null/ciphertrust-manager-cluster-setup/variables.tf new file mode 100644 index 000000000..1b539c9cd --- /dev/null +++ b/modules/null/ciphertrust-manager-cluster-setup/variables.tf @@ -0,0 +1,36 @@ +variable "nodes" { + type = list(object({ + host = string + public_address = string + })) + description = "List of CipherTrust Manager instances to form a cluster. Each instance should have a host and a public_address." + validation { + condition = length(var.nodes) > 1 + error_message = "At least two CipherTrust Manager instances are required to form a cluster." + } +} + +variable "ddc_node_setup" { + type = object({ + enabled = bool + node_address = string + }) + description = "Configuration for DDC node setup. Set 'enabled' to true to run setup for the given 'node_address' as the DDC active node in the cluster." + default = { + enabled = false + node_address = "" + } +} + +variable "credentials" { + sensitive = true + type = object({ + user = string + password = string + }) + description = "Credentials for the CipherTrust Manager api, including user and password." + default = { + user = null + password = null + } +} \ No newline at end of file diff --git a/modules/null/ciphertrust-manager-cluster-setup/versions.tf b/modules/null/ciphertrust-manager-cluster-setup/versions.tf new file mode 100644 index 000000000..430eed69a --- /dev/null +++ b/modules/null/ciphertrust-manager-cluster-setup/versions.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.3.1, < 1.8.0" + + required_providers { + ciphertrust = { + source = "ThalesGroup/ciphertrust" + version = "~> 0.11.1" + } + } +} diff --git a/sed.expr b/sed.expr index ffa1f1e08..d380750a7 100644 --- a/sed.expr +++ b/sed.expr @@ -25,4 +25,7 @@ s;imperva/dsf-agent-gw/azurerm;../../../../modules/azurerm/agent-gw; s;imperva/dsf-db-with-agent/azurerm;../../../../modules/azurerm/db-with-agent; s;imperva/dsf-dra-admin/azurerm;../../../../modules/azurerm/dra-admin; s;imperva/dsf-dra-analytics/azurerm;../../../../modules/azurerm/dra-analytics; +s;imperva/dsf-ciphertrust-manager/aws;../../../../modules/aws/ciphertrust-manager; +s;imperva/dsf-ciphertrust-manager-cluster-setup/aws;../../../../modules/aws/ciphertrust-manager-cluster-setup; +s;imperva/dsf-cte-ddc-agent/aws;../../../../modules/aws/cte-ddc-agent; /latest release tag/c\ \ No newline at end of file