From 7b1efc1448e4c56976271c099c298f3549affa49 Mon Sep 17 00:00:00 2001 From: Vedant Pareek Date: Fri, 12 Apr 2024 13:38:05 +0530 Subject: [PATCH 1/2] Added support for IAM based auth and managing master user password in secrets manager --- iam-rds.tf | 20 ++++++++++++++++++++ iam-sa.tf | 5 +++-- kms.tf | 27 +++++++++++++++++++++++++++ rds.tf | 17 ++++++++++++++++- variables.tf | 42 +++++++++++++++++++++++++++++++++++++++--- versions.tf | 2 +- 6 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 iam-rds.tf create mode 100644 kms.tf diff --git a/iam-rds.tf b/iam-rds.tf new file mode 100644 index 0000000..7d359db --- /dev/null +++ b/iam-rds.tf @@ -0,0 +1,20 @@ +# policy for IAM based authentication to RDS +data "aws_iam_policy_document" "truefoundry_db_iam_auth_policy_document" { + statement { + effect = "Allow" + actions = [ + "rds-db:connect" + ] + resources = [ + "arn:aws:rds-db:${var.aws_region}:${var.aws_account_id}:dbuser:${aws_db_instance.truefoundry_db.id}/*" + ] + } +} + +# we cannnot apply count here as module.truefoundry_oidc_iam requires fixed no of role_policy_arns +resource "aws_iam_policy" "truefoundry_db_iam_auth_policy" { + name_prefix = "${local.svcfoundry_unique_name}-db-iam-auth-policy" + description = "IAM based authentication policy for ${var.svcfoundry_name} and ${var.mlfoundry_name} in cluster ${var.cluster_name}" + policy = data.aws_iam_policy_document.truefoundry_db_iam_auth_policy_document.json + tags = local.tags +} \ No newline at end of file diff --git a/iam-sa.tf b/iam-sa.tf index 37da6a5..3d1e278 100644 --- a/iam-sa.tf +++ b/iam-sa.tf @@ -6,7 +6,7 @@ data "aws_iam_policy" "servicefoundry_ecr_policy" { module "truefoundry_oidc_iam" { source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc" - version = "5.27.0" + version = "5.39.0" create_role = true role_name = "${var.cluster_name}-truefoundry-deps" @@ -21,7 +21,8 @@ module "truefoundry_oidc_iam" { aws_iam_policy.svcfoundry_access_to_ssm.arn, aws_iam_policy.svcfoundry_access_to_multitenant_ssm.arn, aws_iam_policy.truefoundry_assume_role_all.arn, - data.aws_iam_policy.servicefoundry_ecr_policy.arn + data.aws_iam_policy.servicefoundry_ecr_policy.arn, + aws_iam_policy.truefoundry_db_iam_auth_policy.arn, ] tags = local.tags } \ No newline at end of file diff --git a/kms.tf b/kms.tf new file mode 100644 index 0000000..7b20fbf --- /dev/null +++ b/kms.tf @@ -0,0 +1,27 @@ +resource "aws_kms_key" "truefoundry_db_master_user_secret_kms_key" { + count = var.manage_master_user_password ? 1 : 0 + enable_key_rotation = true + description = "Truefoundry RDS Postgres Database encryption key" + policy = data.aws_iam_policy_document.truefoundry_db_master_user_secret_kms_policy[0].json + tags = local.tags +} + +resource "aws_kms_alias" "truefoundry_db_master_user_secret_kms" { + count = var.manage_master_user_password ? 1 : 0 + name = "alias/${var.cluster_name}-db-kms" + target_key_id = aws_kms_key.truefoundry_db_master_user_secret_kms_key[0].id +} + +data "aws_iam_policy_document" "truefoundry_db_master_user_secret_kms_policy" { + count = var.manage_master_user_password ? 1 : 0 + version = "2012-10-17" + statement { + effect = "Allow" + actions = ["kms:*"] + principals { + identifiers = ["arn:aws:iam::${var.aws_account_id}:root"] + type = "AWS" + } + resources = ["*"] + } +} \ No newline at end of file diff --git a/rds.tf b/rds.tf index c426ec6..cab27b1 100644 --- a/rds.tf +++ b/rds.tf @@ -1,4 +1,5 @@ resource "random_password" "truefoundry_db_password" { + count = var.manage_master_user_password ? 0 : 1 length = 24 special = true override_special = "#%&*()-_=+[]{}<>:" @@ -65,16 +66,30 @@ resource "aws_db_instance" "truefoundry_db" { identifier_prefix = var.truefoundry_db_enable_override ? null : local.truefoundry_db_unique_name db_name = local.truefoundry_db_database_name skip_final_snapshot = var.truefoundry_db_skip_final_snapshot - password = random_password.truefoundry_db_password.result + password = var.manage_master_user_password ? null : random_password.truefoundry_db_password[0].result + manage_master_user_password = var.manage_master_user_password ? true : null + master_user_secret_kms_key_id = var.manage_master_user_password ? aws_kms_key.truefoundry_db_master_user_secret_kms_key[0].arn : null + final_snapshot_identifier = var.truefoundry_db_skip_final_snapshot ? null : "${local.truefoundry_db_database_name}-${formatdate("DD-MM-YYYY-hh-mm-ss", timestamp())}" backup_retention_period = var.truefoundry_db_backup_retention_period instance_class = var.truefoundry_db_instance_class performance_insights_enabled = var.truefoundry_db_enable_insights performance_insights_retention_period = var.truefoundry_db_enable_insights ? 31 : 0 publicly_accessible = var.truefoundry_db_publicly_accessible deletion_protection = var.truefoundry_db_deletion_protection + iam_database_authentication_enabled = var.iam_database_authentication_enabled apply_immediately = true storage_encrypted = var.truefoundry_db_storage_encrypted enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"] storage_type = var.truefoundry_db_storage_type iops = var.truefoundry_db_storage_iops == 0 ? null : var.truefoundry_db_storage_iops } + +resource "aws_secretsmanager_secret_rotation" "turefoundry_db_secret_rotation" { + count = var.manage_master_user_password ? var.manage_master_user_password_rotation ? 1 : 0 : 0 + secret_id = aws_db_instance.truefoundry_db.master_user_secret[0].secret_arn + rotate_immediately = var.master_user_password_rotate_immediately + rotation_rules { + automatically_after_days = var.master_user_password_rotation_automatically_after_days + duration = var.master_user_password_rotation_duration + } +} diff --git a/variables.tf b/variables.tf index c165c97..8be6277 100644 --- a/variables.tf +++ b/variables.tf @@ -67,8 +67,8 @@ variable "truefoundry_db_publicly_accessible" { } variable "truefoundry_db_backup_retention_period" { - type = number - default = 14 + type = number + default = 14 description = "Backup retention period for RDS" } @@ -142,6 +142,42 @@ variable "truefoundry_db_multiple_az" { default = false } +variable "iam_database_authentication_enabled" { + description = "Enable IAM database authentication" + type = bool + default = false +} + +variable "manage_master_user_password" { + description = "Enable master user password management. If set to true master user management is done by RDS in secrets manager, if false a random password is generated" + type = bool + default = false +} + +variable "manage_master_user_password_rotation" { + description = "Enable master user password rotation" + type = bool + default = false +} + +variable "master_user_password_rotate_immediately" { + description = "Rotate master user password immediately" + type = bool + default = false +} + +variable "master_user_password_rotation_automatically_after_days" { + description = "Rotate master user password automatically after days" + type = number + default = 90 +} + +variable "master_user_password_rotation_duration" { + description = "Master user password rotation duration" + type = string + default = "3h" +} + ################################################################################## ## Mlfoundry bucket ################################################################################## @@ -151,6 +187,7 @@ variable "truefoundry_s3_enable_override" { type = bool default = false } + variable "truefoundry_s3_override_name" { description = "Override name for s3 bucket. truefoundry_s3_enable_override must be set true" type = string @@ -192,7 +229,6 @@ variable "truefoundry_s3_cors_origins" { default = ["*"] } - ################################################################################## ## MLfoundry service account ################################################################################## diff --git a/versions.tf b/versions.tf index cae652e..e7d978b 100644 --- a/versions.tf +++ b/versions.tf @@ -3,7 +3,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "5.32.1" + version = "5.44.0" } random = { source = "hashicorp/random" From 4e34578068bae743c6dba52160570ebd3ea826a0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 12 Apr 2024 08:10:52 +0000 Subject: [PATCH 2/2] terraform-docs: automated action --- README.md | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 6bbeed2..6116a8c 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,14 @@ Truefoundry AWS Control Plane Module | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.4 | -| [aws](#requirement\_aws) | 5.32.1 | +| [aws](#requirement\_aws) | 5.44.0 | | [random](#requirement\_random) | 3.5.1 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | 5.32.1 | +| [aws](#provider\_aws) | 5.44.0 | | [random](#provider\_random) | 3.5.1 | ## Modules @@ -22,26 +22,32 @@ Truefoundry AWS Control Plane Module | Name | Source | Version | |------|--------|---------| | [truefoundry\_bucket](#module\_truefoundry\_bucket) | terraform-aws-modules/s3-bucket/aws | 3.14.0 | -| [truefoundry\_oidc\_iam](#module\_truefoundry\_oidc\_iam) | terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc | 5.27.0 | +| [truefoundry\_oidc\_iam](#module\_truefoundry\_oidc\_iam) | terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc | 5.39.0 | ## Resources | Name | Type | |------|------| -| [aws_db_instance.truefoundry_db](https://registry.terraform.io/providers/hashicorp/aws/5.32.1/docs/resources/db_instance) | resource | -| [aws_db_subnet_group.rds](https://registry.terraform.io/providers/hashicorp/aws/5.32.1/docs/resources/db_subnet_group) | resource | -| [aws_iam_policy.svcfoundry_access_to_multitenant_ssm](https://registry.terraform.io/providers/hashicorp/aws/5.32.1/docs/resources/iam_policy) | resource | -| [aws_iam_policy.svcfoundry_access_to_ssm](https://registry.terraform.io/providers/hashicorp/aws/5.32.1/docs/resources/iam_policy) | resource | -| [aws_iam_policy.truefoundry_assume_role_all](https://registry.terraform.io/providers/hashicorp/aws/5.32.1/docs/resources/iam_policy) | resource | -| [aws_iam_policy.truefoundry_bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/5.32.1/docs/resources/iam_policy) | resource | -| [aws_security_group.rds](https://registry.terraform.io/providers/hashicorp/aws/5.32.1/docs/resources/security_group) | resource | -| [aws_security_group.rds-public](https://registry.terraform.io/providers/hashicorp/aws/5.32.1/docs/resources/security_group) | resource | +| [aws_db_instance.truefoundry_db](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/resources/db_instance) | resource | +| [aws_db_subnet_group.rds](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/resources/db_subnet_group) | resource | +| [aws_iam_policy.svcfoundry_access_to_multitenant_ssm](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/resources/iam_policy) | resource | +| [aws_iam_policy.svcfoundry_access_to_ssm](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/resources/iam_policy) | resource | +| [aws_iam_policy.truefoundry_assume_role_all](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/resources/iam_policy) | resource | +| [aws_iam_policy.truefoundry_bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/resources/iam_policy) | resource | +| [aws_iam_policy.truefoundry_db_iam_auth_policy](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/resources/iam_policy) | resource | +| [aws_kms_alias.truefoundry_db_master_user_secret_kms](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/resources/kms_alias) | resource | +| [aws_kms_key.truefoundry_db_master_user_secret_kms_key](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/resources/kms_key) | resource | +| [aws_secretsmanager_secret_rotation.turefoundry_db_secret_rotation](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/resources/secretsmanager_secret_rotation) | resource | +| [aws_security_group.rds](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/resources/security_group) | resource | +| [aws_security_group.rds-public](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/resources/security_group) | resource | | [random_password.truefoundry_db_password](https://registry.terraform.io/providers/hashicorp/random/3.5.1/docs/resources/password) | resource | -| [aws_iam_policy.servicefoundry_ecr_policy](https://registry.terraform.io/providers/hashicorp/aws/5.32.1/docs/data-sources/iam_policy) | data source | -| [aws_iam_policy_document.svcfoundry_access_to_multitenant_ssm](https://registry.terraform.io/providers/hashicorp/aws/5.32.1/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.svcfoundry_access_to_ssm](https://registry.terraform.io/providers/hashicorp/aws/5.32.1/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.truefoundry_assume_role_all](https://registry.terraform.io/providers/hashicorp/aws/5.32.1/docs/data-sources/iam_policy_document) | data source | -| [aws_iam_policy_document.truefoundry_bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/5.32.1/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy.servicefoundry_ecr_policy](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/data-sources/iam_policy) | data source | +| [aws_iam_policy_document.svcfoundry_access_to_multitenant_ssm](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.svcfoundry_access_to_ssm](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.truefoundry_assume_role_all](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.truefoundry_bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.truefoundry_db_iam_auth_policy_document](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/data-sources/iam_policy_document) | data source | +| [aws_iam_policy_document.truefoundry_db_master_user_secret_kms_policy](https://registry.terraform.io/providers/hashicorp/aws/5.44.0/docs/data-sources/iam_policy_document) | data source | ## Inputs @@ -52,6 +58,12 @@ Truefoundry AWS Control Plane Module | [aws\_region](#input\_aws\_region) | EKS Cluster region | `string` | n/a | yes | | [cluster\_name](#input\_cluster\_name) | Cluster name | `string` | n/a | yes | | [cluster\_oidc\_issuer\_url](#input\_cluster\_oidc\_issuer\_url) | The oidc url of the eks cluster | `string` | n/a | yes | +| [iam\_database\_authentication\_enabled](#input\_iam\_database\_authentication\_enabled) | Enable IAM database authentication | `bool` | `false` | no | +| [manage\_master\_user\_password](#input\_manage\_master\_user\_password) | Enable master user password management. If set to true master user management is done by RDS in secrets manager, if false a random password is generated | `bool` | `false` | no | +| [manage\_master\_user\_password\_rotation](#input\_manage\_master\_user\_password\_rotation) | Enable master user password rotation | `bool` | `false` | no | +| [master\_user\_password\_rotate\_immediately](#input\_master\_user\_password\_rotate\_immediately) | Rotate master user password immediately | `bool` | `false` | no | +| [master\_user\_password\_rotation\_automatically\_after\_days](#input\_master\_user\_password\_rotation\_automatically\_after\_days) | Rotate master user password automatically after days | `number` | `90` | no | +| [master\_user\_password\_rotation\_duration](#input\_master\_user\_password\_rotation\_duration) | Master user password rotation duration | `string` | `"3h"` | no | | [mlfoundry\_k8s\_namespace](#input\_mlfoundry\_k8s\_namespace) | The k8s mlfoundry namespace | `string` | n/a | yes | | [mlfoundry\_k8s\_service\_account](#input\_mlfoundry\_k8s\_service\_account) | The k8s mlfoundry service account name | `string` | n/a | yes | | [mlfoundry\_name](#input\_mlfoundry\_name) | Name of mlfoundry deployment | `string` | n/a | yes |