How to SSH into EC2 Instances in AWS VPC Private Subnet via NAT Gateway/Bastion Host: A Complete Guide


2 views

When working with AWS VPCs containing private subnets, the typical architecture consists of:

  • Public subnet with NAT gateway/bastion host (your "nat" instance)
  • Private subnet(s) containing application servers ("destination" instances)
  • No public IP addresses assigned to private instances
  • Outbound internet access via NAT

Here are three proven methods to establish SSH connectivity:

Method 1: SSH Port Forwarding via Bastion Host

This is the most secure and recommended approach:

ssh -i your-key.pem -L 2222:private-instance-ip:22 ec2-user@bastion-public-ip

Then in another terminal:

ssh -i your-key.pem -p 2222 ec2-user@localhost

Method 2: SSH ProxyJump (OpenSSH 7.3+)

A cleaner approach using modern SSH features:

ssh -J ec2-user@bastion-public-ip ec2-user@private-instance-ip

Or configure in your ~/.ssh/config:

Host private-instance
  HostName private-instance-ip
  User ec2-user
  ProxyJump ec2-user@bastion-public-ip
  IdentityFile ~/.ssh/your-key.pem

Method 3: Instance Connect Endpoint (AWS-specific)

For AWS-native solution (requires VPC endpoints):

aws ec2-instance-connect send-ssh-public-key \
  --instance-id i-1234567890abcdef0 \
  --availability-zone us-west-2a \
  --instance-os-user ec2-user \
  --ssh-public-key file://my-key.pub
  • Always use SSH keys rather than passwords
  • Limit bastion host ingress to specific IP ranges
  • Consider using AWS Session Manager for fully managed access
  • Implement proper IAM policies for instance access

If connections fail, check these common problems:

  • Security group rules (inbound/outbound)
  • Network ACLs in the VPC
  • Route table configurations
  • Bastion host SSH service status
  • Key file permissions (chmod 400 your-key.pem)

When working with AWS VPC architectures, we commonly deploy instances in private subnets for security reasons. These instances don't have public IPs and rely on NAT gateways (or NAT instances) in public subnets for outbound internet access. The challenge arises when we need inbound SSH access to these private instances from outside the VPC.

To establish this connection, we need:

  • A NAT instance with both public and private IPs
  • Proper security group rules allowing SSH traffic
  • Port forwarding configuration on the NAT instance
  • Route table configurations pointing to the NAT

First, ensure your security groups allow the following:


# NAT instance security group (inbound rules)
Type: SSH
Protocol: TCP
Port Range: 22
Source: 0.0.0.0/0 (or your specific IP range)

# Private instance security group (inbound rules)
Type: SSH
Protocol: TCP
Port Range: 22
Source: [NAT instance private IP]

On your NAT instance (assuming Linux), configure SSH port forwarding:


# Edit sshd_config to enable GatewayPorts
sudo nano /etc/ssh/sshd_config
# Add or modify:
GatewayPorts yes
# Then restart SSH service
sudo service sshd restart

Create an SSH tunnel for each private instance:


# General format:
ssh -i your-key.pem -Nf -L [NAT_PUBLIC_IP]:[EXTERNAL_PORT]:[PRIVATE_INSTANCE_IP]:22 ec2-user@[NAT_PUBLIC_IP]

# Example for instance 10.0.1.5 using port 2201:
ssh -i nat-key.pem -Nf -L 0.0.0.0:2201:10.0.1.5:22 ec2-user@54.123.45.67

Now you can SSH directly to your private instance through the NAT:


ssh -i private-key.pem -p 2201 ec2-user@[NAT_PUBLIC_IP]

For multiple instances, create a script to set up multiple tunnels:


#!/bin/bash
declare -A instances=(
    ["2201"]="10.0.1.5"
    ["2202"]="10.0.1.6"
    ["2203"]="10.0.1.7"
)

for port in "${!instances[@]}"; do
    ssh -i nat-key.pem -Nf -L 0.0.0.0:$port:${instances[$port]}:22 ec2-user@54.123.45.67
done

For production environments, consider using AWS Systems Manager Session Manager instead of port forwarding for better security and auditing:


# Install SSM agent on private instances
sudo yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm
sudo systemctl enable amazon-ssm-agent
sudo systemctl start amazon-ssm-agent

# Connect via AWS CLI
aws ssm start-session --target [instance-id]
  • Ensure NAT instance has IP forwarding enabled: echo 1 > /proc/sys/net/ipv4/ip_forward
  • Verify route tables in private subnet point to the NAT instance
  • Check VPC flow logs if connections fail
  • Confirm security groups allow traffic in both directions

Remember to monitor your NAT instance's network bandwidth and CPU usage, as it becomes a critical path for all your SSH connections.