Featured image of post AWS Systems Manager Session Manager Port Forwarding

AWS Systems Manager Session Manager Port Forwarding

Introduction

In the previous post, we discussed how to use the AWS VPN client to connect to private subnets in a VPC. We used Entra ID to perform federated user-based authentication. This technique is useful for enterprises using federated authentication, but there is another way to achieve the same result using AWS Systems Manager Session Manager (SSM) and port forwarding.

Architecture

SSM Port Forwarding to DB

The diagram above shows the main components used for the SSM port forwarding session setup. The diagram shows the following components:

  • The bastion host EC2 instance that is located in the private subnet and that is used to initiate the port forwarding session with the attached IAM role
  • The EC2 instance security group that allows inbound traffic only from the VPC CIDR range
  • The RDS instance in the private subnet that will be used as the target for the port forwarding session
  • The RDS security group that allows inbound traffic on the DB port (5432) only from the bastion host EC2 instance
  • The SSM session manager document that will be used to initiate the port forwarding session
  • The NAT gateway that will be used to route traffic from the private subnet to the internet
  • The development local machine that will be used to connect to the RDS instance

RDS DB Connection with SSM Port Forwarding

The following parameters are required for the port forwarding session:

  • AWS Region (e.g. eu-central-1)
  • EC2 instance id (e.g. i-04e29d6b82ca52fbc)
  • RDS host (e.g. mytestdb-instance-1.clzjs8sy98st.eu-central-1.rds.amazonaws.com)
  • RDS port (e.g. 5432)
  • RDS username (e.g. postgres)
  • RDS password (e.g. arn:aws:secretsmanager:eu-central-1:123456789012:secret:rds!)
  • RDS cluster identifier (e.g. cluster-506ac9d4-c6f0-5421-911f-85dec405f14a-A12ncL)
  • RDS DB name (e.g. mytestdb)

Install the session manager aws cli plugin locally and start a new session. Keep the session running and execute further commands from a new terminal session:

1
2
3
4
5
$ aws ssm start-session --target i-04e29d6b82ca52fbc --document-name AWS-StartPortForwardingSessionToRemoteHost --parameters '{"portNumber":["5432"],"localPortNumber":["1053"],"host":["mytestdb-instance-1.clzjs8sy98st.eu-central-1.rds.amazonaws.com"]}'

Starting session with SessionId: 429fec6e-29cc-422b-892f-9b8a6973c131-a5xit52zpidhlt7bb95rglflzy
Port 1053 opened for sessionId 429fec6e-29cc-422b-892f-9b8a6973c131-a5xit52zpidhlt7bb95rglflzy.
Waiting for connections...

Export the correct AWS region and RDS parameters in a new terminal session to connect from a local client:

1
2
3
4
5
6
export AWS_REGION="eu-central-1"
export RDS_HOST="localhost"
export RDS_PORT="5432"
export RDS_USERNAME="postgres"
export RDS_DB="mytestdb"
export PGPASSWORD="$(aws secretsmanager get-secret-value --secret-id 'arn:aws:secretsmanager:eu-central-1:123456789012:secret:rds!cluster-506ac9d4-c6f0-5421-911f-85dec405f14a-A12ncL' --query SecretString --output text | jq -r '.password')"

The PGPASSWORD environment variable is automatically picked up by psql when connecting to a PostgreSQL server. Connect using psql to localhost and port 5432 that match the forwarded session that was initiated:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ psql -h $RDS_HOST -p $RDS_PORT "dbname=$RDS_DB user=$RDS_USERNAME"
psql-18 (18.3 (Homebrew), server 17.7)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off, ALPN: postgresql)
Type "help" for help.

mytestdb=> \dt
             List of tables
Schema |    Name     | Type  |  Owner
--------+-------------+-------+----------
public | dummy_table | table | postgres
(1 row)

mytestdb=>

The connection was established, and the describe table command shows the proper output.

Conclusion

This post demonstrates how to use AWS Systems Manager Session Manager to establish a port forwarding session to a remote DB host. This is useful for scenarios where you need to connect to a remote DB instance from a local development machine for debugging or testing purposes. No ssh connection or public internet-facing endpoints are required for this scenario which makes it a practical solution with minimal overhead.

References: