Serverless Development

Connect to AWS RDS Securely Without SSH Keys

Toss the .pem files in the trash

Connect to AWS RDS Securely Without SSH Keys

By
Marcelo Andrade
December 11, 2020

Introduction

Raise your hand if you're already having trouble connecting to a remote database running in a Private Subnet. In almost every company, you will probably need to speak with the SysAdmin, share your SSH key to be inserted inside the bastion host machine, granting to you the permission to have the tunnel working and connect directly from your machine.

This article will help you to get rid of this problematic implementation and demonstrates how to have access over SSH in an EC2 Bastion instance having no previous SSH key installed on it, no ports opened and even no public IP address configured, using the AWS Systems Manager Agent.

Resources

Considering that you already have your Database and Private Subnet created, here is what we are going to use:

EC2 Instance

An EC2 instance using Amazon Linux as AMI. The Amazon Linux already came with the AWS SSM Client installed on it and we are going to need it. You should use another AMI but knowing that you will need to handle the agent installation. Besides it we need to guarantee that the InstanceProfile has permission to assume arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore policy role;

IAM User

Create an IAM User with ssm:StartSession, ec2-instance-connect:SendSSHPublicKey and ec2:DescribeInstances  permissions. We are going to output the Access and Secret Key value of this user to the SLS Pro Dashboard;

4x VPC Endpoints

As we are dealing with a Private Subnet, we are going to need 4 VPC's endpoints configured to make it work as expected. 3 of this endpoints have the type Interface powered by AWS PrivateLink. Creating Interface endpoints incurs additional costs and you can check the pricing here;

Route Table

The last thing we should have is a exclusive Route Table configured for this service, associated to the same Private Subnet that we have the database running.

How it works

We are going to generate a temporary SSH key and inject it to the Bastion instance with the help of Amazon EC2 Instance Connect. And then for connecting the instance, we'll use AWS Systems Session Manager configuring SSH client with a specific proxy command.

If you want more information about it you should check these links:

Let's do this

You can get all the files used to get it done right here.

Requirements

  • You need to have AWS CLI already installed in your machine;
  • You need to have AWS Credentials already configured in your machine;
  • You need to have AWS Session Manager Plugin already installed in your machine; Docs

On Mac:

  
  curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/mac/sessionmanager-bundle.zip" -o "sessionmanager-bundle.zip"
  unzip sessionmanager-bundle.zip
  sudo ./sessionmanager-bundle/install -i /usr/local/sessionmanagerplugin -b /usr/local/bin/session-manager-plugin
  

Verify:

  
  session-manager-plugin
  

Update your ~/.ssh/config:

If you are using Windows and have git installed, the path is probably c:\\Program Files\\Git\\etc\\ssh\\ssh_config. Follow the instructions below.

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

source

Setup Variables in Serverless Pro Dashboard:

  
  AWS_REGION
  SSM_BASTION_VPC_ID
  SSM_BASTION_CIDR_IP
  SSM_BASTION_SUBNET_ID_1
  

Deploy The Bastion

  
  serverless deploy --stage  -v
  

Bastion Connection Script

You are going to need to have one tab of your terminal opened exclusively for this execution.

  
  serverless login
  

This will make sure that your terminal is authenticated with the Serverless Pro Dashboard.

Next we need to make a connection to the RDS database.

  
  sh connect.sh 
    --localport 2345
    --rdsurl xxxxxxxxxxx.xxxxxxxxxxxx.us-east-2.rds.amazonaws.com
    --rdsport 3306
    --slsorg xxxxxxxxxxxxx
    --slsapp xxxxxxxxxx
    --slsservice xxxxxxxxxx
    --slsstage xxxxxxxxxx
  

There are a series of command line arguments being passed in. Below is a description about each one.

Parameters:

  • --localport [ Your tunnel local port ]
  • --rdsurl [ The RDS Endpoint, located at AWS Console -> RDS -> Databases -> Your Database -> Connectivity & security ]
  • --rdsport [ The RDS Port, located at AWS Console -> RDS -> Databases -> Your Database -> Connectivity & security ]
  • --slsorg [ Your Serverless Dashboard Pro Org name ]
  • --slsapp [ Your Serverless Dashboard Pro App name ]
  • --slsservice  [ Your Serverless Dashboard Pro Service name ]
  • --slsstage  [ Your Serverless Dashboard Pro Org stage ]

If we pass the proper values, we should see the following result in our terminal.

https://user-images.githubusercontent.com/232648/98142462-951c5b00-1ea6-11eb-9d8b-e42d13a9113f.png

RDS Connection

To connect to the database you should use localhost as the Host name.

For the port, you should use the same port as you use set for the connect.sh script.

The credentials equal what you've configured for your RDS database in the IAC.

https://user-images.githubusercontent.com/232648/98142520-a82f2b00-1ea6-11eb-8975-b6b600c1cdbc.png

Conclusion

It's much safer and easier to share a connection script with your team and grant them access to your database instance via IAM credentials versus managing SSH keys. Besides the simplicity, you are also going to have all the EC2 instance connections logged to AWS CloudTrail so that you can audit them easily.

A common security issue in the past occurred when a .pem file was accidentally leaked. This allowed anyone with access to SSH into the EC2 instance that had connection with your private database.

All though the SSH Tunnel approach was more secure than the alternative public database, it still had issues.

With this pattern and this article you should no longer face these kind of issues and have peace of mind that you're connecting to RDS in a secure way.