Secure Access To Databases With Bastion Hosts And AWS Session Manager

This blog talks about securing access to databases.
Mohseen Shaikh_avatar
Mohseen Shaikh
Mar 01, 2022 | 6 min read

In Today's Software Development Industry, Owning A Database Is Given. Most Of The Time, Data Stored In The Database Is Sensitive. This Blog Talks About How We Can Secure Databases And Connect To Them With Good Security Practices Using AWS Session Manager And Bastion Hosts.

One of the easiest ways to make a database accessible to users is to provision it in a public subnet. But the disadvantage is that our database gets exposed to the internet, thus increasing security risks.

To avoid this, we need to provision our database in a private subnet which restricts access from the internet. Now the question comes: how we will access it from our local system, if needed? Here Bastion hosts come into the picture.

Bastion Hosts

A bastion host is a server that acts as an intermediate between the user and resources provisioned in private subnets. It adds an extra layer of security and acts as a barrier for private resources like databases. We provision EC2 instance in the public subnet and use that to connect to our RDS instance in the private subnet.

A user will connect to the bastion first, and from there, they would connect to the database.

rds_bastion_architecture

We have secured our RDS by adding bastion hosts in-between RDS and the user. But it is also crucial to secure our connection to the bastion hosts.

To make the connection to bastion host secure, we will use AWS Session Manager, and AWS EC2 Instance Connect.

AWS Systems Manager Session Manager

We can connect to our bastion/EC2 instance by configuring and using Session Manager. Each connection to an EC2 instance is called a session.

Key advantages of using AWS Session Manager:

  • No open ports to the internet.
  • Authentication/login is done using AWS credentials and IAM Role.
  • Logs can be stored in Cloudwatch or S3.

The Session Manager provides real-time monitoring and control of all sessions to prevent and detect malicious activities. Since there are no open ports available to the internet, it is less likely to get compromised. Session history provides visibility around who accessed the instances.

session_manager

Before we start using AWS Session Manager, we need to set up AWS Systems Manager Session Manager CLI.

To start a session, run the following command:

aws ssm start-session --target <instance_id>

~ aws ssm start-session --target i-7l8u2pkN21m01j9

Starting session with SessionId: treece-048a3bf2269ad7caf

sh-4.2$ psql -h 10.0.3.88 -U test_user -d postgres_db
Password for user test_user:

SSL connection (cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256)
Type "help" for help.

postgres_db => CREATE TABLE users (id INT, Name VARCHAR(30));
CREATE TABLE

SSH Tunnel

SSH stands for Secure Shell or Secure Socket Shell. It is a cryptographic network protocol that allows two computers to communicate. It also allows sharing data over an insecure network such as the internet.

At this point, we can remotely log into the bastion with Session Manager, and access our database instance through the bastion host. But for some use-cases we may want the database to be accessible on a port of our local machine; For that, we need to leverage the SSH tunnel. It will also help us to use database GUI tools like pgAdmin.

To create the SSH connection, we need to modify the ~/.ssh/config file with the below snippet on our local machine so that servers starting with i- will proxy their connections through the aws ssm start-session command.

host i-*
ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"

We need a private key and a public key configured to establish an SSH connection. Here is the documentation for the same.

We will send our public key to the bastion host, and use the private key to authenticate ourselves. This is done using AWS EC2 Instance Connect.

AWS EC2 Instance Connect

AWS EC2 Instance Connect helps us send our public key to the bastion host and provides a one-minute time window to establish an SSH connection. That doesn't mean our SSH connection will lose after one minute. It simply means we have one minute to perform authentication with the help of a public key and a private key. After successful authentication, the SSH connection will remain open till we want.

Command:

~ aws ec2-instance-connect send-ssh-public-key \
    --instance-id i-7l8u2pkN21m01j9 \
    --availability-zone us-east-1a \
    --instance-os-user ssm-user \
    --ssh-public-key file://$HOME/.ssh/id_rsa.pub

These required values are available on the AWS Console.

aws_instances

Run the below command to SSH into bastion host.

ssh -i $HOME/.ssh/id_rsa ssm-user@<instance_id>

If you are using the standard SSH key located at ~/.ssh/id_rsa, then you can shorten the command as ssh ssm-user@<instance_id>

~ ssh ssm-user@i-7l8u2pkN21m01j9
Last login: Sun Feb  6 15:17:00 2022 from localhost

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
18 package(s) needed for security, out of 57 available
Run "sudo yum update" to apply all updates.

Now that we have SSH access to the bastion, we can create an SSH tunnel with our RDS and access it on any suitable port. For example, let us use port 5000.

To create a tunnel we can simply run:

ssh ssm-user@<bastion_instance_id} -L <local_port>:<rds_hostname_endpoint>:<rds_remote_port>

~ ssh ssm-user@i-7l8u2pkN21m01j9 -L 5000:test-rds.cluster.us-east-1.rds.amazonaws.com:5432
Last login: Fri Feb 11 03:53:13 2022 from localhost

       __|  __|_  )
       _|  (     /   Amazon Linux 2 AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
22 package(s) needed for security, out of 62 available
Run "sudo yum update" to apply all updates.

And then, from a separate shell, we can access our database locally on port 5000 by running:

~ psql -h localhost -p 5000 -U test_user -d postgres_db

Password for user test_user:

SSL connection (cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256)
Type "help" for help.

postgres_db =>

Using tunnels through bastion hosts, we can access any resources residing in private networks without exposing them to the internet.

Concluding Thoughts

This blog gives us a better understanding of bastion hosts, AWS Systems Manager Session Manager, and AWS EC2 Instance Connect. Ultimately, using these together, we can secure our databases. I hope you find this blog informative and helpful. For more such technical blogs you can visit us at medly.tech.