Securely connect to an Amazon RDS

Nathan (Nursultan) Bekenov - May 6 - - Dev Community

Hello there! In this post I am going to show you Terraform code example of how to create resources for secure connection to your DB in RDS cluster.


I am going to follow approach provided by AWS team in this article

Ok, let's figure out what resources we need to create.
I assume that you already have: VPC and RDS Cluster (if not yet then check out previous post ).

  • VPC endpoints (ssm, ssmmessages, ec2messages)
  • EC2 instance
  • Security Group for EC2 instance
  • IAM role and instance profile

I will put details after each part of the code. Also if you want to skip and jump right to the code then everything can be found in my repo

Below code creates VPC endpoints. Since we need 3 of them I am looping through the list. We also will need security group.



# ----------------
# VPC Endpoints
# ----------------
locals {
  endpoints = {
    "endpoint-ssm" = {
      name        = "ssm"
      private_dns = false
    },
    "endpoint-ssm-messages" = {
      name        = "ssmmessages"
      private_dns = false
    },
    "endpoint-ec2-messages" = {
      name        = "ec2messages"
      private_dns = false
    },
  }
}

resource "aws_vpc_endpoint" "endpoints" {
  for_each            = local.endpoints

  vpc_id              = module.vpc.vpc_id
  vpc_endpoint_type   = "Interface"
  service_name        = "com.amazonaws.${data.aws_region.current.name}.${each.value.name}"
  security_group_ids  = [aws_security_group.vpc_endpoint_sg.id]
  subnet_ids          = data.aws_subnets.private.ids
  private_dns_enabled = each.value.private_dns

}

# SG for VPC endpoints
resource "aws_security_group" "vpc_endpoint_sg" {
  name_prefix = "vpc-endpoint-sg"
  vpc_id      = module.vpc.vpc_id
  description = "security group for VPC endpoints"

  ingress {
    from_port   = 0
    to_port     = 65535
    protocol    = "tcp"
    cidr_blocks = [module.vpc.vpc_cidr_block]
    description = "allow all TCP within VPC CIDR"
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
    description = "allow all outbound traffic from VPC"
  }

  tags = {
    Name = "vpc-endpoints-sg"
  }
}


Enter fullscreen mode Exit fullscreen mode

Now let's create EC2 instance and configure instance profile for it.



resource "aws_instance" "bastion_host" {
  ami                     = data.aws_ami.amazon_linux_2_ssm.id
  instance_type           = "t3.nano"
  subnet_id               = data.aws_subnets.private.ids[1]
  vpc_security_group_ids  = data.aws_security_groups.vpc_endpoint_sg.ids
  iam_instance_profile    = aws_iam_instance_profile.bastion_host_instance_profile.name
  user_data               = templatefile("ssm-agent-installer.sh", {})
  disable_api_termination = false
  metadata_options {
    http_endpoint = "enabled"
    http_tokens   = "required"
  }
  tags = {
    Name = "ssm-bastion-host"
  }
}

## Instance profile
resource "aws_iam_role" "bastion_host_role" {
  name = "EC2-SSM-Session-Manager-Role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          Service = "ec2.amazonaws.com"
        }
        Action = "sts:AssumeRole"
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "bastion_host_role_policy" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
  role       = aws_iam_role.bastion_host_role.name
}

resource "aws_iam_instance_profile" "bastion_host_instance_profile" {
  name = "EC2_SSM_InstanceProfile"
  role = aws_iam_role.bastion_host_role.name
}


Enter fullscreen mode Exit fullscreen mode

For Instance IAM role we are using existing policy AmazonSSMManagedInstanceCore that we will need in order to be able to use SSM.
Note that in user_data I am using shell script to install necessary packages.



#!/bin/bash
main(){
    #####Installing dependencies and packages ####
    echo "Installing Security Updates..."
    sudo yum -y update
    echo "Installing ec2 instance connect..."
    sudo yum install ec2-instance-connect
    echo "Installing latest aws-ssm agent..."
    sudo yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm
    sudo systemctl enable amazon-ssm-agent
    echo "Starting latest aws-ssm agent..."
    sudo systemctl start amazon-ssm-agent
    sudo amazon-linux-extras enable postgresql14
    sudo yum -y install postgresql
}
time main > /tmp/time-output.txt


Enter fullscreen mode Exit fullscreen mode

Once all infra is created follow the steps from my Readme
https://github.com/nbekenov/rds-aurora/blob/main/bastion_host/README.md

  • Create a remote port forwarding session ```

aws ssm start-session \   
--region us-east-1 \   
--target  \    
--document-name AWS-StartPortForwardingSessionToRemoteHost \    
--parameters host="",portNumber="5432" localPortNumber="5432"

- Connect to DB using PGAdmin

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cg6u777u8d7z3hlpx47r.png)


Use username and password from AWS Secrets Manager

---

In the next post I will be providing details on how to run DB Migrations using Flyway and Lambda Function
Enter fullscreen mode Exit fullscreen mode
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .