Skip to content

Applying security best practices to my architecture / web application via Terraform?

0

Hello! I've been working steadily on attempting to apply security features that further enhance the security of my infrastructure created using Terraform and AWS CloudFormation! In spite of my progress at working to improve it, I've found that there are number of areas I feel that I'm trying to address properly in order to integrate and apply to my infrastructure for a better secured infrastructure. If I sought to do the following:

  1. Enable VPC flow logging to my VPC
  2. Use KMS to apply customer managed Keys and encrypt S3 buckets, secrets from AWS Secret Manager, and Amazon FSx file system
  3. Apply cross-region replication and bucket versioning to S3 buckets
  4. Set default security group to restrict all traffic
  5. Employ enhanced monitoring for Amazon RDS for MySQL Server (do I need to make a new IAM role to allow this?)

What would be the particular steps I would need to take in order to fulfill apply these security measures? I have a good feeling the following initial coding is putting me in the right direction towards my goals, but I feel as though there are further configurations that must be incorporated and modified:

 # Enable enhanced monitoring for AWS RDS MySQL database
  monitoring_interval = 30
  monitoring_role_arn = aws_iam_role.rds_enhanced_monitoring.arn

   # Enable VPC flow logging to VPC  
resource "aws_flow_log" "centralized" {
  log_destination      = "arn:aws:s3:::centralized-vpc-flow-logs-<log_archive_account_id>" # Optionally, a prefix can be added after the ARN.
  log_destination_type = "s3"
  traffic_type         = "ALL"
  vpc_id               = var.vpc_id
  log_format           = local.custom_log_format_v5 # If you want fields from VPC Flow Logs v3+, you will need to create a custom log format.
  tags                 = {
    Name = "centralized_flow_log"
  }
}

   # Apply bucket versioning to S3 bucket
resource "aws_s3_bucket" "my_bucket" {
  bucket = "example-bucket"
}

resource "aws_s3_bucket_acl" "example" {
  bucket = aws_s3_bucket.my_bucket.id
  acl    = "private"
}

resource "aws_s3_bucket_versioning" "bucket_version" {
  bucket = aws_s3_bucket.my_bucket.id
  versioning_configuration {
    status = "Enabled"
  }
}

   # Create individual Terraform resources for destination bucket 
resource "aws_s3_bucket" "destination" {
  bucket = "tf-bucket-destination"
  region = "us-west-2"

  versioning {
    enabled = true
  }
}

   # Enable replication for cross-region
replication_configuration {
    role = "${aws_iam_role.ps-db-backups-replication.arn}"

    rules {
      id     = "ps-db-backups-replication"
      status = "Enabled"

      destination {
  bucket        = "${aws_s3_bucket.destination.arn}"
  storage_class = "STANDARD"
        }
      }
    }
  }

# Encrypt Amazon FSx with KMS via customer managed key
resource "aws_fsx_windows_file_system" "example" {
  active_directory_id = aws_directory_service_directory.example.id
  kms_key_id          = aws_kms_key.example.arn
  storage_capacity    = 300
  subnet_ids          = [aws_subnet.example.id]
  throughput_capacity = 1024
}

   # Create and manage my KMS key
resource "aws_kms_key" "example" {
  description             = "My encryption KMS key"
  enable_key_rotation     = true
  deletion_window_in_days = 30
  policy = jsonencode({
    Version = "2012-10-17"
    Id      = "key-default-1"
    Statement = [
      {
        Sid    = "Enable IAM User Permissions"
        Effect = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
        },
        Action   = "kms:*"
        Resource = "*"
      },
      {
        Sid    = "Allow administration of the key"
        Effect = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/Alice"
        },
        Action = [
          "kms:ReplicateKey",
          "kms:Create*",
          "kms:Describe*",
          "kms:Enable*",
          "kms:List*",
          "kms:Put*",
          "kms:Update*",
          "kms:Revoke*",
          "kms:Disable*",
          "kms:Get*",
          "kms:Delete*",
          "kms:ScheduleKeyDeletion",
          "kms:CancelKeyDeletion"
        ],
        Resource = "*"
      },
      {
        Sid    = "Allow use of the key"
        Effect = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/Bob"
        },
        Action = [
          "kms:DescribeKey",
          "kms:Encrypt",
          "kms:Decrypt",
          "kms:ReEncrypt*",
          "kms:GenerateDataKey",
          "kms:GenerateDataKeyWithoutPlaintext"
        ],
        Resource = "*"
      }
    ]
  })
}

