Skip to content

Commit

Permalink
feat: added support to the DA for using a different KMS key for backu…
Browse files Browse the repository at this point in the history
…p encryption (#474)

Co-authored-by: Jordan <[email protected]>
  • Loading branch information
Aayush-Abhyarthi and jor2 authored Dec 4, 2024
1 parent 5a2341c commit 0281230
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 10 deletions.
4 changes: 4 additions & 0 deletions ibm_catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@
"title": "KMS encryption",
"description": "Supports key management encryption (BYOK and KYOK), including service-to-service authorization policies with KMS."
},
{
"title": "Supports backup restoration",
"description": "Provides database restoration using a backup created by a deployment with the same service ID."
},
{
"title": "CBR",
"description": "Create context-based restriction (CBR) rules for both Databases for Redis instance."
Expand Down
94 changes: 86 additions & 8 deletions solutions/standard/main.tf
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@

locals {
existing_kms_instance_crn_split = var.existing_kms_instance_crn != null ? split(":", var.existing_kms_instance_crn) : null
existing_kms_instance_guid = var.existing_kms_instance_crn != null ? element(local.existing_kms_instance_crn_split, length(local.existing_kms_instance_crn_split) - 3) : null
existing_kms_instance_region = var.existing_kms_instance_crn != null ? element(local.existing_kms_instance_crn_split, length(local.existing_kms_instance_crn_split) - 5) : null
existing_kms_instance_guid = var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].service_instance : null
existing_kms_instance_region = var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].region : null

key_name = var.prefix != null ? "${var.prefix}-${var.key_name}" : var.key_name
key_ring_name = var.prefix != null ? "${var.prefix}-${var.key_ring_name}" : var.key_ring_name
kms_key_crn = var.existing_kms_key_crn != null ? var.existing_kms_key_crn : module.kms[0].keys[format("%s.%s", local.key_ring_name, local.key_name)].crn
create_cross_account_auth_policy = !var.skip_iam_authorization_policy && var.ibmcloud_kms_api_key != null
kms_service_name = local.kms_key_crn != null ? (
can(regex(".*kms.*", local.kms_key_crn)) ? "kms" : can(regex(".*hs-crypto.*", local.kms_key_crn)) ? "hs-crypto" : null
) : null

kms_service_name = var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].service_name : null

}

#######################################################################################################################
Expand All @@ -32,6 +31,14 @@ data "ibm_iam_account_settings" "iam_account_settings" {
count = local.create_cross_account_auth_policy ? 1 : 0
}

# If existing KMS intance CRN passed, parse details from it
module "kms_instance_crn_parser" {
count = var.existing_kms_instance_crn != null ? 1 : 0
source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser"
version = "1.1.0"
crn = var.existing_kms_instance_crn
}

resource "ibm_iam_authorization_policy" "kms_policy" {
count = local.create_cross_account_auth_policy ? 1 : 0
provider = ibm.kms
Expand Down Expand Up @@ -79,14 +86,84 @@ module "kms" {
]
}

#######################################################################################################################
# KMS backup encryption key for Redis
#######################################################################################################################

locals {
existing_backup_kms_instance_guid = var.existing_backup_kms_instance_crn != null ? module.backup_kms_instance_crn_parser[0].service_instance : null
existing_backup_kms_instance_region = var.existing_backup_kms_instance_crn != null ? module.backup_kms_instance_crn_parser[0].region : null

backup_key_name = var.prefix != null ? "${var.prefix}-backup-encryption-${var.key_name}" : "backup-encryption-${var.key_name}"
backup_key_ring_name = var.prefix != null ? "${var.prefix}-backup-encryption-${var.key_ring_name}" : "backup-encryption-${var.key_ring_name}"
backup_kms_key_crn = var.existing_backup_kms_key_crn != null ? var.existing_backup_kms_key_crn : var.existing_backup_kms_instance_crn != null ? module.backup_kms[0].keys[format("%s.%s", local.backup_key_ring_name, local.backup_key_name)].crn : null
backup_kms_service_name = var.existing_backup_kms_instance_crn != null ? module.backup_kms_instance_crn_parser[0].service_name : null
}

# If existing KMS intance CRN passed, parse details from it
module "backup_kms_instance_crn_parser" {
count = var.existing_backup_kms_instance_crn != null ? 1 : 0
source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser"
version = "1.1.0"
crn = var.existing_backup_kms_instance_crn
}

resource "ibm_iam_authorization_policy" "backup_kms_policy" {
count = local.existing_backup_kms_instance_guid == local.existing_kms_instance_guid ? 0 : var.existing_backup_kms_key_crn != null ? 0 : var.existing_backup_kms_instance_crn != null ? !var.skip_iam_authorization_policy ? 1 : 0 : 0
provider = ibm.kms
source_service_account = local.create_cross_account_auth_policy ? data.ibm_iam_account_settings.iam_account_settings[0].account_id : null
source_service_name = "databases-for-redis"
source_resource_group_id = module.resource_group.resource_group_id
target_service_name = local.backup_kms_service_name
target_resource_instance_id = local.existing_backup_kms_instance_guid
roles = ["Reader"]
description = "Allow all Redis instances in the resource group ${module.resource_group.resource_group_id} to read from the ${local.backup_kms_service_name} instance GUID ${local.existing_backup_kms_instance_guid}"
}

