How to Establish Secure SSH Tunnel for Remote PostgreSQL Access via AWS Bastion Host


2 views

When working with cloud infrastructure, we often need to access private database instances like PostgreSQL RDS that aren't publicly exposed. The standard approach involves using a bastion host as a jump server, but the tunneling mechanics can be tricky to get right.

Let's analyze your current configuration:

  • Bastion host: example.compute-1.amazonaws.com
  • Private PostgreSQL: postgres.example.us-east-1.rds.amazonaws.com:5432
  • Authentication: SSH key (key.pem)

The issue stems from creating the tunnel after already being inside the bastion. The -L flag binds to localhost by default, making it only accessible from the bastion itself.

You should establish the tunnel from your local machine to the bastion, which then forwards traffic to RDS:

ssh -i key.pem -N -L 5433:postgres.example.us-east-1.rds.amazonaws.com:5432 ec2-user@example.compute-1.amazonaws.com

Key parameters:

  • -N: Don't execute remote commands (just forward ports)
  • -L 5433:...: Bind local port 5433 to remote PostgreSQL
  • Different local port avoids conflicts with local PostgreSQL

After establishing the tunnel, test the connection from your local machine:

psql -p 5433 -h localhost -U postgres

For production use, consider these enhancements:

ssh -i key.pem -f -N -L 5433:postgres.example.us-east-1.rds.amazonaws.com:5432 \
  -o ServerAliveInterval=60 \
  -o ExitOnForwardFailure=yes \
  ec2-user@example.compute-1.amazonaws.com

Security group requirements:

  • Bastion: Allow SSH (22) from your IP
  • RDS: Allow 5432 from bastion's security group
  • No need to expose 5432 on bastion

If connections fail:

  1. Verify SSH connectivity to bastion alone first
  2. Check RDS security group allows the bastion
  3. Test network connectivity from bastion to RDS
  4. Confirm PostgreSQL is configured to accept connections

For frequent access, add to ~/.ssh/config:

Host aws-bastion
  HostName example.compute-1.amazonaws.com
  User ec2-user
  IdentityFile ~/path/to/key.pem
  LocalForward 5433 postgres.example.us-east-1.rds.amazonaws.com:5432

Then simply run:

ssh -fN aws-bastion

When working with cloud infrastructure, you often need to access private database instances (like AWS RDS) that aren't publicly accessible. A common pattern is to use a bastion host (jump server) as an entry point to your VPC. Here's the scenario:

  • Bastion host: example.compute-1.amazonaws.com (publicly accessible)
  • Private PostgreSQL RDS: postgres.example.us-east-1.rds.amazonaws.com:5432

While you can SSH into the bastion and then connect to the database, you might want direct access from your local machine for development tools.

The correct approach is to establish the SSH tunnel from your local machine, not from within the bastion. Here's the proper command structure:

ssh -i key.pem -N -L [local_port]:[rds_endpoint]:[rds_port] ec2-user@[bastion_host]

Key flags:

  • -N: Don't execute remote commands (just forward ports)
  • -L: Local port forwarding

For your specific case, run this on your local machine:

ssh -i key.pem -N -L 5433:postgres.example.us-east-1.rds.amazonaws.com:5432 ec2-user@example.compute-1.amazonaws.com

Notice we're using port 5433 locally to avoid conflicts if you have a local PostgreSQL server running. You can then connect using:

psql -h localhost -p 5433 -U postgres

While you mentioned opening port 5432 on the bastion, this isn't actually necessary for SSH tunneling. The connection happens over SSH (port 22). The security groups should be configured as:

  • Bastion: Allow inbound SSH (port 22) from your IP
  • RDS: Allow inbound PostgreSQL (port 5432) from the bastion's security group

For development, you might want a more persistent solution. Consider using autossh:

autossh -M 0 -f -i key.pem -N -L 5433:postgres.example.us-east-1.rds.amazonaws.com:5432 ec2-user@example.compute-1.amazonaws.com

Key additions:

  • -M 0: Disable monitoring (or use a monitoring port)
  • -f: Run in background

If connections fail:

  1. Verify the bastion can connect to RDS (test from within the bastion)
  2. Check security group rules for both bastion and RDS
  3. Confirm your local firewall isn't blocking the local port
  4. Add -v flag to SSH for verbose debugging

For frequent use, add this to your ~/.ssh/config:

Host db-tunnel
  HostName example.compute-1.amazonaws.com
  User ec2-user
  IdentityFile ~/path/to/key.pem
  LocalForward 5433 postgres.example.us-east-1.rds.amazonaws.com:5432
  ServerAliveInterval 60
  ServerAliveCountMax 2

Then simply run:

ssh -N db-tunnel