# Encrypting AWS Secrets Manager via KMS with a customer managed key
resource "aws_secretsmanager_secret" "example" {
  name = "example"
  kms_key_id = aws_kms_key.example.arn
}
3 Answers
1

I suggest you use open source tools such as checkov or tfsec to scan your terraform code for misconfigurations and to discover security risks.

You can also incorporate such tools in your CI/CD pipeline and follow the "Shift left" DevSecOps practice to fail/abort IaC deployment if security findings above a certain severity level are discovered (e.g., HIGH or CRITICAL).

AWS
EXPERT

answered 2 years ago

EXPERT

reviewed 2 years ago

0

Hello,

Your terraform configuration looks almost correct. Additionally use KMS to apply customer managed Keys and encrypt S3 bucket and you need to create a new IAM role to allow enhanced monitoring, and cross-region replication setup is mostly correct, but you need to ensure that the IAM role has the correct policy attached for replication.

# Enable VPC Flow Logging to VPC
resource "aws_flow_log" "centralized" {
  log_destination      = "arn:aws:s3:::centralized-vpc-flow-logs-<log_archive_account_id>" # S3 bucket ARN for storing logs
  log_destination_type = "s3"
  traffic_type         = "ALL" # Capture all traffic
  vpc_id               = var.vpc_id
  log_format           = local.custom_log_format_v5 # Custom log format
  tags                 = {
    Name = "centralized_flow_log"
  }
}

# Apply bucket versioning to S3 bucket
resource "aws_s3_bucket" "my_bucket" {
  bucket = "example-bucket"
}

resource "aws_s3_bucket_acl" "example" {
  bucket = aws_s3_bucket.my_bucket.id
  acl    = "private" # Ensure the bucket is private
}

resource "aws_s3_bucket_versioning" "bucket_version" {
  bucket = aws_s3_bucket.my_bucket.id
  versioning_configuration {
    status = "Enabled" # Enable versioning
  }
}

# Create individual Terraform resources for destination bucket for replication
resource "aws_s3_bucket" "destination" {
  bucket = "tf-bucket-destination"
  region = "us-west-2"

  versioning {
    enabled = true
  }
}

# IAM Role for S3 bucket replication
resource "aws_iam_role" "s3_replication_role" {
  name = "s3_replication_role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Action    = "sts:AssumeRole",
        Effect    = "Allow",
        Principal = {
          Service = "s3.amazonaws.com"
        }
      }
    ]
  })
}

# IAM Role Policy for S3 bucket replication
resource "aws_iam_role_policy" "s3_replication_policy" {
  role = aws_iam_role.s3_replication_role.id

  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Action = [
          "s3:GetReplicationConfiguration",
          "s3:ListBucket",
          "s3:GetObjectVersion",
          "s3:GetObjectVersionAcl",
          "s3:GetObjectVersionTagging"
        ],
        Effect   = "Allow",
        Resource = [
          "arn:aws:s3:::example-bucket",
          "arn:aws:s3:::example-bucket/*"
        ]
      },
      {
        Action = [
          "s3:ReplicateObject",
          "s3:ReplicateDelete",
          "s3:ReplicateTags",
          "s3:GetObjectVersionForReplication",
          "s3:GetObjectVersionAclForReplication",
          "s3:GetObjectVersionTaggingForReplication"
        ],
        Effect   = "Allow",
        Resource = "arn:aws:s3:::tf-bucket-destination/*"
      }
    ]
  })
}

# Enable replication for cross-region
resource "aws_s3_bucket_replication_configuration" "example" {
  bucket = aws_s3_bucket.my_bucket.id

  role = aws_iam_role.s3_replication_role.arn

  rules {
    id     = "replication-rule"
    status = "Enabled"

    destination {
      bucket        = aws_s3_bucket.destination.arn
      storage_class = "STANDARD"
    }
  }
}

# Encrypt Amazon FSx with KMS via customer managed key
resource "aws_fsx_windows_file_system" "example" {
  active_directory_id = aws_directory_service_directory.example.id
  kms_key_id          = aws_kms_key.example.arn
  storage_capacity    = 300
  subnet_ids          = [aws_subnet.example.id]
  throughput_capacity = 1024
}

