SSH Bastion Hosts: Building a Secure Gateway for Your Infrastructure
Learn how to set up and manage SSH bastion hosts (jump servers) to control access, audit connections, and protect your internal network.
In a secure infrastructure, you don't expose every server to the internet. Instead, you place a single hardened server — the bastion host (or jump server) — at the network boundary. All SSH connections flow through this gateway, giving you centralized access control, auditing, and a reduced attack surface.
This guide shows you how to build, harden, and manage SSH bastion hosts for production environments.
Why Use a Bastion Host?
Without Bastion Host
Internet → Server 1 (SSH exposed)
→ Server 2 (SSH exposed)
→ Server 3 (SSH exposed)
Problems:
- Every server is a potential entry point
- Each server needs individual security hardening
- No centralized access logging
- Difficult to revoke access (must update every server)
With Bastion Host
Internet → Bastion Host (only SSH exposed)
→ Server 1 (internal only)
→ Server 2 (internal only)
→ Server 3 (internal only)
Benefits:
- Single hardened entry point
- Centralized authentication and authorization
- Complete audit trail of all connections
- Easy access revocation (one place to update)
- Internal servers not exposed to internet
Architecture Design
Basic Setup
┌─────────────┐
Internet ───────→│ Bastion Host│────── Private Network
│ (Public IP) │
└─────────────┘
│
┌──────────┼──────────┐
↓ ↓ ↓
┌────────┐ ┌────────┐ ┌────────┐
│ Web │ │ App │ │ DB │
│ Server │ │ Server │ │ Server │
└────────┘ └────────┘ └────────┘
(Private (Private (Private
IP only) IP only) IP only)
Multi-Tier Architecture
For larger organizations:
Internet → External Bastion → Internal Bastion → Production Servers
↓
Staging Servers
Each tier adds another layer of access control.
Setting Up a Bastion Host
Step 1: Choose the Right Server
Requirements:
- Minimal OS installation (Ubuntu Server, Alpine Linux, or Amazon Linux)
- Small instance (t3.micro or equivalent is sufficient for most teams)
- Public IP address
- Private network access to internal servers
Step 2: Install and Harden OpenSSH
# Update system
sudo apt update && sudo apt upgrade -y
# Install OpenSSH server
sudo apt install openssh-server -y
# Backup default config
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
Edit SSH configuration:
# /etc/ssh/sshd_config
# Disable password authentication
PasswordAuthentication no
# Enable public key authentication
PubkeyAuthentication yes
# Disable root login
PermitRootLogin no
# Restrict which users can connect
AllowUsers alice bob charlie
# Enable detailed logging
LogLevel VERBOSE
# Set idle timeout (15 minutes)
ClientAliveInterval 900
ClientAliveCountMax 0
# Disable X11 forwarding (unless needed)
X11Forwarding no
# Disable agent forwarding by default
AllowAgentForwarding no
# Limit authentication attempts
MaxAuthTries 3
# Use strong ciphers only
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
# Use strong MACs
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
# Port (optional: change from default 22)
Port 2222
Restart SSH:
sudo systemctl restart sshd
Step 3: Configure Firewall
# Install UFW
sudo apt install ufw -y
# Allow SSH (custom port if changed)
sudo ufw allow 2222/tcp
# Deny all other incoming traffic
sudo ufw default deny incoming
# Enable firewall
sudo ufw enable
Step 4: Set Up User Accounts
# Create user
sudo adduser alice
# Create .ssh directory
sudo mkdir -p /home/alice/.ssh
sudo chmod 700 /home/alice/.ssh
# Add public key
sudo echo "ssh-ed25519 AAAA... alice@company.com" > /home/alice/.ssh/authorized_keys
sudo chmod 600 /home/alice/.ssh/authorized_keys
sudo chown -R alice:alice /home/alice/.ssh
# Restrict shell (optional: limit to SSH tunneling only)
sudo usermod -s /bin/rbash alice
Jump Host Configuration
Client-Side Configuration
Users configure their SSH client to use the bastion as a jump host.
SSH Config File
# ~/.ssh/config
# Bastion host
Host bastion
HostName bastion.example.com
Port 2222
User alice
IdentityFile ~/.ssh/id_ed25519_bastion
ForwardAgent no
# Internal servers via bastion
Host internal-*
ProxyJump bastion
User deploy
IdentityFile ~/.ssh/id_ed25519_internal
# Specific servers
Host internal-web-01
HostName 10.0.1.10
Host internal-db-01
HostName 10.0.2.10
Connect directly:
ssh internal-web-01
# Automatically jumps through bastion
Command-Line Jump
ssh -J alice@bastion.example.com:2222 deploy@10.0.1.10
FoxTerm Configuration
FoxTerm makes jump hosts visual:
- Settings → SSH → Add Server
- Enter bastion details (hostname, port, key)
- Mark as "Jump Host"
- Add internal servers with bastion as proxy
- Connect to any internal server — FoxTerm handles the jump automatically
Access Control
Role-Based Access
Create groups for different access levels:
# Create groups
sudo groupadd ssh-admins
sudo groupadd ssh-developers
sudo groupadd ssh-readonly
# Add users to groups
sudo usermod -aG ssh-developers alice
sudo usermod -aG ssh-admins bob
Configure access per group:
# /etc/ssh/sshd_config
# Admins: full access
Match Group ssh-admins
AllowTcpForwarding yes
AllowAgentForwarding yes
PermitOpen any
# Developers: limited forwarding
Match Group ssh-developers
AllowTcpForwarding local
AllowAgentForwarding no
PermitOpen 10.0.1.0/24:*,10.0.2.0/24:*
# Read-only: no forwarding
Match Group ssh-readonly
AllowTcpForwarding no
AllowAgentForwarding no
ForceCommand /usr/local/bin/readonly-shell.sh
Time-Based Access
Restrict access to business hours:
# /etc/security/access.conf
+ : alice bob : Mon-Fri 09:00-18:00
+ : charlie : Mon-Sun 00:00-23:59 # On-call engineer
- : ALL : ALL
Enable PAM access control:
# /etc/ssh/sshd_config
UsePAM yes
Certificate-Based Access
For large teams, use SSH certificates instead of managing individual keys.
See our SSH Certificates article for details.
Auditing and Logging
Enable Detailed Logging
# /etc/ssh/sshd_config
LogLevel VERBOSE
SyslogFacility AUTH
Logs appear in /var/log/auth.log:
Oct 27 14:32:15 bastion sshd[12345]: Accepted publickey for alice from 203.0.113.50 port 54321 ssh2: ED25519 SHA256:abc123
Oct 27 14:32:16 bastion sshd[12345]: session opened for user alice
Oct 27 14:35:22 bastion sshd[12345]: Received disconnect from 203.0.113.50 port 54321: disconnected by user
Session Recording
Record all terminal sessions for audit purposes.
Using script command
# /usr/local/bin/recorded-shell.sh
#!/bin/bash
LOG_DIR="/var/log/ssh-sessions"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/$(whoami)-$(date +%Y%m%d-%H%M%S).log"
script -f "$LOG_FILE"
Configure as forced command:
# /etc/ssh/sshd_config
Match Group ssh-readonly
ForceCommand /usr/local/bin/recorded-shell.sh
Using tlog (specialized tool)
# Install tlog
sudo apt install tlog
# Configure
sudo systemctl enable tlog-rec-session
Sessions are recorded and can be replayed with tlog-play.
Centralized Logging
Forward logs to a central system:
# /etc/rsyslog.d/ssh.conf
auth,authpriv.* @logs.company.com:514
Or use cloud services:
- AWS CloudWatch Logs
- Datadog
- Splunk
- ELK Stack (Elasticsearch, Logstash, Kibana)
Monitoring and Alerting
Monitor Failed Login Attempts
# Check recent failures
grep "Failed password" /var/log/auth.log | tail -20
# Count failures per IP
grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn
Set Up Alerts
Use fail2ban to detect and block brute force attacks:
sudo apt install fail2ban
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
Dashboard Metrics
Monitor bastion health:
# Active connections
who | wc -l
# System load
uptime
# Disk usage
df -h /var/log
# Memory usage
free -m
Integrate with monitoring tools (Prometheus, Grafana) for visualization.
High Availability
For critical infrastructure, run multiple bastion hosts.
DNS Round-Robin
# DNS configuration
bastion.example.com → 203.0.113.10
bastion.example.com → 203.0.113.11
bastion.example.com → 203.0.113.12
Clients connect to bastion.example.com, DNS rotates between IPs.
Load Balancer
Place a load balancer in front of bastion hosts:
Internet → Load Balancer → Bastion 1
→ Bastion 2
→ Bastion 3
Use HAProxy or Nginx for TCP load balancing.
Shared Configuration
Keep bastion configs synchronized:
- Use configuration management (Ansible, Puppet, Chef)
- Store SSH keys in a central system (HashiCorp Vault)
- Sync user accounts via LDAP/Active Directory
Hardening Checklist
Before deploying to production:
- OS fully updated
- SSH hardened (no passwords, key-only auth)
- Firewall configured (only SSH port open)
- Fail2ban installed and configured
- Logging enabled and forwarded
- User accounts created with proper keys
- Access control rules defined
- Session recording enabled (if required)
- Monitoring and alerting configured
- Backup bastion ready (for HA)
- Documentation complete
- Incident response plan documented
Common Mistakes
1. Exposing Internal Servers
Wrong: Internal servers have public IPs and accept direct SSH.
Right: Internal servers only have private IPs, accessible only via bastion.
2. Weak Authentication
Wrong: Password-based authentication enabled.
Right: Key-only authentication, preferably with certificates.
3. No Logging
Wrong: Default SSH logging (minimal information).
Right: Verbose logging, centralized, with session recording.
4. Overly Permissive Access
Wrong: All users can access all servers.
Right: Role-based access, principle of least privilege.
5. Single Point of Failure
Wrong: One bastion host, no backup.
Right: Multiple bastion hosts with failover.
FoxTerm Bastion Support
FoxTerm simplifies bastion host management:
Visual Jump Host Configuration
Add bastion hosts once, reuse across all connections. No repetitive SSH config editing.
Automatic Key Management
Keys stored in Secure Enclave, automatically presented when connecting through bastions.
Connection Chaining
Connect to deeply nested internal servers through multiple bastion hops. FoxTerm manages the chain transparently.
Audit Trail
FoxTerm Pro logs all connections, including:
- Timestamp
- Server accessed
- Bastion used
- Duration
- Commands executed (with session recording)
Export logs for compliance reporting.
Conclusion
SSH bastion hosts are essential for secure infrastructure. They reduce attack surface, centralize access control, and provide complete audit trails. With proper hardening, monitoring, and high availability, bastion hosts become the foundation of your security posture.
FoxTerm makes working with bastion hosts seamless, so you get security without sacrificing convenience.
FoxTerm Pro includes bastion host support and audit logging. Download at foxterm.app.