Tutorials: Using AWS SSM Session Manager Port Forwarding to Connect to Private VPC Resources
A common issue people run into when using AWS is how to connect to private resources such as RDS databases and EC2 instances from their local machine without exposing their resources to the public internet. In this tutorial, I will show you how to use AWS SSM Port Forwarding to connect to a private RDS database without the need for a public database, a public facing bastion instance, or a VPN.
What is AWS SSM Session Manager?
AWS Systems Manager Session Manager is a fully managed tool that's part of the AWS Systems Manager (aka SSM) service which is used to manage EC2 instances, on-premises servers, and any other virtual machines (VMs). The Session Manager feature allows you to connect to your instances using an interactive browser-based shell accessible from the EC2 console, or from the AWS CLI
The main benefits of using Session Manager are:
- You don't need to open any inbound ports or manage bastion hosts or SSH keys, which leads to a reduced attack surface
- Your instances and databases don't need to have public IP addresses attached (which also saves money)
- All sessions are end to end encrypted by default, and can be encrypted using a custom CMK if needed for compliance
- Access to your instances is centrally managed using IAM permissions, so you can control which users or groups can use Session Manager and which access which managed instances they can access
- All Session Manager calls are logged to CloudTrail, and session specific connection logs can be stored in S3 for additional auditing and compliance
- And most recently, port forwarding and tunneling to remote hosts is now supported, which is what we'll be covering in this tutorial
What is AWS SSM Session Manager Port Forwarding to Remote Hosts? Why should I care?
On May 27, 2022, AWS announced support for port forwarding to remote hosts using Session Manager. This feature was a game changer, it enables you to create a secure tunnel between your local machine and a remote host (e.g. an RDS database or EC2 instance) without needing to make your instance or database publicly accessible, or set up a VPN into your AWS VPC network.
Session Manager is only one feature of AWS Systems Manager. AWS SSM includes a suite of tools that help you manage your AWS resources and benefit your operations, including automated instance patching via Patch Manager, automation workflows via Automation Documents and Run Commands, a centralized view of all your managed instances and other nodes, securely store parameters and secrets via SSM Parameter Store, and of course, connecting to your managed nodes via Session Manager without opening any inbound ports.
Scenario: Connecting to a Private RDS Database From Your Local Machine
In this tutorial, you are an AWS administrator or engineer who needs to connect to a private RDS database from your local machine to run some queries. You don't want to make your RDS database publicly accessible since that's against security best practices. You also aren't able to set up a VPN connection to your AWS VPC network or launch a public facing bastion instance due to your company's security policies. So what can you do? Enter AWS SSM Session Manager
Prerequisites
Before you begin, you should have the following:
- An AWS account that you have administrator permissions in to create the resources required for this tutorial
- AWS CLI that is set up and configured on the local system with the correct set of permissions. Refer to the Installing or updating to the latest version of the AWS CLI guide
- The AWS Session Manager plugin for the AWS CLI installed
- A database client installed locally, such as MySQLWorkbench, DBBeaver, or PGAdmin.
Step 1: Create the AWS Infrastructure
Note: if you've already configured an AWS VPC, SSM managed EC2 instance, and RDS database, you can skip this step.
You will need to create an AWS VPC with 2 public and 2 private subnets, configured with a NAT gateway and appropriate route tables, a Amazon Linux 2023 EC2 instance that is managed by SSM in a private subnet, and an RDS database in the same private subnets as the instance.
I've created a Cloudformation template that has everything you need to create the infrastructure for this tutorial. You can download the template from here: ssm-port-forwarding-tutorial.yaml
Once you've saved the .yml template file, follow the instructions in this document to create the Cloudformation Stack in the AWS account you will be using for this tutorial. Create Cloudformation Stack via the AWS Console
The Cloudformation template will create the following resources in your AWS account:
-
VPC and Networking Resources
- VPC with CIDR block 10.200.0.0/24
- 2 Public Subnets (10.200.0.0/26 and 10.200.0.64/26)
- 2 Private Subnets (10.200.0.128/26 and 10.200.0.192/26)
- Internet Gateway for public subnet internet access
- NAT Gateway for private subnet internet access
- Route Tables for both public and private subnets
-
EC2 Instance Bastion with SSM Agent preinstalled
- t4g.nano EC2 instance running Amazon Linux 2023 (ARM64) in private subnet
- SSM Agent pre-installed and configured via instance profile
- IAM Role and Instance Profile for SSM access
- Security Group for the bastion host with no inbound rules and all outbound network access allowed to 0.0.0.0/0
-
Database
- An Aurora Postgres RDS database cluster in the same private subnets
- Database Subnet Group for the Aurora cluster
- Secrets Manager secret containing database username and password
- Security Group for the RDS database allowing inbound access from bastion host on port 5432 (the PostgreSQL default port)
Figure 1: AWS SSM Port Forwarding Tutorial Architecture
🚨 Make sure to keep track of the bastion EC2 instance id and RDS Aurora Cluster endpoint, you'll need them in the next step.
If you used the Cloudformation template I provided earlier in Step 1, this information will be easily available in the AWS console in the Cloudformation stack outputs tab.
Step 2: Establish the Session Manager Port Forwarding Tunnel
Now that you've completed the prerequisites and have the necessary AWS infrastructure spun up, we can create the actual SSM port forwarding tunnel now to allow us to tunnel from our local machine to the private RDS database. We'll be doing this by using the AWS CLI and the aws ssm start-session
command with the AWS-managed StartPortForwardingSessionToRemoteHost
SSM automation document.
Open a terminal on your local machine and ensure the AWS CLI session is configured to the same AWS account and region your bastion instance and database are in.
Run the following command to establish the SSM port forwarding session to the RDS database endpoint which can be found in the AWS console or in the Cloudformation stack outputs.
🚨 Replace the <ssm-managed-instance-id>
and <rds-database-endpoint>
placeholders with your actual bastion EC2 instance id and RDS database endpoint (either the reader or writer endpoint).
aws ssm start-session \
--target <ssm-managed-instance-id> \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{"host":["<rds-database-endpoint>"],"portNumber":["5432"], "localPortNumber":["5432"]}' \
--region us-east-1 #change if not using us-east-1
# If you also happen to have a Postgres instance running locally, port 5432 may be in use.
# You can use a different local port number if needed, such as 5433 or 5434.
If you ran the command successfully, you should see output similar to the following (with your own instance id, database endpoint, and SessionId):
aws ssm start-session \
--target i-08f8da02ef86a9019 \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters '{"host":["ssm-tutorial-auroradbinstance-xgbvrzhiwkl9.cxi22kwuecp7.us-east-1.rds.amazonaws.com"],"portNumber":["5432"], "localPortNumber":["5432"]}' \
--region us-east-1
Starting session with SessionId: esantana-2u3p56pdlverjceucba3gh3kh8
Port 5432 opened for sessionId esantana-2u3p56pdlverjceucba3gh3kh8.
Waiting for connections...
🚨 Leave this running until the end of the tutorial since closing this connection will kill the tunnel.
For the purposes of this tutorial, the remote host is our RDS database, but this can be any host/IP address/DNS name and port that your EC2 instance can connect to, such as another EC2 instance, an internal-facing ALB, or even an on-premises server.
For more information about this step, take a look at the Starting a session port forwarding to remote host documentation.
Step 3: Connect to the RDS Database
Now that the SSM port forwarding tunnel is active, you can connect to the RDS database from your local machine using your favorite database client. At this point, go grab your database name and your postgres username and password.
If you used the Cloudformation template from Step 1, the database will be named ssmPortForwardingTutorial
these credentials wil be located in AWS Secrets Manager under a secret that starts with AuroraSecret-
Instead of your database endpoint, you will use localhost
and the local port number you specified in the previous step, (e.g. localhost:5432
. instead of ssm-vpc-lab-auroradbcluster-4vlv20ckgp8b.cluster-cxi22kwuecp7.us-east-1.rds.amazonaws.com
)
For example, if you are using pgAdmin to connect to a PostgreSQL database, you can register a new server with the settings below and click save.
- Name: aws-ssm-tutorial
- Host name/address: localhost
- Port: 5432
- Maintenance database: ssmPortForwardingTutorial
- Username: postgres
- Password:
Confirm the connection by clicking on the server you just registered and you should see the database tables and schemas in the left pane.
If everything worked as expected, then congratulations! 🎉 You've successfully connected to a private RDS database using AWS SSM Session Manager Port Forwarding, without needing to expose your database to the public internet, set up a VPN, or exposing any inbound ports from your local machine!
Step 4: Cleaning Up
Once you're done with this tutorial, you can easily clean up all of the resources created in Step 1 by going to the AWS Cloudformation console, selecting the stack you created for this tutorial, and clicking the "Delete" button. This will permanently delete all of the resources created by the Cloudformation template, including the VPC, EC2 instance, RDS database, and Secrets Manager secret.
Make sure to monitor the delete status of the stack to make sure no errors come up. Some resources, such as the RDS database can take a few minutes to delete, so please be patient.
Conclusion
In this post, I showed you how to connect to remote servers inside of your private AWS VPC network using AWS Systems Manager Session Manager's port forwarding to remote hosts feature. While this tutorial used an RDS database as an example, you can use this same technique to remotely connect to any type of host from your local machine without needing to expose your AWS resources to the public internet or set up a VPN.
I hope you found this tutorial helpful and that you can use this technique to improve your security posture and make your AWS infrastructure easier to maintain. If you have any questions or feedback, feel free to send me an email at eduardo@teamsantana.com
Top comments (1)
Great article! Nicely done.