A bastion host (also called a jump host) is a single, hardened server that acts as the only entry point for SSH access to your other servers — instead of exposing SSH on every machine directly to the internet, only the bastion is publicly reachable.
Why Use a Bastion Host
- Reduces your attack surface to a single, heavily-monitored entry point instead of many exposed servers
- Centralizes SSH access logging and auditing in one place
- Internal servers can be configured with no public IP or firewall rules blocking all direct SSH from the internet
Architecture
Your laptop → Bastion host (public IP, hardened) → Internal servers (private IPs only, SSH accessible only from the bastion)
Prerequisites
- A dedicated VPS to serve as the bastion (can be a small/inexpensive instance)
- Internal servers reachable via a private network (see How to Set Up a Site-to-Site VPN Between Two VPS Servers if servers are on different providers, or a shared private network if using the same provider)
Step 1 — Harden the Bastion Host Itself
The bastion is your single point of entry, so it deserves the strictest possible hardening — see How to Harden a Fresh Linux VPS in 15 Minutes and SSH Hardening: Change the Port, Disable Root Login & Use SSH Keys, applying every recommendation without exception.
Step 2 — Restrict Internal Servers to Only Accept SSH from the Bastion
sudo ufw allow from BASTION_PRIVATE_IP to any port 22
On each internal server, remove any broader SSH access rule, leaving only the bastion's IP allowed.
Step 3 — Connect Through the Bastion (Basic Method)
ssh -J user@BASTION_PUBLIC_IP user@INTERNAL_SERVER_PRIVATE_IP
-J (jump) tells SSH to first connect to the bastion, then tunnel the connection through to the internal server — a single command, no separate manual hop required.
Step 4 — Simplify with SSH Config
nano ~/.ssh/config
Host bastion
HostName BASTION_PUBLIC_IP
User youruser
IdentityFile ~/.ssh/bastion_key
Host internal-server-1
HostName 10.0.0.10
User youruser
IdentityFile ~/.ssh/internal_key
ProxyJump bastion
Now connecting is simply:
ssh internal-server-1
Step 5 — Use Separate Keys for the Bastion and Internal Servers (Recommended)
Avoid reusing the same SSH key for both the bastion and internal servers — if the bastion is ever compromised, a separate internal key limits what an attacker could do with just the bastion's own credentials.
Step 6 — Enable Detailed Logging on the Bastion
sudo nano /etc/ssh/sshd_config
LogLevel VERBOSE
sudo systemctl restart sshd
Since every connection to internal infrastructure passes through this one machine, detailed logging here gives you a complete audit trail — see How to Monitor Auth Logs and Detect Intrusion Attempts on a Linux VPS.
Step 7 — Consider Session Recording for Compliance-Sensitive Environments
For environments with audit requirements, tools exist that record full SSH session activity on the bastion — relevant primarily for larger teams or regulated industries; not necessary for most small-scale setups.
Common Errors
"ProxyJump" connection fails — verify the bastion itself is reachable independently first (ssh bastion), then troubleshoot the internal hop separately.
Internal server unreachable even through the bastion — verify network connectivity (private IP routing) between the bastion and internal server, and that the internal server's firewall explicitly allows the bastion's private IP.
Best Practices
- Apply the strictest possible hardening to the bastion, since it's your single point of entry
- Use separate SSH keys for bastion and internal server access
- Enable and regularly review detailed access logging on the bastion
Continue Reading
- SSH Hardening: Change the Port, Disable Root Login & Use SSH Keys
- How to Set Up a Site-to-Site VPN Between Two VPS Servers
- How to Monitor Auth Logs and Detect Intrusion Attempts on a Linux VPS
Browse more articles in Server Security & Hardening.