Keeper – Using with Terraform

When using with Terraform with Keeper Secrets Manager (KSM) a provider is needed to be configured, the Keeper documentation details how this can be configured. Keeper: Terraform Provider Documentation. However, a short version can be found below.

https://docs.keeper.io/en/keeperpam/secrets-manager/integrations/terraform

You need to have already created a Keeper Secrets Manager (KSM) profile which you can use for authentication, this you’ll be exporting to JSON so it can be used by Terraform.

Convert Profiles to Terraform Compatible

We want to get these Applications (profiles) in a format in which we can use them with Terraform, we can export each of the Applications (profiles) to their own JSON file. The recommendation for using this with Terraform is to use a JSON file. 

If your KSM profile is ever updated, you’ll need to run and update to export an updated JSON file.

Ensure you have entered the Python Virtual Environment where KSM is installed, before you start to administer or run your Terraform:

source ~/virtualenvs/keeper/bin/activate

Then we can export, in my example I have two KSM profiles which have access to different sets of Secrets, so I’ll create both of these.

cd ~/.keeper/ksm/

ksm profile export --plain --file-format json ids-imt-net-development > account1-development.json
ksm profile export --plain --file-format json ids-imt-net-staging > account2-staging.json

Now fix the permissions on the files:

chmod 0600 account1-development.json
chmod 0600 account2-staging.json

You now have your JSON files ready for use with Terraform.

Prepare Terraform Environment

We’re going to show a basic setup of our two Terraform environments which use their own JSON file with the corresponding profile from KSM.

  • account1-development.json
  • account2-staging.json

Example 1 – account1-development.json

We add to the providers block the “secretsmanager” provider an example is given below (providers.tf):

terraform {
  # Terraform Version
  required_version = ">= 1.8.2"

  # Terraform State and Lock Location
  backend "s3" {
    bucket         = "account1-development-tgw"
    dynamodb_table = "account1-development-tgw"
    key            = "account1-development-tgw-state"
    region         = "eu-west-2"
  }

  # Terraform Providers (with version constraints)
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~>6.11.0"
    }
    secretsmanager = {
      source  = "keeper-security/secretsmanager"
      version = ">= 1.0.0"
    }
  }
}

Next we need to specify within the same file (below) the provider “runtime”, which specifies where this Terraform environment can find the JSON file containing the credentials to access KSM (Keeper Secrets Manager) to be able to download the secrets we need (using their UID).

...
provider "secretsmanager" {
  credential = file("~/.keeper/ksm/account1-development.json")
}

Once you have added the new provider (KSM – secretsmanager) you’ll likely need to run a “terraform init” to load this provider into your Terraform environment.

terraform init -upgrade

This Terraform environment is now ready to use the variables, let’s look at a second one.

Example 2 – account2-staging.json

We add to the providers block the “secretsmanager” provider an example is given below (providers.tf):

terraform {
  # Terraform Version
  required_version = ">= 1.8.2"

  # Terraform State and Lock Location
  backend "s3" {
    bucket         = "account2-staging-tgw"
    dynamodb_table = "account2-staging-tgw"
    key            = "account2-staging-tgw-state"
    region         = "eu-west-2"
  }

  # Terraform Providers (with version constraints)
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~>6.11.0"
    }
    secretsmanager = {
      source  = "keeper-security/secretsmanager"
      version = ">= 1.0.0"
    }
  }
}

Next we need to specify within the same file (below) the provider “runtime”, which specifies where this Terraform environment can find the JSON file containing the credentials to access KSM (Keeper Secrets Manager) to be able to download the secrets we need (using their UID).

...
provider "secretsmanager" {
  credential = file("~/.config/ksm/account2-staging.json")
}

That’s it, that’s both of the Terraform environments ready for use. We can now start retrieving some secrets from KSM within our templates.

Once you have added the new provider (KSM – secretsmanager) you’ll likely need to run a “terraform init” to load this provider into your Terraform environment.

terraform init -upgrade

This Terraform environment is now ready to use the variables, let’s look at a second one.

Terraform Example using account1-development.json

Now we have the two Terraform environments setup, we can actually start using KSM to obtain the secrets and interpolate them into our Terraform templates at runtime.

We are just going to use a simple example here to obtain one of the shared keys that is stored in KSM for the ids-imt-net-development profile.

Get the Path UID for the Tunnel 1 Secret

Run the following to show up the secrets:

