Jump Servers (Bastion Host)
A jump server, or bastion host, is a hardened gateway for accessing internal systems. Rather than connecting directly, you first log into this secure host and then “jump” to other machines. This setup enhances security, centralizes access control, and allows detailed session logging.
SSH jump servers, also called bastion hosts, are key elements in secure network setups. They serve as controlled gateways, enabling access to private servers that are not directly reachable from the internet. Using the -J flag in SSH makes connecting through these jump servers seamless, letting you hop from your local machine to a target server in a single command.
Understanding the Concept
A bastion host sits in a DMZ or public subnet, serving as the only entry point to your private infrastructure. Instead of exposing internal servers directly, you authenticate through the bastion, which then forwards your connection.
Basic Syntax
$ ssh -J user@bastion-host user@target-hostTopology
[Client] → [Bastion (public IP)] → [Internal Host (private IP)]Real-World Examples
1. Simple Jump Through a Single Bastion
# Connect to internal server via bastion
ssh -J admin@bastion.example.com dev@10.0.1.23
# Using different ports
ssh -J admin@bastion.example.com:2222 dev@10.0.1.23:222. Multiple Jump Hosts (Chain)
# Chaining multiple jump servers
ssh -J user1@jump1.example.com,user2@jump2.example.com user@final-serverHere, SSH passes through multiple bastions in sequence before reaching the final target.
3. Using Private Keys
# Specify keys for both connections
ssh -J -i ~/.ssh/bastion_key user@bastion -i ~/.ssh/internal_key user@target-i ~/.ssh/bastion_key→ key for the bastion host-i ~/.ssh/internal_key→ key for the internal target server
This allows you to authenticate with different keys at each stage of the jump, keeping credentials separate and secure.
SSH Config File Setup (Pro. Way)
Create ~/.ssh/config for persistent configurations:
# Bastion Host Configuration
Host bastion
HostName bastion.example.com
User admin
Port 22
IdentityFile ~/.ssh/bastion_rsa
ForwardAgent yes
# Internal Server via Bastion
Host internal-server
HostName 10.0.1.23
User developer
Port 22
ProxyJump bastion
IdentityFile ~/.ssh/internal_rsa
# Multiple Internal Servers with Pattern Matching
Host 10.0.1.*
User developer
ProxyJump bastion
IdentityFile ~/.ssh/internal_rsa
# AWS EC2 instances through bastion
Host ec2-*.compute.amazonaws.com
User ec2-user
ProxyJump bastion
IdentityFile ~/.ssh/aws-key.pemThen connect simply with:
ssh internal-server
ssh 10.0.1.45Advanced Techniques
Port Forwarding Through Jump Server
# Local port forwarding through bastion
ssh -J bastion -L 8080:internal-server:80 user@internal-server
# Access internal database through tunnel
ssh -J bastion -L 5432:db.internal:5432 user@bastionAccess remote web app at:
http://localhost:8080
Copying Files via Jump Server
# SCP through bastion (modern approach)
scp -J admin@bastion file.txt dev@10.0.1.23:/path/
# Rsync through bastion
rsync -e "ssh -J admin@bastion" -avz file.txt dev@10.0.1.23:/path/Using Agent Forwarding
# Enable agent forwarding through bastion
ssh -J bastion -A user@target
# In config file
Host target
ProxyJump bastion
ForwardAgent yes⚠️ Use cautiously—exposes your agent to the bastion.
Security Best Practices
1. Use SSH Keys, Not Passwords
# Generate dedicated keys
ssh-keygen -t ed25519 -f ~/.ssh/bastion_key -C "bastion-access"
ssh-keygen -t ed25519 -f ~/.ssh/internal_key -C "internal-access"2. Restrict Jump Server Access
To harden a bastion host, you can limit what users can do when connecting. In the ~/.ssh/authorized_keys file on the jump server, you can restrict access like this:
In bastion's ~/.ssh/authorized_keys:
command="internal-sftp",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAA... bastion-keycommand="internal-sftp"→ forces only SFTP; no shell access.no-port-forwarding→ disables SSH tunneling.no-X11-forwarding→ disables GUI forwarding.no-agent-forwarding→ prevents key forwarding.no-pty→ blocks interactive shells.
This ensures the jump server is used only as intended, minimizing the risk of abuse.
3. Implement MFA
Combine with tools like google-authenticator or use certificates.
4. Audit and Logging
Enable detailed logging on the bastion:
# In /etc/ssh/sshd_config
LogLevel VERBOSE
# Monitor logs
tail -f /var/log/auth.log | grep sshd
BONUS
Troubleshooting Common Issues
Verbose Mode for Debugging
Sometimes your jump connection fails or behaves unexpectedly. SSH's verbose mode shows exactly what happens during the connection, including key negotiation, authentication, and ProxyJump routing.
ssh -vvv -J user@bastion_ip user@10.0.0.10-v→ verbose output-vv→ more detailed-vvv→ maximum debug info (step-by-step connection flow)
Advanced SSH: Bastions + ProxyJump + Config Includes
Create reusable configurations with include directives:
Why Use Include
Instead of putting all your SSH hosts in a single file, you can split them into reusable config files. This keeps your SSH setup organized, scalable, and easier to maintain.
1) Directory Structure
~/.ssh/
├── config # main SSH config
├── config.d/
│ ├── bastions.conf # bastion host definitions
│ └── internal.conf # internal hosts definitions
└── keys/
├── prod-bastion-key
├── dev-bastion-key
└── internal-key2) Main SSH Config (~/.ssh/config)
# Load all config files from the config.d folder
Include config.d/*.confKeeps the main config minimal; all host definitions are in separate files.
3) Bastion Hosts Config (~/.ssh/config.d/bastions.conf)
# Production bastion
Host prod-bastion
HostName 54.123.45.67
User ops
IdentityFile ~/.ssh/keys/prod-bastion-key
# Development bastion
Host dev-bastion
HostName 10.5.0.1
User devops
IdentityFile ~/.ssh/keys/dev-bastion-key4) Internal Hosts Config (config.d/internal.conf)
# Internal production server, accessed via prod-bastion
Host prod-app-01
HostName 10.10.0.10
User admin
IdentityFile ~/.ssh/keys/internal-key
ProxyJump prod-bastion
# Internal development server, accessed via dev-bastion
Host dev-app-01
HostName 10.5.0.20
User dev
IdentityFile ~/.ssh/keys/internal-key
ProxyJump dev-bastion5) How to Connect
Now connecting is simple:
# Connect to bastion directly
ssh prod-bastion
ssh dev-bastion
# Connect to internal hosts via bastions automatically
ssh prod-app-01
ssh dev-app-01No need to type -J every time—SSH reads the ProxyJump from your config.
6) Why This Setup Works
- Modular: Separate files for bastions and internal hosts
- Scalable: Easily add new servers by creating a new
.confinconfig.d/ - Clean: Main
~/.ssh/configstays short and readable - Automated jumps: ProxyJump handles routing automatically
Performance Optimization
You can improve SSH performance and stability, especially over slower networks or multi-hop connections, with a few key config options.
# Enable compression for slower connections
Host bastion
Compression yes
CompressionLevel 6
# Keep connections alive
Host *
ServerAliveInterval 60
ServerAliveCountMax 3Using the -J flag provides a simple, secure way to access private hosts through a bastion. For repeated connections, configure ~/.ssh/config with key-based authentication, optional agent forwarding, and ProxyJump settings. Always log and monitor bastion access. This approach scales with your infrastructure while maintaining strong security.
This wraps up our tutorial. Thanks for following along.