From 0a03d75af7d478999c0e52860a1e675960abbaa2 Mon Sep 17 00:00:00 2001 From: Young-ook Date: Sat, 18 Dec 2021 13:05:09 +0900 Subject: [PATCH] feat(proxy): rds proxy submodule (#24) * feat(proxy): rds proxy submodule * feat(proxy): get secrets policy --- modules/proxy/README.md | 17 ++++++ modules/proxy/default.tf | 15 +++++ modules/proxy/labels.tf | 17 ++++++ modules/proxy/main.tf | 111 +++++++++++++++++++++++++++++++++++++ modules/proxy/outputs.tf | 6 ++ modules/proxy/variables.tf | 37 +++++++++++++ 6 files changed, 203 insertions(+) create mode 100644 modules/proxy/README.md create mode 100644 modules/proxy/default.tf create mode 100644 modules/proxy/labels.tf create mode 100644 modules/proxy/main.tf create mode 100644 modules/proxy/outputs.tf create mode 100644 modules/proxy/variables.tf diff --git a/modules/proxy/README.md b/modules/proxy/README.md new file mode 100644 index 0000000..7820d18 --- /dev/null +++ b/modules/proxy/README.md @@ -0,0 +1,17 @@ +# Amazon RDS Proxy +By using [Amazon RDS Proxy](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html), you can allow your applications to pool and share database connections to improve their ability to scale. RDS Proxy makes applications more resilient to database failures by automatically connecting to a standby DB instance while preserving application connections. By using RDS Proxy, you can also enforce AWS Identity and Access Management (IAM) authentication for databases, and securely store credentials in AWS Secrets Manager. + +## Quickstart +### Setup +```hcl +module "proxy" { + source = "Young-ook/aurora/aws//modules/proxy" + name = var.name +} +``` + +Run terraform: +``` +terraform init +terraform apply +``` diff --git a/modules/proxy/default.tf b/modules/proxy/default.tf new file mode 100644 index 0000000..bf00a5e --- /dev/null +++ b/modules/proxy/default.tf @@ -0,0 +1,15 @@ +### default values + +locals { + default_proxy_config = { + debug_logging = false + engine_family = "MYSQL" # allowed values: MYSQL | POSTGRESQL + idle_client_timeout = 1800 + require_tls = true + target_role = "READ_WRITE" # allowed values: READ_WRITE | READ_ONLY + } + default_auth_config = { + auth_scheme = "SECRETS" + iam_auth = "DISABLED" + } +} diff --git a/modules/proxy/labels.tf b/modules/proxy/labels.tf new file mode 100644 index 0000000..0df5b92 --- /dev/null +++ b/modules/proxy/labels.tf @@ -0,0 +1,17 @@ +resource "random_string" "uid" { + length = 12 + upper = false + lower = true + number = false + special = false +} + +locals { + service = "rdsproxy" + uid = join("-", [local.service, random_string.uid.result]) + name = var.name == null || var.name == "" ? local.uid : var.name + default-tags = merge( + { "terraform.io" = "managed" }, + { "Name" = local.name }, + ) +} diff --git a/modules/proxy/main.tf b/modules/proxy/main.tf new file mode 100644 index 0000000..76baba7 --- /dev/null +++ b/modules/proxy/main.tf @@ -0,0 +1,111 @@ +## rds proxy + +# parameters +locals { + security_groups = var.security_groups == [] ? null : var.security_groups + debug_logging = lookup(var.proxy_config, "debug_logging", local.default_proxy_config.debug_logging) + engine_family = lookup(var.proxy_config, "engine_family", local.default_proxy_config.engine_family) + idle_client_timeout = lookup(var.proxy_config, "idle_client_timeout", local.default_proxy_config.idle_client_timeout) + require_tls = lookup(var.proxy_config, "require_tls", local.default_proxy_config.require_tls) + target_role = lookup(var.proxy_config, "target_role", local.default_proxy_config.target_role) + auth = lookup(var.proxy_config, "auth", local.default_auth_config) + cluster_id = lookup(var.proxy_config, "cluster_id", null) + db_id = lookup(var.proxy_config, "db_id", null) + user_name = lookup(var.auth_config, "user_name") # required + user_password = lookup(var.auth_config, "user_password") # required +} + +# security/secret +resource "aws_secretsmanager_secret" "password" { + name = join("-", [local.user_name, local.name]) + tags = var.tags + #policy = var.policy +} + +resource "aws_secretsmanager_secret_version" "password" { + secret_id = aws_secretsmanager_secret.password.id + secret_string = local.user_password +} + +# aws partition and region (global, gov, china) +module "aws" { + source = "Young-ook/spinnaker/aws//modules/aws-partitions" +} + +# security/policy +resource "aws_iam_role" "proxy" { + name = local.name + tags = merge(local.default-tags, var.tags) + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = [format("rds.%s", module.aws.partition.dns_suffix)] + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_iam_role_policy" "get-secret" { + role = aws_iam_role.proxy.id + policy = jsonencode({ + Statement = [{ + Action = [ + "secretsmanager:GetResourcePolicy", + "secretsmanager:GetSecretValue", + "secretsmanager:DescribeSecret", + "secretsmanager:ListSecretVersionIds", + ] + Effect = "Allow" + Resource = [aws_secretsmanager_secret.password.arn] + }, { + Action = [ + "secretsmanager:GetRandomPassword", + "secretsmanager:ListSecrets", + ] + Effect = "Allow" + Resource = ["*"] + }] + Version = "2012-10-17" + }) +} + +# proxy +resource "aws_db_proxy" "proxy" { + name = local.name + tags = merge(local.default-tags, var.tags) + debug_logging = local.debug_logging + engine_family = local.engine_family + idle_client_timeout = local.idle_client_timeout + require_tls = local.require_tls + role_arn = aws_iam_role.proxy.arn + vpc_subnet_ids = var.subnets + vpc_security_group_ids = local.security_groups + + auth { + description = lookup(local.auth, "description", null) + auth_scheme = lookup(local.auth, "auth_scheme", null) + secret_arn = lookup(local.auth, "secret_arn", aws_secretsmanager_secret.password.arn) + iam_auth = lookup(local.auth, "iam_auth", null) + } +} + +resource "aws_db_proxy_default_target_group" "proxy" { + db_proxy_name = aws_db_proxy.proxy.name + connection_pool_config { + connection_borrow_timeout = 120 + init_query = "SET x=1, y=2" + max_connections_percent = 100 + max_idle_connections_percent = 50 + session_pinning_filters = ["EXCLUDE_VARIABLE_SETS"] + } +} + +resource "aws_db_proxy_target" "proxy" { + db_instance_identifier = local.db_id + db_cluster_identifier = local.cluster_id + db_proxy_name = aws_db_proxy.proxy.name + target_group_name = aws_db_proxy_default_target_group.proxy.name +} diff --git a/modules/proxy/outputs.tf b/modules/proxy/outputs.tf new file mode 100644 index 0000000..2d0fb90 --- /dev/null +++ b/modules/proxy/outputs.tf @@ -0,0 +1,6 @@ +# output variables + +output "proxy" { + description = "Attributes of the generated rds proxy" + value = aws_db_proxy.proxy +} diff --git a/modules/proxy/variables.tf b/modules/proxy/variables.tf new file mode 100644 index 0000000..c495d5b --- /dev/null +++ b/modules/proxy/variables.tf @@ -0,0 +1,37 @@ +### network +variable "subnets" { + description = "The subnet IDs for rds" + type = list(string) + default = [] +} + +### security +variable "security_groups" { + description = "The security group IDs for rds" + type = list(string) + default = [] +} + +variable "auth_config" { + description = "Authentication configuration" + default = {} +} + +### db proxy +variable "proxy_config" { + description = "RDS proxy configuration" + default = {} +} + +### description +variable "name" { + description = "The logical name of the module instance" + type = string +} + +### tags +variable "tags" { + description = "The key-value maps for tagging" + type = map(string) + default = {} +}