How to Connect to an EC2 Instance in a Private Network Using a Bastion Host

Table of contents

Introduction
In cloud environments like AWS, it's a best practice to place your application servers in private subnets to prevent direct access from the internet. But then, how do you connect to those private instances for troubleshooting or configuration?
This is where a Bastion Host comes in, a secure jump server that provides SSH access to private instances.
In this post, you’ll learn step-by-step how to:
Configure your VPC, security groups and route tables
Set up a bastion host
SSH into a private EC2 instance via the bastion host
Prerequisites
Before we start, ensure the following:
You have an AWS account
You’re familiar with basic VPC, EC2, and SSH concepts (optional, but helpful)
Architecture Overview
You’ll create a simple VPC setup like this:
STEP 1: Create Your VPC and Subnets
Create a VPC
Enter a CIDR block (e.g., 10.0.0.0/16 the range of IP addresses your VPC will have)
Give your VPC a name (always ensure you tag your resources)
Create 2 Subnets:
Public Subnet (e.g., 10.0.1.0/24)
Private Subnet (e.g., 10.0.2.0/24)
Create an Internet Gateway (IGW):
This will give internet access to the public subnets within the VPC and then in the action’s menu.
Once the Internet Gateway is created, click on the “Actions” button on the top-right, and attach to VPC. Ensure you select your created VPC, your tags should make this easier to find.
Update route table
Create a route to destination (0.0.0.0./0) and the target should be the Internet Gateway you Attached to the VPC.
Create the Private Route table
What makes a route table private is the absence of a route to outside traffic through an Internet Gateway. This is what’d needed for the private subnet.
Create a route table association for the Private Subnet.
NOTE: By default, both subnets are implicitly associated to the VPC’s main route table, and this would give them internet access, but only one requires it, so another route table will be made for the private subnet.
By the time you’re done with the above steps, you have the following in your VPC resource map.
Step 2: Launch the Bastion Host (Public Instance)
Launch an EC2 instance (Amazon Linux/Ubuntu) in the public subnet.
Assign it a public IP
Associate it with a security group that:
- Allows SSH (port 22) from your IP
Use a key pair for SSH access, it will automatically download. Ensure you take note of its location.
Step 3: Launch the Private Instance (Private Subnet)
Launch an EC2 instance in the private subnet
Do not assign a public IP
Configure your security group to:
- Allows SSH (port 22) from the bastion host’s security group
Select a key-pair, just as in the previous instance.
Step 4: SSH Into the Private Instance via Bastion
Now let’s connect:
SSH into the Bastion Host.
Go the Instances dashboard and select the instance your created
Go to the Connect option on the top-right and click on SSH client. Now copy the command and paste it in your terminal as such.
ssh -i "test.pem" ec2-user@54.165.42.99
NOTE: test.pem
was what I saved my key-pair as.
If you’re on a Linux distro such as ubuntu you have to manage the permissions of your key-pair using the following command.
chmod 400 "test.pem"
Once you’ve SHH’d into the into the instance, you should get something like below
Security concerns: The key-pair and instances used were strictly created for this project and have since been deleted.
SSH into the Private EC2 Instance.
NOTE: The key-pair for your private instance was download unto your local PC, so your instance does not have it. In order to SSH into your private subnet, we’d need to copy the content of the key pair to your Bastion Host.
Open your key-pair file for your private instance saved to your local PC and copy all of its contents using
CTRL + a
andCTRL + c
Now go to your Bastion Host, presumably still SHH’d into and perform the following:
vim test.pem
- A screen like such below will come up
Press
i
on your keyboard to edit, and then paste your key-pair copied in previous steps.Now to save the file press
ESC
on your keyboard and type:wq!
to save and exit the vim editor.To confirm if your keypair saved, use the following to view its contents
cat test.pem
Now modify the permissions for the key pair
chmod 400 "test.pem"
Now, just like your bastion host, go to the EC2 dashboard, and click on “connect” on the top-right corner, click ln the “SSH Client” and then copy the command. Paste it into your Bastion Hosts terminal.
ssh -i "test.pem" ec2-user@10.0.0.11
This should display the above, indicating you’ve SSH’d into your private subnet
CONGRATULATIONS!!🥂
You have successfully and securely accessed your private instance using a bastion host, now you can configure your instance how you want
NOTE: This method does not give your private instance internet access to download resources.
NOTE: Using Session Manager (SSM) is better for enhanced, will be discussed in a future post
COMMONLY FACED ERRORS
Timeout When SSHing to Private EC2
Cause: Private security group doesn't allow SSH from Bastion hosts security group
Fix: Allow port 22 from Bastion's Security group in Private EC2's security group
Permission Denied (publickey)
Cause: Wrong key or wrong permissions
Fix: Use correct
.pem
file and/or runchmod 400 my-key.pem
Can’t Reach Bastion Host
Cause: No public IP, IGW, or incorrect route table
Fix: Ensure the bastion host has a public IP, IGW attached to the VPC, and route to 0.0.0.0/0 on the route table.
BEST PRACTICES
Use key rotation and minimal SSH access
Set up CloudWatch for monitoring the bastion
Consider using Session Manager (SSM) for enhanced security (no SSH needed)
Restrict bastion access with security groups and NACLs
CONCLUSION
Using a bastion host is a secure and controlled way to access EC2 instances in a private network. This setup is essential for production-grade cloud environments.
Stay tuned for a future post where we’ll automate this entire setup using Terraform or CloudFormation!
Subscribe to my newsletter
Read articles from VICTOR ILIYA directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by