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

VICTOR ILIYAVICTOR ILIYA
5 min read

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:

Bastion host Architecture

STEP 1: Create Your VPC and Subnets

  1. 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)

  2. Create 2 Subnets:

    • Public Subnet (e.g., 10.0.1.0/24)

    • Private Subnet (e.g., 10.0.2.0/24)

  3. 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.

  4. 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.

  5. 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.

  6. 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)

  1. Launch an EC2 instance (Amazon Linux/Ubuntu) in the public subnet.

  2. Assign it a public IP

  3. Associate it with a security group that:

    • Allows SSH (port 22) from your IP
  4. 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)

  1. Launch an EC2 instance in the private subnet

  2. Do not assign a public IP

  3. Configure your security group to:

    • Allows SSH (port 22) from the bastion host’s security group
  4. Select a key-pair, just as in the previous instance.

Step 4: SSH Into the Private Instance via Bastion

Now let’s connect:

  1. 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 and CTRL + c

    • Now go to your Bastion Host, presumably still SHH’d into and perform the following:

    •       vim test.pem
      
      1. A screen like such below will come up

  1. Press i on your keyboard to edit, and then paste your key-pair copied in previous steps.

  2. Now to save the file press ESC on your keyboard and type :wq! to save and exit the vim editor.

  3. To confirm if your keypair saved, use the following to view its contents

  4.    cat test.pem
    
  5. Now modify the permissions for the key pair

  6.    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

  1. 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

  2. Permission Denied (publickey)

    • Cause: Wrong key or wrong permissions

    • Fix: Use correct .pem file and/or run chmod 400 my-key.pem

  3. 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!

1
Subscribe to my newsletter

Read articles from VICTOR ILIYA directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

VICTOR ILIYA
VICTOR ILIYA