How to Set Up a Bastion Host for Secure Server Access (2025 Guide)
Table of Contents
What is a Bastion Host?
A bastion host (also known as a jump server) is a specially designed and configured computer on a network specifically designed and configured to withstand attacks. The computer generally hosts a single application, for example a proxy server, and all other services are removed or limited to minimize security risks.
Think of a bastion host as a fortified gatekeeper that controls access to your private network from the outside world. Instead of exposing all your servers to the internet, you only expose the bastion host, which acts as the single point of entry.
Why Use a Bastion Host?
Implementing a bastion host provides several security benefits for your infrastructure:
- Reduced Attack Surface: By limiting external access to just the bastion host, you significantly reduce the number of potential entry points for attackers.
- Centralized Access Control: All SSH/RDP access is funneled through a single point, making it easier to monitor and control.
- Improved Auditing: With all access going through one point, it’s easier to log and audit who accessed what and when.
- Network Segmentation: Helps enforce network segmentation by acting as the only bridge between different security zones.
- Easier Security Hardening: You can focus your security efforts on hardening a single system rather than every server in your infrastructure.
Factor | With Bastion Host | Direct Access |
---|---|---|
Attack Surface | Minimal (single entry point) | All servers exposed |
Access Control | Centralized and strict | Decentralized and harder to manage |
Audit Logging | Comprehensive and centralized | Distributed and harder to track |
Maintenance | Update one system | Update all systems |
Setting Up a Bastion Host on AWS
Amazon Web Services (AWS) provides several options for setting up a bastion host. Here’s a step-by-step guide to creating a secure bastion host using EC2:
Step 1: Launch an EC2 Instance
# Create a new security group for the bastion host
aws ec2 create-security-group --group-name bastion-sg
--description "Security group for bastion host"
--vpc-id vpc-1234567890abcdef0
# Add SSH access rule (restrict to your IP for better security)
aws ec2 authorize-security-group-ingress
--group-name bastion-sg
--protocol tcp
--port 22
--cidr YOUR_IP_ADDRESS/32
# Launch the EC2 instance
aws ec2 run-instances
--image-id ami-0c55b159cbfafe1f0
--instance-type t2.micro
--key-name MyKeyPair
--security-group-ids sg-1234567890abcdef0
--subnet-id subnet-1234567890abcdef0
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=BastionHost}]'
Step 2: Configure Security Groups
Update the security groups of your private instances to only allow SSH/RDP access from the bastion host’s security group.
Step 3: Connect to Private Instances
Use SSH agent forwarding or the -J
flag to jump through the bastion host:
# Using SSH agent forwarding
ssh -A ec2-user@BASTION_PUBLIC_IP
# Using the -J flag (OpenSSH 7.3+)
ssh -J ec2-user@BASTION_PUBLIC_IP ec2-user@PRIVATE_INSTANCE_IP
PasswordAuthentication no
in /etc/ssh/sshd_config
.Setting Up Azure Bastion
Azure offers a fully managed service called Azure Bastion that provides secure and seamless RDP/SSH connectivity to your virtual machines directly through the Azure portal.
Step 1: Create an Azure Bastion Host
- Sign in to the Azure Portal
- Click “Create a resource”
- Search for “Bastion” and select it
- Click “Create” and fill in the required details:
- Subscription and Resource Group
- Name: YourBastionHost
- Region: Same as your VNet
- Tier: Standard (recommended for production)
- Virtual Network: Select your VNet
- Subnet: Must be named ‘AzureBastionSubnet’ (will be created if it doesn’t exist)
- Public IP: Create new
- Click “Review + create” then “Create”
Step 2: Connect to VMs Using Azure Bastion
- Navigate to the VM you want to connect to in the Azure Portal
- Click “Connect” and select “Bastion”
- Enter your credentials (username and SSH private key or password)
- Click “Connect” to establish the session
Setting Up a Bastion Host on GCP
Google Cloud Platform (GCP) provides multiple options for setting up a bastion host, including using Compute Engine or the Identity-Aware Proxy (IAP) for TCP forwarding.
Option 1: Using Compute Engine as a Bastion
# Create a firewall rule to allow IAP's TCP forwarding
gcloud compute firewall-rules create allow-ssh-ingress-from-iap
--direction=INGRESS
--action=allow
--rules=tcp:22
--source-ranges=35.235.240.0/20
--target-tags=bastion
# Create the bastion instance
gcloud compute instances create bastion
--zone=us-central1-a
--machine-type=e2-micro
--subnet=default
--network-tier=PREMIUM
--tags=bastion
--image-family=debian-10
--image-project=debian-cloud
--boot-disk-size=10GB
--boot-disk-type=pd-standard
--boot-disk-device-name=bastion
# Connect to the bastion host
gcloud compute ssh bastion --tunnel-through-iap
Option 2: Using IAP for TCP Forwarding (No Bastion Host Needed)
GCP’s IAP TCP forwarding allows you to connect to instances without a bastion host:
# Connect directly to a private instance through IAP
gcloud compute ssh private-instance
--tunnel-through-iap
--zone=us-central1-a
Security Best Practices for Bastion Hosts
1. Harden the Bastion Host
- Keep the operating system and all software up to date
- Disable password authentication (use SSH keys only)
- Install and configure a host-based firewall (e.g., UFW, firewalld)
- Enable automatic security updates
- Remove unnecessary software and services
2. Network Security
- Place the bastion host in a public subnet with a restrictive security group
- Limit inbound access to specific IP addresses or ranges
- Use security groups to restrict which instances the bastion can access
- Consider using a Network Load Balancer in front of multiple bastion hosts for high availability
3. Access Control
- Use individual SSH keys for each user (no shared credentials)
- Implement multi-factor authentication (MFA) for SSH access
- Regularly rotate SSH keys and review access permissions
- Use tools like
ssh-copy-id
to manage authorized_keys
4. Monitoring and Logging
- Enable detailed logging of all SSH sessions
- Forward logs to a centralized logging solution
- Set up alerts for failed login attempts
- Regularly review access logs for suspicious activity
Troubleshooting Common Issues
1. Connection Timeouts
Symptoms: Unable to connect to the bastion host or through to private instances.
Solutions:
- Verify the security group rules allow inbound traffic on port 22 (SSH) or 3389 (RDP)
- Check network ACLs if using a VPC
- Ensure the instance has a public IP address (if required)
- Verify the route table for the subnet
2. Permission Denied (publickey)
Symptoms: Authentication fails with “Permission denied (publickey)” error.
Solutions:
- Verify the SSH key is correctly added to the instance’s
~/.ssh/authorized_keys
file - Check file permissions:
chmod 700 ~/.ssh
andchmod 600 ~/.ssh/authorized_keys
- Ensure the correct username is being used (e.g., ec2-user, ubuntu, admin)
3. Agent Forwarding Issues
Symptoms: Unable to connect from the bastion to private instances.
Solutions:
- Use
ssh -A
to enable agent forwarding - Verify
AllowAgentForwarding yes
is set in/etc/ssh/sshd_config
on the bastion - Consider using SSH config file with
ForwardAgent yes
Conclusion
Implementing a bastion host is a critical security measure for any organization managing cloud infrastructure. By following the guidelines in this article, you can establish a secure access pattern that significantly reduces your attack surface while maintaining the ability to administer your servers effectively.
Remember that security is an ongoing process. Regularly review and update your bastion host configuration, monitor access logs, and stay informed about new security best practices and vulnerabilities.
For organizations using serverless architectures, consider how your bastion host strategy integrates with other security measures like principle of least privilege and compliance requirements.
`;
// Set the HTML content to the hidden div document.getElementById('post-html').textContent = postHtml;
// Set up the download functionality document.querySelectorAll('.download-btn').forEach(btn => { btn.addEventListener('click', function(e) { if (this.getAttribute('href') === '#post-html') { e.preventDefault(); const blob = new Blob([postHtml], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'serverless-servants-bastion-host-guide.html'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } }); }); });