(keeper) user@machine:~/vscode/aws-transit-gateway-development$ ksm secrets list

 UID                     Record Type Title
 ----------------------- ----------- -------------------------------------------------------
 ECxfDDZa123456VorEaQ  login       account2 - AWS VPN Preshared Keys (Tunnel 1)
 _V7SH123456WS6YB5F_w  login       account2 - AWS VPN Preshared Keys (Tunnel 2)

We want the first one, the one with the UID of: ECxfDDZa123456VorEaQ

Add the data definition which refers to the UID, so that Terraform can access KSM and retrieve the secret within the variables.tf file:

# Keeper Secrets Manager
data "secretsmanager_login" "TUNNEL1_PRESHARED_KEY" {
  path = "ECxfDDZa123456VorEaQ"
}

Now we have the data resource within Terraform, we can use it to populate a value (i.e. interpolate) in the preshared key for the VPN which is stored in that KSM Secret at runtime, for example see the below, on line 16, the line:

tunnel1_preshared_key = data.secretsmanager_login.TUNNEL1_PRESHARED_KEY.password

Here you can see the use of the variable in action, we first refer to it as a data resource, then the secretsmanager_login then the name of the variable you defined above, i.e. TUNNEL1_PRESHARED_KEY and finally the actual attribute where the secret is held, in this example password.

At the time of writing you can’t use/access “Custom Fields”, so all values must be stored in the allowed list of attributes: https://registry.terraform.io/providers/keeper-security/secretsmanager/latest/docs/data-sources/login

So an example AWS VPN configuration within Terraform that uses a KSM obtained secret can be seen below:

resource "aws_vpn_connection" "TGW-VPN-1" {
  customer_gateway_id      = aws_customer_gateway.CGW-WTGC-1.id
  transit_gateway_id       = aws_ec2_transit_gateway.TGW.id
  type                     = "ipsec.1"
  tunnel_inside_ip_version = "ipv4"

  tunnel1_ike_versions = ["ikev2"]
  tunnel2_ike_versions = ["ikev2"]

  tunnel1_dpd_timeout_action = "clear"
  tunnel2_dpd_timeout_action = "clear"

  tunnel1_dpd_timeout_seconds = 30
  tunnel2_dpd_timeout_seconds = 30

  tunnel1_preshared_key = data.secretsmanager_login.TUNNEL1_PRESHARED_KEY.password
  tunnel2_preshared_key = data.secretsmanager_login.TUNNEL2_PRESHARED_KEY.password

  tunnel1_inside_cidr = var.tunnel1_inside_cidr
  tunnel2_inside_cidr = var.tunnel2_inside_cidr

  # IPSEC Phase1 Settings (IKE)
  tunnel1_phase1_encryption_algorithms = ["AES256"]
  tunnel2_phase1_encryption_algorithms = ["AES256"]

  tunnel1_phase1_integrity_algorithms = ["SHA2-256", "SHA2-512"]
  tunnel2_phase1_integrity_algorithms = ["SHA2-256", "SHA2-512"]

  tunnel1_phase1_dh_group_numbers = [14, 21]
  tunnel2_phase1_dh_group_numbers = [14, 21]

  tunnel1_phase1_lifetime_seconds = 14400
  tunnel2_phase1_lifetime_seconds = 14400

  # IPSEC Phase2 Settings
  tunnel1_phase2_encryption_algorithms = ["AES256"]
  tunnel2_phase2_encryption_algorithms = ["AES256"]

  tunnel1_phase2_integrity_algorithms = ["SHA2-256", "SHA2-512"]
  tunnel2_phase2_integrity_algorithms = ["SHA2-256", "SHA2-512"]

  tunnel1_phase2_dh_group_numbers = [14, 21]
  tunnel2_phase2_dh_group_numbers = [14, 21]

  tunnel1_phase2_lifetime_seconds = 3600
  tunnel2_phase2_lifetime_seconds = 3600

  tunnel1_log_options {
    cloudwatch_log_options {
      log_enabled       = true
      log_group_arn     = aws_cloudwatch_log_group.VPN_Tunnel1_Log_Group.arn
      log_output_format = "text"
    }
  }

  tunnel2_log_options {
    cloudwatch_log_options {
      log_enabled       = true
      log_group_arn     = aws_cloudwatch_log_group.VPN_Tunnel2_Log_Group.arn
      log_output_format = "text"
    }
  }

  tags = {
    name         = "TGW-VPN-1",
    environment  = var.environment
    wsi_owner    = var.wsi_owner
    project_code = var.project_code
    budget_code  = var.budget_code
    function = "VPN"
  }
}

You can find a list of the different types of value you can store in Keeper Secrets Manager with: https://docs.keeper.io/en/keeperpam/secrets-manager/integrations/terraform

Leave a comment