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_cli_azure.yml b/.github/workflows/dsf_poc_cli_azure.yml
index 1bfb68de9..0cc0f083c 100644
--- a/.github/workflows/dsf_poc_cli_azure.yml
+++ b/.github/workflows/dsf_poc_cli_azure.yml
@@ -350,12 +350,10 @@ 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 [ -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
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/installation/sonar_multi_account_deployment/main.tf b/examples/aws/installation/sonar_multi_account_deployment/main.tf
index 6cd09c2e0..d832949f5 100644
--- a/examples/aws/installation/sonar_multi_account_deployment/main.tf
+++ b/examples/aws/installation/sonar_multi_account_deployment/main.tf
@@ -310,31 +310,130 @@ 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
+ ]
}
-module "federation" {
- count = length(local.hub_gws_combinations)
+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 "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 = {
+ for idx, val in module.agentless_gw_main : 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_main_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 +446,65 @@ module "federation" {
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 = {
+ for idx, val in module.agentless_gw_dr : 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_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,
]
}
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
[](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/examples/azure/poc/dsf_deployment/agent_sources.tf b/examples/azure/poc/dsf_deployment/agent_sources.tf
index 868854dbd..9402c9a28 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 = module.network[0].vnet_subnets[0]
+ 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.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 ba8dda01e..31fb78870 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
@@ -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.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.subnet_address_spaces
hub_details = var.enable_sonar ? {
address = coalesce(module.hub_main[0].public_ip, module.hub_main[0].private_ip)
@@ -47,16 +47,16 @@ 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
}
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.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 72ac3679a..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(module.network[0].vnet_address_space, 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
@@ -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
@@ -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.subnet_address_spaces
allowed_hub_cidrs = local.hub_cidr_list
allowed_ssh_cidrs = concat(local.workstation_cidr, var.allowed_ssh_cidrs)
@@ -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
@@ -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.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 c91e60573..8151e777d 100644
--- a/examples/azure/poc/dsf_deployment/networking.tf
+++ b/examples/azure/poc/dsf_deployment/networking.tf
@@ -1,22 +1,41 @@
-# 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 {
+ 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))
+
+ 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))
+
+ db_subnet_id = coalesce(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))
+
+ 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)
+
+ ipv4_regex = "([0-9]{1,3}\\.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?"
+
+ _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._subnet_address_spaces: cidr
+ if can(regex(local.ipv4_regex, cidr))
+ ]
+}
+
+data "azurerm_subnet" "provided_subnet" {
+ count = var.subnet_id == null ? 0 : 1
+
+ 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 = 1
+ 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}"
@@ -32,41 +51,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 = 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
@@ -75,6 +61,7 @@ resource "azurerm_public_ip" "nat_gw_public_ip" {
}
resource "azurerm_nat_gateway" "nat_gw" {
+ 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
@@ -83,12 +70,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 = 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 = var.subnet_id == null ? 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/outputs.tf b/examples/azure/poc/dsf_deployment/outputs.tf
index e99d7a42e..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 = module.network[0].vnet_address_space
+ 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 8d9e1638e..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(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.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" {
@@ -14,8 +14,9 @@ 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
storage_details = var.hub_storage_details
instance_size = var.hub_instance_size
@@ -28,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.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
@@ -60,8 +61,9 @@ 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
storage_details = var.hub_storage_details
instance_size = var.hub_instance_size
@@ -77,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.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
@@ -113,9 +115,10 @@ 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
instance_size = var.agentless_gw_instance_size
base_directory = var.sonar_machine_base_directory
password = local.password
@@ -124,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.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 = {
@@ -146,9 +149,10 @@ 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
instance_size = var.agentless_gw_instance_size
base_directory = var.sonar_machine_base_directory
password = local.password
@@ -160,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.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 23416dd9f..b18bf2429 100644
--- a/examples/azure/poc/dsf_deployment/variables.tf
+++ b/examples/azure/poc/dsf_deployment/variables.tf
@@ -92,23 +92,10 @@ variable "vnet_ip_range" {
description = "Vnet ip range"
}
-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
- })
+variable "subnet_id" {
+ type = 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.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."
}
##############################
@@ -192,7 +179,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" {
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
+[](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
+[](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