# Create and manage my KMS key
resource "aws_kms_key" "example" {
  description             = "My encryption KMS key"
  enable_key_rotation     = true
  deletion_window_in_days = 30
  policy = jsonencode({
    Version = "2012-10-17"
    Id      = "key-default-1"
    Statement = [
      {
        Sid    = "Enable IAM User Permissions"
        Effect = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
        },
        Action   = "kms:*"
        Resource = "*"
      },
      {
        Sid    = "Allow administration of the key"
        Effect = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/Alice"
        },
        Action = [
          "kms:ReplicateKey",
          "kms:Create*",
          "kms:Describe*",
          "kms:Enable*",
          "kms:List*",
          "kms:Put*",
          "kms:Update*",
          "kms:Revoke*",
          "kms:Disable*",
          "kms:Get*",
          "kms:Delete*",
          "kms:ScheduleKeyDeletion",
          "kms:CancelKeyDeletion"
        ],
        Resource = "*"
      },
      {
        Sid    = "Allow use of the key"
        Effect = "Allow"
        Principal = {
          AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/Bob"
        },
        Action = [
          "kms:DescribeKey",
          "kms:Encrypt",
          "kms:Decrypt",
          "kms:ReEncrypt*",
          "kms:GenerateDataKey",
          "kms:GenerateDataKeyWithoutPlaintext"
        ],
        Resource = "*"
      }
    ]
  })
}

# Apply encryption to S3 bucket using KMS
resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
  bucket = aws_s3_bucket.my_bucket.id

  rule {
    apply_server_side_encryption_by_default {
      kms_master_key_id = aws_kms_key.example.arn
      sse_algorithm     = "aws:kms"
    }
  }
}

# Encrypting AWS Secrets Manager via KMS with a customer managed key
resource "aws_secretsmanager_secret" "example" {
  name      = "example"
  kms_key_id = aws_kms_key.example.arn
}

# Set default security group to restrict all traffic
resource "aws_security_group" "default" {
  name        = "default_security_group"
  vpc_id      = var.vpc_id
  description = "Default security group with no traffic allowed"

  ingress {
    from_port   = 0
    to_port     = 0
    self      = true
    protocol    = "-1"
  }
}

# Create IAM role for RDS enhanced monitoring
resource "aws_iam_role" "rds_enhanced_monitoring" {
  name = "rds_enhanced_monitoring_role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Action    = "sts:AssumeRole",
        Effect    = "Allow",
        Principal = {
          Service = "monitoring.rds.amazonaws.com"
        }
      }
    ]
  })
}

# Attach policy to IAM role for RDS enhanced monitoring
resource "aws_iam_role_policy" "rds_enhanced_monitoring_policy" {
  role = aws_iam_role.rds_enhanced_monitoring.id

  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Action = [
          "cloudwatch:PutMetricData",
          "logs:CreateLogStream",
          "logs:PutLogEvents"
        ],
        Effect   = "Allow",
        Resource = "*"
      }
    ]
  })
}

# Enable enhanced monitoring for AWS RDS MySQL database
resource "aws_db_instance" "example" {
  identifier             = "example"
  engine                 = "mysql"
  instance_class         = "db.t2.micro"
  username               = "foo"
  password               = "barbarbar"
  allocated_storage      = 10

  monitoring_interval    = 30 # Monitoring interval in seconds
  monitoring_role_arn    = aws_iam_role.rds_enhanced_monitoring.arn # ARN of the monitoring role
}

EXPERT

answered 2 years ago

0

A bit late to the party, but still... The suggestions above about checkov and tfsec are good for scanning your Terraform code before deployment. To answer your specific question about what to configure:

For S3 - enable server-side encryption with KMS (aws:kms), turn on versioning, block all public access (block_public_acls, block_public_policy, ignore_public_acls, restrict_public_buckets all set to true), and enable access logging to a separate bucket.

For RDS - enable storage encryption, set multi-az to true for production, configure automated backups with at least 7 days retention, enable deletion protection, and restrict the security group to only your application subnets.

For VPC - enable flow logs to CloudWatch or S3 for audit trail, use private subnets for your application and database tiers, and restrict the default security group to deny all traffic.

One thing the scanning tools will not do for you - they detect problems but can not fix them. If you want these settings enforced by default so developers do not need to remember each one, consider baking them directly into your Terraform modules. A module that has encryption and logging enabled by default is more reliable than scanning for it after the fact.

answered 3 months ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.