·10 min read

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.

SSHSecurityBastionInfrastructureAudit

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:

  1. Settings → SSH → Add Server
  2. Enter bastion details (hostname, port, key)
  3. Mark as "Jump Host"
  4. Add internal servers with bastion as proxy
  5. 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.