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
}
2 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).

profile pictureAWS
EXPERT
answered 5 days ago
profile picture
EXPERT
reviewed 5 days 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
}

profile picture
EXPERT
answered 5 days ago