AWS Bastion Host: What It Is, Why It Matters, and How to Set It Up
AWS Bastion Host: What It Is, Why It Matters, and How to Set It Up
When you work with AWS, one of the first security principles you learn is that not every server should be directly accessible from the Internet.
Your application servers, internal services, and databases often live inside private subnets. But sooner or later, a DevOps engineer needs to connect to those private resources for troubleshooting, maintenance, deployments, or system administration.
So how do you access a private EC2 instance without giving it a public IP?
One traditional solution is a Bastion Host.
In this guide, we'll understand what a bastion host is, why it is important, how it works in AWS, how to configure one, and why AWS Systems Manager Session Manager is increasingly becoming a better alternative.
What Is a Bastion Host?
A Bastion Host is a specially configured server that acts as a controlled entry point into a private network.
In AWS, a bastion host is typically an EC2 instance deployed in a public subnet. Administrators connect to the bastion from a trusted network and then use it to access servers located in private subnets.
A simple architecture looks like this:
Internet
|
|
Trusted Admin IP
|
| SSH :22
v
+------------------+
| Bastion Host |
| Public Subnet |
+------------------+
|
| SSH :22
|
v
+------------------+
| Private EC2 |
| Private Subnet |
+------------------+
|
v
RDS
The important point is that the private EC2 instance does not need a public IP address.
Why Do We Need a Bastion Host?
Imagine you have an application server running in a private subnet:
Private EC2 10.0.2.10
The server doesn't have a public IP because you don't want it directly exposed to the Internet.
However, your DevOps team needs SSH access to troubleshoot an issue.
One approach would be to assign the EC2 instance a public IP and allow SSH:
Internet | | SSH :22 v Private/Application Server
This is not a good architecture for most production environments.
You are unnecessarily exposing your application server to the public Internet.
A bastion host provides a controlled access path:
Administrator
|
| SSH
v
Bastion Host
|
| SSH through private network
v
Private EC2
Only the bastion needs to be reachable from the administrator's network.
Bastion Host Architecture in AWS
A typical AWS architecture might look like this:
Internet
|
Internet Gateway
|
+--------------+--------------+
| |
Public Subnet Private Subnet
10.0.1.0/24 10.0.2.0/24
| |
| |
+--------------+ +--------------+
| Bastion EC2 |--------------| Application |
| | SSH | EC2 |
| Public IP | | No Public IP |
+--------------+ +--------------+
|
|
+----------+
| RDS |
| Private |
+----------+
The public subnet contains the bastion.
The application server and database remain inside private subnets.
Public Subnet vs Private Subnet
Before setting up a bastion, it's important to understand the difference.
Public Subnet
A subnet is considered public when its route table has a route to an Internet Gateway.
For example:
Destination Target 10.0.0.0/16 local 0.0.0.0/0 Internet Gateway
A bastion host can be placed here.
Private Subnet
A private subnet does not have a direct route to an Internet Gateway for inbound Internet access.
For example:
Destination Target 10.0.0.0/16 local
Your application servers and databases should generally be placed here.
How a Bastion Host Works
The connection flow is simple.
Step 1
The administrator connects to the bastion:
ssh -i bastion.pem ec2-user@<BASTION_PUBLIC_IP>
Step 2
From the bastion, the administrator connects to the private EC2:
ssh ec2-user@10.0.2.10
The traffic flow is:
Laptop | | SSH v Bastion | | SSH through private VPC v Private EC2
The private EC2 never needs to accept SSH traffic directly from the Internet.
Setting Up a Bastion Host in AWS
Let's walk through a basic AWS setup.
We'll assume the following network:
VPC 10.0.0.0/16 Public Subnet 10.0.1.0/24 Private Subnet 10.0.2.0/24
Step 1: Create a VPC
Go to:
AWS Console → VPC → Your VPCs → Create VPC
Example:
Name: production-vpc CIDR: 10.0.0.0/16
This gives us the overall private network.
Step 2: Create the Public Subnet
Create a subnet:
Name: public-subnet CIDR: 10.0.1.0/24 Availability Zone: ap-south-1a
This subnet will contain the bastion host.
Step 3: Create the Private Subnet
Create another subnet:
Name: private-subnet CIDR: 10.0.2.0/24 Availability Zone: ap-south-1a
Your application EC2 instance can live here.
Step 4: Create an Internet Gateway
Create an Internet Gateway:
production-igw
Attach it to:
production-vpc
Then configure the public subnet's route table:
Destination Target 10.0.0.0/16 local 0.0.0.0/0 production-igw
Now the public subnet has Internet connectivity.
Step 5: Launch the Bastion EC2 Instance
Go to:
EC2 → Launch Instance
For example:
Name: production-bastion AMI: Amazon Linux 2023 Instance Type: t3.micro
Select:
VPC: production-vpc Subnet: public-subnet
Enable a public IPv4 address.
For production environments, you may also use an Elastic IP if a stable public IP is required.
Step 6: Create the Bastion Security Group
Create:
bastion-sg
The most important rule is the inbound SSH rule.
For example:
Type: SSH Protocol: TCP Port: 22 Source: YOUR_PUBLIC_IP/32
If your office's public IP is:
203.0.113.50
then:
SSH TCP 22 203.0.113.50/32
This means only that IP address can attempt SSH access.
Avoid this:
SSH TCP 22 0.0.0.0/0
That allows SSH connections from anywhere on the Internet.
Step 7: Configure the Private EC2 Security Group
Now suppose your application server has:
private-ec2-sg
Instead of allowing SSH from the Internet, configure:
Type: SSH Port: 22 Source: bastion-sg
This is an important AWS security practice.
You are effectively saying:
Only instances associated with the bastion security group can SSH into this private EC2 instance.
The architecture becomes:
Internet | | SSH :22 | YOUR_IP ONLY v Bastion | | SSH :22 | Source = bastion-sg v Private EC2
Why Security Group References Are Better
You could allow SSH from the entire VPC:
10.0.0.0/16
But using the bastion's Security Group as the source is more precise:
Source: bastion-sg
This creates a logical relationship between the two servers.
If the bastion's private IP changes, the rule doesn't need to be updated.
Connecting to the Bastion
Once the instance is running:
ssh -i bastion.pem ec2-user@<BASTION_PUBLIC_IP>
For Ubuntu:
ssh -i bastion.pem ubuntu@<BASTION_PUBLIC_IP>
After connecting:
ssh ec2-user@10.0.2.10
You are now connected to the private EC2 instance.
Don't Copy Your Private Key to the Bastion
A common beginner mistake is:
Laptop | | Copy private key v Bastion | v Private EC2
For example, copying:
production.pem
to the bastion.
This is generally not a good security practice.
If someone compromises the bastion and your private key is stored there, they may be able to use that key to access additional infrastructure.
A better approach is to use SSH Agent Forwarding or ProxyJump, or preferably use AWS Systems Manager where possible.
Using SSH Agent Forwarding
On your local machine:
ssh-add ~/.ssh/private-key.pem
Then connect:
ssh -A ec2-user@<BASTION_PUBLIC_IP>
From the bastion:
ssh ec2-user@10.0.2.10
Your private key remains on your local machine.
Using SSH ProxyJump
An even cleaner SSH workflow is ProxyJump.
Configure:
~/.ssh/config
Example:
Host bastion
HostName <BASTION_PUBLIC_IP>
User ec2-user
IdentityFile ~/.ssh/bastion.pem
Host private-server
HostName 10.0.2.10
User ec2-user
IdentityFile ~/.ssh/private.pem
ProxyJump bastion
Now you can simply run:
ssh private-server
SSH automatically creates the connection:
Laptop | | SSH v Bastion | | SSH v Private EC2
Bastion Host Security Best Practices
A bastion host is itself a security-sensitive system.
It should therefore be hardened properly.
1. Restrict SSH access
Don't use:
0.0.0.0/0
Instead:
YOUR_OFFICE_IP/32
or your VPN CIDR.
2. Use SSH keys
Disable password-based SSH authentication where possible.
Use:
SSH Key Authentication
instead of:
Username + Password
3. Keep the operating system patched
Regularly apply security updates:
sudo dnf update -y
For Ubuntu:
sudo apt update sudo apt upgrade -y
4. Keep the bastion minimal
Don't install unnecessary applications.
A bastion should primarily provide controlled administrative access.
Avoid turning it into:
Bastion + Jenkins + Docker + Database + Application + Monitoring + Random tools
The more software you install, the larger the attack surface becomes.
5. Monitor the bastion
Monitor:
- SSH login attempts
- Failed authentication
- CPU
- Memory
- Disk
- Network traffic
- System logs
AWS services such as CloudWatch, CloudTrail, GuardDuty, and VPC Flow Logs can contribute to the monitoring and security architecture.
Bastion Host and High Availability
A single bastion can become a single point of failure.
For example:
Internet
|
Bastion-1
|
Private VPC
If Bastion-1 goes down, administrators lose their normal SSH access path.
For larger environments, you may consider:
Internet
|
+-------+-------+
| |
Bastion-1 Bastion-2
| |
+-------+-------+
|
Private VPC
However, before deploying multiple bastions, consider whether AWS Systems Manager Session Manager would solve the access requirement more simply.
Bastion Host vs NAT Gateway
These two services are often confused.
They serve completely different purposes.
Bastion Host
Used for:
Inbound administrative access
Example:
Administrator
|
v
Bastion
|
v
Private EC2
NAT Gateway
Used primarily for:
Outbound Internet access from private resources
Example:
Private EC2
|
v
NAT Gateway
|
v
Internet Gateway
|
v
Internet
The private EC2 can download updates or access external APIs without accepting inbound Internet connections.
So:
Bastion = controlled administrative access INTO private infrastructure NAT Gateway = outbound Internet access FROM private infrastructure
Bastion Host vs Internet Gateway
An Internet Gateway connects your VPC to the Internet.
It doesn't provide the same functionality as a bastion.
For example:
Internet | v Internet Gateway | v Public Subnet | v Bastion
The Internet Gateway provides network connectivity.
The bastion provides a controlled administrative endpoint.
Bastion Host vs AWS Systems Manager
This is one of the most important modern AWS considerations.
Traditionally:
Administrator
|
| SSH
v
Bastion
|
| SSH
v
Private EC2
With Systems Manager Session Manager:
Administrator
|
| AWS API
v
Systems Manager
|
v
Private EC2
You don't necessarily need:
- Public IP
- Port 22
- SSH keys
- Bastion server
What Is AWS Systems Manager Session Manager?
Session Manager allows administrators to access managed EC2 instances through AWS Systems Manager.
For example:
aws ssm start-session \
--target i-xxxxxxxxxxxxxxxxx
You can get a shell on the EC2 instance without exposing SSH to the Internet.
This can significantly reduce the attack surface.
Bastion vs SSM Session Manager
FeatureBastion HostSSM Session ManagerPublic IPUsually requiredNoSSH port 22Usually requiredNoSSH private keysUsuallyNoIAM integrationIndirectStrongCloudTrail integrationLimitedStrongSession loggingAdditional configurationSupportedOperational overheadHigherLowerAWS-nativeYesYesRecommended for new AWS environmentsSituationalOften preferred
This doesn't mean bastions are obsolete.
They are still useful for environments that require traditional SSH, third-party tooling, legacy systems, or specific network access patterns.
Production-Grade AWS Access Architecture
For a modern AWS production environment, you could use:
AWS Account
|
+---------+---------+
| |
IAM / SSO CloudTrail
|
v
Systems Manager
|
v
Private Subnets
|
+---------+---------+
| |
v v
Application EC2 EKS
|
v
RDS
There is no need to expose every server to the Internet.
If traditional SSH is required:
Trusted Network
|
v
Bastion Host
|
+------------+------------+
| | |
v v v
EC2 App EKS Internal VM
|
v
RDS
Common Bastion Host Mistakes
Mistake 1: Allowing SSH from anywhere
0.0.0.0/0
Avoid this wherever possible.
Mistake 2: Storing private keys on the bastion
Don't casually copy production SSH keys onto the bastion.
Mistake 3: Giving the bastion excessive IAM permissions
Follow the principle of least privilege.
Mistake 4: Running unnecessary applications
Keep the bastion hardened and minimal.
Mistake 5: Giving private servers public IPs anyway
If the entire purpose of the architecture is controlled private access, don't undermine it by exposing the application servers.
Mistake 6: Ignoring logging
Administrative access should be auditable.
Bastion Host: Interview Explanation
If you're preparing for an AWS DevOps interview, you can explain it like this:
A bastion host is a hardened EC2 instance deployed in a public subnet that provides controlled administrative access to resources in private subnets. Instead of exposing private EC2 instances directly to the Internet, administrators connect to the bastion from trusted IP addresses and then access private resources through the VPC. The bastion Security Group should restrict SSH access to trusted sources, while private instances should allow SSH only from the bastion Security Group. In modern AWS architectures, Systems Manager Session Manager can often replace a traditional bastion by providing auditable access without exposing SSH or requiring public IP addresses.
Final Takeaway
A bastion host is essentially a controlled gateway into your private infrastructure.
The traditional architecture is:
Internet
|
Trusted IP only
|
| SSH
v
+-------------+
| Bastion |
| Public |
+-------------+
|
| SSH
v
+-------------+
| Private EC2 |
+-------------+
|
v
RDS
The key security principle is:
Expose as little infrastructure as possible to the public Internet.
A bastion helps achieve this by providing a single, controlled administrative entry point.
However, for new AWS environments, don't automatically deploy a bastion just because it's a common architecture. Evaluate AWS Systems Manager Session Manager first. If it satisfies your operational requirements, it can provide a more secure and lower-maintenance way to access private EC2 instances.
For a DevOps engineer, understanding both patterns is important:
Traditional: Administrator → Bastion → Private EC2 Modern AWS: Administrator → SSM → Private EC2
The best architecture depends on your security requirements, operational model, compliance needs, and the tooling used by your team.