Skip to content

Commit

Permalink
Merge pull request #3 from synapsestudios/fix-sast-violations
Browse files Browse the repository at this point in the history
fix sast violations
  • Loading branch information
dragonfleas authored Jul 19, 2024
2 parents 4aa9a99 + d2cb5ef commit 78d7310
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 54 deletions.
40 changes: 40 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Local .terraform directories
**/.terraform/*

# .tfstate files
*.tfstate
*.tfstate.*

# Crash log files
crash.log
crash.*.log

# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars
*.tfvars.json

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Include override files you do wish to add to version control using negated pattern
# !example_override.tf

# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*

# Ignore CLI configuration files
.terraformrc
terraform.rc

# Add vscode workspace file
.vscode/*

# Ignore test files
**/.terraform.lock.hcl
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ No requirements.
| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | n/a |
| <a name="provider_random"></a> [random](#provider\_random) | n/a |

## Modules

Expand All @@ -21,14 +20,14 @@ No modules.
| Name | Type |
|------|------|
| [aws_db_subnet_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_subnet_group) | resource |
| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_rds_cluster.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster) | resource |
| [aws_rds_cluster_instance.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster_instance) | resource |
| [aws_secretsmanager_secret.connection_string](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret) | resource |
| [aws_secretsmanager_secret.root_password](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret) | resource |
| [aws_secretsmanager_secret_version.connection_string](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_version) | resource |
| [aws_secretsmanager_secret_version.root_password](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_version) | resource |
| [aws_security_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [random_password.password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource |
| [aws_iam_policy_document.rds_monitoring](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_secretsmanager_secret_version.root_password](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/secretsmanager_secret_version) | data source |
| [aws_vpc.database_vpc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc) | data source |

## Inputs
Expand All @@ -37,21 +36,23 @@ No modules.
|------|-------------|------|---------|:--------:|
| <a name="input_additional_security_groups"></a> [additional\_security\_groups](#input\_additional\_security\_groups) | Any additional security groups the cluster should be added to | `list(string)` | `[]` | no |
| <a name="input_availability_zones"></a> [availability\_zones](#input\_availability\_zones) | Availability zones for the database | `list(string)` | n/a | yes |
| <a name="input_database_name"></a> [database\_name](#input\_database\_name) | Name of the default database to create | `string` | n/a | yes |
| <a name="input_database_name"></a> [database\_name](#input\_database\_name) | Name of the default database to create | `string` | `"main"` | no |
| <a name="input_database_subnets"></a> [database\_subnets](#input\_database\_subnets) | Subnets for the database | `list(string)` | n/a | yes |
| <a name="input_db_cluster_parameter_group_name"></a> [db\_cluster\_parameter\_group\_name](#input\_db\_cluster\_parameter\_group\_name) | parameter group | `string` | n/a | yes |
| <a name="input_instance_class"></a> [instance\_class](#input\_instance\_class) | Instance class | `string` | n/a | yes |
| <a name="input_deletion_protection"></a> [deletion\_protection](#input\_deletion\_protection) | Enable deletion protection. DO NOT DISABLE IN PRODUCTION, THIS IS ONLY FOR TESTING. | `bool` | `true` | no |
| <a name="input_instance_class"></a> [instance\_class](#input\_instance\_class) | Instance class | `string` | `"db.t4g.medium"` | no |
| <a name="input_instance_count"></a> [instance\_count](#input\_instance\_count) | How many RDS instances to create | `number` | `1` | no |
| <a name="input_name"></a> [name](#input\_name) | Determines naming convention of assets. Generally follows DNS naming convention. | `string` | n/a | yes |
| <a name="input_name"></a> [name](#input\_name) | Determines naming convention of assets. Generally follows DNS naming convention. Service name or abbreviation. | `string` | n/a | yes |
| <a name="input_tags"></a> [tags](#input\_tags) | A mapping of tags to assign to the AWS resources. | `map(string)` | `{}` | no |
| <a name="input_vpc_id"></a> [vpc\_id](#input\_vpc\_id) | The ID of the vpc the database belongs to | `string` | n/a | yes |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_connection_string_arn"></a> [connection\_string\_arn](#output\_connection\_string\_arn) | n/a |
| <a name="output_db_cluster_id"></a> [db\_cluster\_id](#output\_db\_cluster\_id) | n/a |
| <a name="output_root_password_secret_id"></a> [root\_password\_secret\_id](#output\_root\_password\_secret\_id) | n/a |
| <a name="output_security_group_id"></a> [security\_group\_id](#output\_security\_group\_id) | n/a |
| <a name="output_connection_string_arn"></a> [connection\_string\_arn](#output\_connection\_string\_arn) | The ARN of the secret that stores the connection string for the RDS cluster.<br>The secret stored inside is formatted as: postgresql://<username>:<password>@<endpoint>:<port>/<database> |
| <a name="output_db_cluster_id"></a> [db\_cluster\_id](#output\_db\_cluster\_id) | The ID of the RDS cluster |
| <a name="output_root_credentials"></a> [root\_credentials](#output\_root\_credentials) | A map containing the username and password for the root user of the RDS cluster. Caution: This output will display the password in plain text. |
| <a name="output_root_password_id"></a> [root\_password\_id](#output\_root\_password\_id) | The ID of the secret that stores the root password for the RDS cluster |
| <a name="output_security_group_id"></a> [security\_group\_id](#output\_security\_group\_id) | The ID of the EC2 security group that controls access to the RDS cluster |
<!-- END_TF_DOCS -->
93 changes: 51 additions & 42 deletions main.tf
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
resource "aws_rds_cluster" "this" {
# checkov:skip=CKV2_AWS_8: Using snapshots for backups
# checkov:skip=CKV2_AWS_27: Parameter group is passed in as a variable
# checkov:skip=CKV_AWS_327: We will use AWS managed keys because CMK are expensive and not necessary for our use case
# checkov:skip=CKV_AWS_162: IAM Authentication does not fit into our use cases
cluster_identifier_prefix = var.name
engine = "aurora-postgresql"
engine_version = "14.6"
database_name = var.database_name
skip_final_snapshot = false
final_snapshot_identifier = "${var.name}-final"
master_username = "root"
master_password = aws_secretsmanager_secret_version.root_password.secret_string
manage_master_user_password = true
db_subnet_group_name = aws_db_subnet_group.this.name
storage_encrypted = true
availability_zones = var.availability_zones
Expand All @@ -15,47 +19,64 @@ resource "aws_rds_cluster" "this" {
vpc_security_group_ids = concat([aws_security_group.this.id], var.additional_security_groups)
tags = var.tags
db_cluster_parameter_group_name = var.db_cluster_parameter_group_name
deletion_protection = true
}

resource "aws_secretsmanager_secret" "root_password" {
name_prefix = "aurora-root-${var.name}"
description = "Root password for the ${var.name} aurora cluster database"
tags = var.tags
}

resource "aws_secretsmanager_secret_version" "root_password" {
secret_id = aws_secretsmanager_secret.root_password.id
secret_string = random_password.password.result
}
deletion_protection = var.deletion_protection
copy_tags_to_snapshot = true

resource "random_password" "password" {
length = 32
special = true
min_special = 1
override_special = "-._~" # URL-safe characters prevent parsing errors when using this password in a connection string
enabled_cloudwatch_logs_exports = [
"postgresql",
]
}

resource "aws_secretsmanager_secret" "connection_string" {
# checkov:skip=CKV2_AWS_57: RDS connection strings cannot be rotated
# checkov:skip=CKV_AWS_149: We will use AWS managed keys because CMK are expensive and not necessary for our use case
name_prefix = "aurora-connectionstring-${var.name}"
description = "Connection String for the ${var.name} aurora cluster database"
tags = var.tags
}

data "aws_secretsmanager_secret_version" "root_password" {
secret_id = aws_rds_cluster.this.master_user_secret[0].secret_arn
}

resource "aws_secretsmanager_secret_version" "connection_string" {
secret_id = aws_secretsmanager_secret.connection_string.id
secret_string = "postgresql://${aws_rds_cluster.this.master_username}:${aws_secretsmanager_secret_version.root_password.secret_string}@${aws_rds_cluster.this.endpoint}:${aws_rds_cluster.this.port}/${aws_rds_cluster.this.database_name}"
secret_string = "postgresql://${aws_rds_cluster.this.master_username}:${urlencode(jsondecode(data.aws_secretsmanager_secret_version.root_password.secret_string)["password"])}@${aws_rds_cluster.this.endpoint}:${aws_rds_cluster.this.port}/${aws_rds_cluster.this.database_name}"
}

data "aws_iam_policy_document" "rds_monitoring" {
statement {
actions = [
"sts:AssumeRole",
]

principals {
type = "Service"
identifiers = ["monitoring.rds.amazonaws.com"]
}
}
}

resource "aws_iam_role" "this" {
name = "${var.name}-rds-monitoring-role"
assume_role_policy = data.aws_iam_policy_document.rds_monitoring.json
managed_policy_arns = ["arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole"]
}

resource "aws_rds_cluster_instance" "this" {
count = var.instance_count
engine = "aurora-postgresql"
engine_version = "14.6"
identifier_prefix = "${var.name}-${count.index + 1}"
cluster_identifier = aws_rds_cluster.this.id
instance_class = var.instance_class
db_subnet_group_name = aws_db_subnet_group.this.name
tags = var.tags
# checkov:skip=CKV_AWS_354: We will use AWS managed keys because CMK are expensive and not necessary for our use case
count = var.instance_count
engine = "aurora-postgresql"
engine_version = "14.6"
identifier_prefix = "${var.name}-${count.index + 1}"
cluster_identifier = aws_rds_cluster.this.id
instance_class = var.instance_class
db_subnet_group_name = aws_db_subnet_group.this.name
tags = var.tags
auto_minor_version_upgrade = true
monitoring_interval = 5
monitoring_role_arn = aws_iam_role.this.arn
performance_insights_enabled = true
}

resource "aws_db_subnet_group" "this" {
Expand All @@ -78,29 +99,17 @@ resource "aws_security_group" "this" {
to_port = 5432
protocol = "tcp"
cidr_blocks = [data.aws_vpc.database_vpc.cidr_block]
description = "PostgreSQL traffic in"
}
egress {
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = [data.aws_vpc.database_vpc.cidr_block]
description = "PostgreSQL traffic out"
}
}

data "aws_vpc" "database_vpc" {
id = var.vpc_id
}

output "db_cluster_id" {
value = aws_rds_cluster.this.cluster_identifier
}

output "security_group_id" {
value = aws_security_group.this.id
}
output "root_password_secret_id" {
value = aws_secretsmanager_secret.root_password.id
}
output "connection_string_arn" {
value = aws_secretsmanager_secret.connection_string.arn
}
32 changes: 32 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
output "db_cluster_id" {
description = "The ID of the RDS cluster"
value = aws_rds_cluster.this.cluster_identifier
}

output "security_group_id" {
description = "The ID of the EC2 security group that controls access to the RDS cluster"
value = aws_security_group.this.id
}

output "root_password_id" {
description = "The ID of the secret that stores the root password for the RDS cluster"
value = data.aws_secretsmanager_secret_version.root_password.id
}

output "connection_string_arn" {
description = <<EOT
The ARN of the secret that stores the connection string for the RDS cluster.
The secret stored inside is formatted as: postgresql://<username>:<password>@<endpoint>:<port>/<database>
EOT
value = aws_secretsmanager_secret.connection_string.arn
}

output "root_credentials" {
description = "A map containing the username and password for the root user of the RDS cluster. Caution: This output will display the password in plain text."
value = {
username = aws_rds_cluster.this.master_username
password = data.aws_secretsmanager_secret_version.root_password.secret_string
}

sensitive = true
}
10 changes: 9 additions & 1 deletion variables.tf
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
variable "name" {
type = string
description = "Determines naming convention of assets. Generally follows DNS naming convention."
description = "Determines naming convention of assets. Generally follows DNS naming convention. Service name or abbreviation."
}

variable "database_name" {
type = string
description = "Name of the default database to create"
default = "main"
}

variable "vpc_id" {
Expand Down Expand Up @@ -49,4 +50,11 @@ variable "db_cluster_parameter_group_name" {
variable "instance_class" {
type = string
description = "Instance class"
default = "db.t4g.medium"
}

variable "deletion_protection" {
type = bool
description = "Enable deletion protection. DO NOT DISABLE IN PRODUCTION, THIS IS ONLY FOR TESTING."
default = true
}

0 comments on commit 78d7310

Please sign in to comment.