Connecting to RDS Database through Jumphost in AWS

This article connects to MySQL database provisioned in RDS using a jumphost or bastion host ec2 instance. In scenarios where direct access to the RDS instance is restricted to private networks, employing a Jump Host server hosted in a public subnet becomes essential. This technical article serves as a comprehensive guide for establishing a secure connection to an RDS database through a Jump Host server.

The following diagram shows the configuration when this tutorial completes.

Network Infrastructure Setup:

  1. VPC Creation: Start by creating a VPC named "jump-vpc" with cidr block 25.0.0.0/16.

  2. Subnet Creations: For this scenario, we will create one public subnet with cidr block 25.0.25.0/24. Second is private subnet for seating RDS database which requires to subnet created in two availablity groups.

  3. Route table: Create a route table and association for public subnet assigning to a internet gateway which will enable this subnet to access internet. Similarly, for private subnet, assign route to a NAT gateway which will indirectly enable internet access to the private network.

    Here is the terraform code for VPC, subnet, route table creation.

resource "aws_vpc" "jumpbox" {
  cidr_block = var.vpc-cidr
  tags = {
    Name = var.vpc
  }
}
# Create one subnet for jumpbox server
# create two private subnets for DB server
resource "aws_subnet" "jumpbox" {
  vpc_id                  = aws_vpc.jumpbox.id
  cidr_block              = var.jumpbox-cidr
  availability_zone       = "us-east-1a"
  map_public_ip_on_launch = true
  tags = {
    Name = var.jumpbox-subnet
  }
}
# These two subnets are for RDS
resource "aws_subnet" "rds" {
  count             = length(var.availability_zones)
  vpc_id            = aws_vpc.jumpbox.id
  cidr_block        = "25.0.${count.index + 1}.0/24"
  availability_zone = var.availability_zones[count.index]
  tags = {
    Name = "Private-Subnet-RDS"
  }
}
resource "aws_db_subnet_group" "rds" {
  name       = "rds-subnet-group"
  subnet_ids = aws_subnet.rds[*].id
  tags = {
    Name = "rds-subnet-group"
  }
}
  1. Security Group: security group is like firewall rules controlling access bothwise (ingress, egress) . This is defined at the VPC level, the mapping of SG happens at the resource level while create RDS, EC2 or any instance.

    For Jumpbox instance, we allow ingress port 22, 80 and egress full access to internet.

    For RDS instance, we allow ingress port 3306 (SQL listener port) from specific subnet (in this case above public subnet).

    Below TF code, beautifully shows public & private subnet.

    
     # This security group is for jumpbox
    
     resource "aws_security_group" "jumpbox" {
       name        = var.sg-jumpbox
       vpc_id      = aws_vpc.jumpbox.id
       description = "Allow all inbound traffic"
       ingress {
         from_port   = 80
         to_port     = 80
         protocol    = "tcp"
         cidr_blocks = ["0.0.0.0/0"]
       }
       ingress {
         from_port   = 22
         to_port     = 22
         protocol    = "tcp"
         cidr_blocks = ["0.0.0.0/0"]
       }
       ingress {
         description      = "allow http 8080 port"
         from_port        = 8080
         to_port          = 8080
         protocol         = "tcp"
         cidr_blocks      = ["0.0.0.0/0"]
         ipv6_cidr_blocks = ["::/0"]
       }
       egress {
         from_port        = 0
         to_port          = 0
         protocol         = -1
         cidr_blocks      = ["0.0.0.0/0"]
         ipv6_cidr_blocks = ["::/0"]
       }
    
       tags = {
         Name = var.sg-jumpbox
       }
     }
     # Security group for RDS to be created
     resource "aws_security_group" "rds" {
       name        = var.sg-rds
       vpc_id      = aws_vpc.jumpbox.id
       egress {
         from_port   = 0
         to_port     = 0
         protocol    = "-1"
         cidr_blocks = ["0.0.0.0/0"]
       }
       ingress {
         from_port   = 3306
         to_port     = 3306
         protocol    = "tcp"
         cidr_blocks = [aws_subnet.jumpbox.cidr_block]
       }
     }
    

    Jumpbox EC2 Instance Setup: Now that we got VPC, IG, Subnet, route-table, security group created, we will create a jumpbox server residing in public vpc. Below terraform code shows the configuration.

     resource "aws_instance" "jumpbox" {
       ami           = var.ami
       instance_type = var.instance_type
       key_name      = var.key_name
       user_data     = file(var.startup_script)
       tags = {
         Name = "jumpbox"
       }
       vpc_security_group_ids      = ["${aws_security_group.jumpbox.id}"]
       subnet_id                   = aws_subnet.jumpbox.id
       associate_public_ip_address = true
     }
    

    Above script, uses, vpc, subnet & security groups as defined above. User_data attribute is used to execute shell script as startup. key_name attribute enhance access security using public/private keys.

Create RDS instance using Terraform script. Below Terraform code, helps to create a RDS server with MySQL. Here is the configuration.

resource "aws_db_instance" "rds" {
  identifier           = "rds-mysql-instance"
  allocated_storage    = 20
  engine               = "mysql"
  engine_version       = "5.7.41"
  instance_class       = "db.t3.micro"
  db_name                = "myDB"
  username             = "admin"
  password             = "mypassword"
  vpc_security_group_ids = [aws_security_group.rds.id]
  db_subnet_group_name    = aws_db_subnet_group.rds.id
  multi_az             = false
  skip_final_snapshot = true
 }

In the above code snippet, db_subnet_group_name is a group of subnets in atleast in two availability zones. Refer to subnet snippet for mapping aws_db_subnet_grup with private subnet that we have configured. multi_az can be false if we donot want database created in two zones. Remaining attributes are straight forward, terraform script simplifies RDS DB creation to a large extent.

Connecting to RDS through Jumbox: After implementing above configuration.

ssh to jumpbox
# install MySQL client on Ubuntu
$ apt-get install mysql-client
# connect to RDS using Mysql client
$ mysql -h <refer_to_rds_host name> -P 3306 -u admin -p
password:
mysql:> show databases;
mysql:> use myDB;  # refer to tf code db_name creation.

Conclusion: Establishing a secure connection to an RDS database through a Jump Host server hosted in a public subnet is a fundamental practice for maintaining data integrity and confidentiality in cloud environments. By following the guidelines outlined in this technical article, organizations can ensure robust security measures while enabling seamless access to their RDS instances.