CLOUD
Security

The Bastion Host: One Watched Door to All Your Servers

CLOUD|October 20, 2026|7 min read|114 views
The Bastion Host: One Watched Door to All Your Servers

Open SSH on one hardened server only and let every other server accept connections from it through ProxyJump, with key only login and logged sessions. A Starter server at 3,450 SAR a year protects a whole fleet.

A bastion host is one small, hardened server that is the only machine in your fleet with SSH open to the internet. Every other server accepts SSH only from the bastion's IP or from a private network, and engineers reach them in one step with ssh -J or a ProxyJump line in their SSH config, while their private keys never leave their laptops. That single door is where you enforce key only login, restrict source IPs to your office and VPN, and record who connected where and when. A fresh public IP typically sees its first SSH brute force attempts within minutes and thousands per day after that, so closing port 22 on 10 servers and opening it on one removes 90% of that exposure overnight. On CLOUD, a Starter server with 1 vCPU and 2 GB RAM costs 3,450 SAR a year, under 9.5 SAR a day, and comfortably handles dozens of concurrent sessions for a whole team. This guide covers why direct SSH everywhere is a risk, the exact ProxyJump config, hardening the bastion itself, session logging for audits, IP and key restrictions, and when WireGuard is the better choice or the right companion.

Why SSH should not be open on every server

Every open SSH port is an attack surface you must patch, monitor and harden separately. With 10 servers that means 10 sets of sshd_config to keep identical, 10 auth logs to watch, and 10 places where one forgotten PasswordAuthentication yes or an old employee's key in authorized_keys becomes the way in. When a new OpenSSH vulnerability is published, like the 2024 regreSSHion flaw that affected default configurations, you race to patch all of them at once.

A bastion turns that into one place to harden and one place to watch. Internal servers become invisible to internet scanners, and when someone leaves the team you remove one key from one machine and their access to the entire fleet ends.

Setting up ProxyJump

ProxyJump, available since OpenSSH 7.3, opens an SSH connection to the bastion and tunnels a second, end to end encrypted SSH session through it to the target. The bastion only forwards bytes, so it never sees your private key and you do not need agent forwarding. Put this in ~/.ssh/config on each engineer's machine:

Host bastion
    HostName 203.0.113.10
    User ahmed
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes

Host app-* db-*
    User ahmed
    ProxyJump bastion
    IdentityFile ~/.ssh/id_ed25519

Host app-1
    HostName 10.0.0.11
Host db-1
    HostName 10.0.0.21

Now ssh db-1 goes through the bastion transparently, and so do scp, rsync and tools like Ansible that read the same config. The extra hop adds roughly 1 to 3 ms when both servers sit in the same Riyadh data center, which nobody notices in an interactive session.

Avoid ForwardAgent yes as a shortcut. With agent forwarding, anyone with root on the bastion can use your loaded keys while you are connected. ProxyJump gives you the same convenience without that risk.

Hardening the bastion itself

The bastion is now your most valuable target, so it should run almost nothing: no web server, no database, no application code. Its /etc/ssh/sshd_config should include at least:

PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
AllowGroups jumpers admins
MaxAuthTries 3
LoginGraceTime 20
LogLevel VERBOSE
X11Forwarding no

Match Group jumpers
    AllowTcpForwarding yes
    PermitOpen 10.0.0.11:22 10.0.0.21:22
    PermitTTY no
    ForceCommand /usr/sbin/nologin

The Match block is the important part. Members of jumpers can use the bastion only as a hop to the listed internal SSH ports, with no shell, no terminal and no way to open tunnels to arbitrary services. Only a small admins group gets a real shell on the bastion for maintenance. LogLevel VERBOSE records the fingerprint of the key used for every login, so the log shows which key was used, not just which account.

Keep the bastion patched automatically with unattended-upgrades for security updates, and reboot it on a fixed weekly window. A machine this small reboots in under a minute.

Restrict by IP and by key

Key only login is the baseline, and the network layer should narrow things further. On the bastion, allow port 22 only from your office's fixed IP and your VPN range:

ufw default deny incoming
ufw allow from 198.51.100.24 to any port 22 proto tcp
ufw allow from 10.8.0.0/24 to any port 22 proto tcp
ufw enable

On each internal server, allow SSH only from the bastion's private IP, for example ufw allow from 10.0.0.5 to any port 22 proto tcp, and deny everything else. Even a stolen key is useless from an unknown network.

For the keys themselves, use ed25519, protect them with a passphrase, and prefer hardware keys with ed25519-sk for admins, so a key file copied from a laptop cannot be used without the physical device. Keep a simple register of who owns which key fingerprint, and review it every 90 days.

Session logging for audits

A bastion gives you one place to answer the auditor's question: who accessed the database server on the 14th, and what did they do? Build that answer in layers:

  • Connection logs: with LogLevel VERBOSE, the bastion's auth.log records the user, source IP, key fingerprint, time, and each forwarded destination.
  • Command logs on targets: enable auditd with a rule on execve for interactive users, which records every command run, including those run through sudo.
  • Full session recording where regulation demands it: tools such as tlog record terminal input and output and can replay a session later.
  • Off host storage: forward all of it with rsyslog to a separate log server the admins cannot edit, and keep it for at least 12 months.

Session recording of a busy admin produces only a few megabytes a day, so storage is not the constraint, reviewing it is. Set alerts for logins outside working hours and for any connection from a key that is not in your register.

Bastion or WireGuard

WireGuard is the other common answer: put every server on a private VPN and close public SSH entirely. The two solve different problems, and many teams use both.

CriterionBastion hostWireGuard VPN
Public exposureOne TCP port 22One UDP port that ignores unauthenticated packets
Access granularityPer user, per destination hostNetwork level, filtered by firewall rules
Audit trailRich SSH logs with key fingerprintsHandshake times only, needs extra logging
Non SSH traffic (databases, dashboards)Possible but awkward through tunnelsNative
Setup timeAbout 1 hourAbout 1 to 2 hours plus client setup

A strong pattern is WireGuard on the bastion, with SSH on the bastion reachable only from inside the VPN. The bastion then disappears from internet scans entirely, and you still keep per user SSH control and detailed logs. If your team needs to reach internal dashboards and database ports, WireGuard handles that more cleanly than stacking SSH tunnels.

What it costs to protect a fleet

A bastion needs little power. A Starter cloud server with 1 vCPU, 2 GB RAM and 20 GB SSD at 3,450 SAR a year handles 30 to 50 concurrent SSH sessions, since forwarding encrypted bytes uses well under 5% of a core per session. Spread across a fleet of 10 servers, that is 345 SAR per server per year for a single, auditable entry point. Put it in the same Riyadh location as the rest of your servers so internal hops stay at 1 to 3 ms, while engineers connecting from anywhere in the Kingdom see about 8 ms to Riyadh plus their own network.

If the bastion becomes critical for a large team, run a second one in standby and list both in your SSH config, so a reboot never locks anyone out.

Your rollout plan

  1. Provision a small server as the bastion, install nothing on it except SSH, the firewall and log shipping.
  2. Apply the hardened sshd_config with the jumpers group and allowed destinations.
  3. Add ProxyJump to every engineer's SSH config and confirm access to each server through the bastion.
  4. Change the firewall on internal servers to accept SSH only from the bastion's private IP.
  5. Turn on LogLevel VERBOSE, auditd on targets, and off host log forwarding.
  6. Decide whether WireGuard goes in front of the bastion, and review your key register every 90 days.

Review server sizes and add-ons on our pricing page and close public SSH on your fleet this week.

Share