# workaround for https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4478
resource "time_sleep" "wait_for_backup_kms_authorization_policy" {
depends_on = [ibm_iam_authorization_policy.backup_kms_policy]
create_duration = "30s"
}

module "backup_kms" {
providers = {
ibm = ibm.kms
}
count = var.existing_backup_kms_key_crn != null ? 0 : var.existing_backup_kms_instance_crn != null ? 1 : 0
source = "terraform-ibm-modules/kms-all-inclusive/ibm"
version = "4.15.13"
create_key_protect_instance = false
region = local.existing_backup_kms_instance_region
existing_kms_instance_crn = var.existing_backup_kms_instance_crn
key_ring_endpoint_type = var.kms_endpoint_type
key_endpoint_type = var.kms_endpoint_type
keys = [
{
key_ring_name = local.backup_key_ring_name
existing_key_ring = false
force_delete_key_ring = true
keys = [
{
key_name = local.backup_key_name
standard_key = false
rotation_interval_month = 3
dual_auth_delete_enabled = false
force_delete = true
}
]
}
]
}

module "redis" {
source = "../../modules/fscloud"
depends_on = [time_sleep.wait_for_authorization_policy]
depends_on = [time_sleep.wait_for_authorization_policy, time_sleep.wait_for_backup_kms_authorization_policy]
resource_group_id = module.resource_group.resource_group_id
instance_name = var.prefix != null ? "${var.prefix}-${var.name}" : var.name
region = var.region
redis_version = var.redis_version
skip_iam_authorization_policy = var.skip_iam_authorization_policy || local.create_cross_account_auth_policy
skip_iam_authorization_policy = local.create_cross_account_auth_policy ? true : var.skip_iam_authorization_policy
existing_kms_instance_guid = local.existing_kms_instance_guid
kms_key_crn = local.kms_key_crn
access_tags = var.access_tags
Expand All @@ -101,5 +178,6 @@ module "redis" {
auto_scaling = var.auto_scaling
configuration = var.configuration
service_credential_names = var.service_credential_names
backup_encryption_key_crn = local.backup_kms_key_crn
backup_crn = var.backup_crn
}
19 changes: 17 additions & 2 deletions solutions/standard/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,14 @@ variable "ibmcloud_kms_api_key" {
}

variable "existing_kms_instance_crn" {
description = "The CRN of the KMS instance (Hyper Protect Crypto Services or Key Protect). Required to create a new root key if no value is passed with the `existing_kms_key_crn` input. Also required to create an authorization policy if `skip_iam_authorization_policy` is false. If the KMS instance is in different account you must also provide a value for `ibmcloud_kms_api_key`."
description = "The CRN of an Hyper Protect Crypto Services or Key Protect instance that you want to use for both disk and backup encryption. Backup encryption is only supported is some regions ([learn more](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok)), so if you need to use a different instance for backup encryption from a supported region, use the `existing_backup_kms_instance_crn` input."
type = string
default = null
}

variable "existing_kms_key_crn" {
type = string
description = "The CRN of a Hyper Protect Crypto Services or Key Protect root key to use for disk encryption. If not specified, a new key ring and root key are created in the KMS instance specified in the `existing_kms_instance_crn` input."
description = "The CRN of an Hyper Protect Crypto Services or Key Protect encryption key that you want to use to use for both disk and backup encryption. If no value is passed, a new key ring and key will be created in the instance provided in the `existing_kms_instance_crn` input. Backup encryption is only supported is some regions ([learn more](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok)), so if you need to use a key from a different region for backup encryption, use the `existing_backup_kms_key_crn` input."
default = null
}

Expand Down Expand Up @@ -237,3 +237,18 @@ variable "provider_visibility" {
error_message = "Invalid visibility option. Allowed values are 'public', 'private', or 'public-and-private'."
}
}

##############################################################
# Backup Encryption
##############################################################
variable "existing_backup_kms_key_crn" {
type = string
description = "The CRN of an Hyper Protect Crypto Services or Key Protect encryption key that you want to use to encrypt database backups. If no value is passed, the value of `existing_kms_key_crn` is used. If no value is passed for that, a new key will be created in the provided KMS instance and used for both disk encryption, and backup encryption."
default = null
}

variable "existing_backup_kms_instance_crn" {
description = "The CRN of an Hyper Protect Crypto Services or Key Protect instance that you want to use to encrypt database backups. If no value is passed, the value of the `existing_kms_instance_crn` input will be used, however backup encryption is only supported in certain regions so you need to ensure the KMS for backup is coming from one of the supported regions. [Learn more](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok)"
type = string
default = null
}
1 change: 1 addition & 0 deletions tests/pr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ func TestRunStandardSolutionSchematics(t *testing.T) {
{Name: "service_credential_names", Value: "{\"admin_test\": \"Administrator\", \"editor_test\": \"Editor\"}", DataType: "map(string)"},
{Name: "existing_secrets_manager_instance_crn", Value: permanentResources["secretsManagerCRN"], DataType: "string"},
{Name: "service_credential_secrets", Value: serviceCredentialSecrets, DataType: "list(object)"},
{Name: "existing_backup_kms_key_crn", Value: permanentResources["hpcs_south_root_key_crn"], DataType: "string"},
}
err := options.RunSchematicTest()
assert.Nil(t, err, "This should not have errored")
Expand Down

0 comments on commit 0281230

Please sign in to comment.