Skip to main content

Jump Servers: Design, Harden and Operate a Secure Bastion

October 28, 2026 Technical Guides By Peter French

Design an SSH access path with a jump server. Learn bastion hardening, access restrictions, logging, recovery and when a fuller PAM platform is needed.

Use a jump server when administrators need a deliberately restricted SSH path into another security zone. Make the bastion reachable only from approved administrative sources, allow forwarding only to intended targets, and retain authentication and privilege controls on those targets. A gateway is an additional system to secure and recover, not a substitute for target security.

This guide uses an Ubuntu Server 24.04 SSH bastion, OpenSSH clients and a forwarding-only bastion account. It assumes you already administer the bastion and target, have working public-key access and possess an alternative recovery route. It does not install an access-management platform or create private networking. Run each diagnostic or service command separately and read its result before proceeding.

Map the access path and its trust boundaries

The intended path is:

Bastion connection path
Administrator's workstation
    | SSH to bastion; authenticate as bastion-user
    v
SSH bastion
    | TCP forwarding to TARGET_ADDRESS:22 only
    v
Protected target
    | Separate SSH authentication as target-user
    v
Authorised target session

OpenSSH ProxyJump first connects to the jump host, then establishes TCP forwarding to the destination. The destination connection still has its own SSH authentication and host-key verification. Keep the target private key on the administrator's workstation; do not copy it to the bastion to make the second hop work.

Define three network rules before changing SSH: approved administrator sources may reach the bastion's SSH port; the bastion may reach the permitted target's SSH port; ordinary direct administrator-to-target access is denied. Keep any separately authorised recovery route documented and restricted. Apply the design to every address family and route actually in use, including an existing public target address. A diagram alone does not enforce these boundaries.

This design requires real network reachability from bastion to target. It does not create a tunnel into an otherwise unreachable network or imply a hosting provider supplies a private network. If the necessary path does not exist, network design is a prerequisite, not something an SSH client setting can repair.

Keep identities and host keys separate

Use individual identities, with a distinct unprivileged forwarding account on the bastion and the appropriate account on the target. Keep a separate bastion maintenance account with the privileges needed to administer the host. The forwarding account must not have another route to a shell or administrative privilege; otherwise it could run its own network tools around forwarding restrictions.

Install each authorised public key on the intended account, protect the private keys on the workstation and use passphrases. Ubuntu's OpenSSH guidance explains public-key installation and requires that other users cannot write the account's authorized_keys. Prove that the maintenance account and target key work before removing an existing authentication method.

Collect the bastion and target host-key fingerprints through your existing trusted administrative or console access. Match them when enrolling host keys in the workstation's known_hosts. The strict client configuration below requires those trusted entries to exist. Do not disable host-key checking to get past an unexplained change; identify whether the server was deliberately rebuilt or whether you reached the wrong endpoint.

On the workstation, edit ~/.ssh/config. The uppercase address tokens, account names and identity-file paths below are placeholders for your own systems. Replace them before use. TARGET_ADDRESS must use the same literal destination as the server's PermitOpen rule later.

SSH client configuration for the bastion path
Host controlled-bastion
    HostName BASTION_ADDRESS
    User bastion-user
    IdentityFile ~/.ssh/id_bastion
    IdentitiesOnly yes
    ForwardAgent no
    StrictHostKeyChecking yes

Host protected-target
    HostName TARGET_ADDRESS
    User target-user
    IdentityFile ~/.ssh/id_target
    IdentitiesOnly yes
    ForwardAgent no
    StrictHostKeyChecking yes
    ProxyJump controlled-bastion

Configure both host entries: destination options do not generally apply to the jump host. IdentitiesOnly yes limits the identities offered to configured files, while ForwardAgent no avoids exposing the workstation's authentication agent through the connection. Agent forwarding can let a compromised remote host use identities loaded into that agent even without extracting private-key material.

Use ssh -G protected-target to inspect the effective client configuration. Look for the intended hostname, user, identity and proxyjump value. Existing wildcard entries and earlier settings can affect the result. This inspection checks configuration selection; it does not establish that the connection works.

Restrict what the forwarding account can do

On the bastion, first preserve the current SSH configuration and all included snippets in a protected recovery location outside the files being edited. Record which file you change. Retain a working maintenance session and usable alternative console or recovery access. Do not proceed if losing the SSH service would remove every way to repair it.

Ubuntu includes /etc/ssh/sshd_config.d/*.conf near the start of the main configuration, and OpenSSH uses the first value for most directives. Inspect existing settings before adding a snippet. The example below is a deliberate configuration change, not an instruction to overwrite the main file or append contradictory settings blindly.

Use your privileged text editor to put the global restrictions before any Match block and the account-specific block at the end of the main configuration, after all global directives. Do not put a Match block in an early included file if subsequent global-only directives would then fall inside its scope. Here, bastion-user is forwarding-only and bastion-maintainer is the already-tested maintenance account. Replace both names and TARGET_ADDRESS with your own values. This example uses public-key authentication only; it is not an MFA configuration.

Restricted SSH server configuration
PubkeyAuthentication yes
AuthenticationMethods publickey
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
AllowUsers bastion-user bastion-maintainer
AllowAgentForwarding no
X11Forwarding no
AllowStreamLocalForwarding no
PermitTunnel no
LogLevel VERBOSE

Match User bastion-user
    AllowTcpForwarding local
    PermitOpen TARGET_ADDRESS:22
    MaxSessions 0
    PermitTTY no
    PermitUserRC no
Match all

AllowTcpForwarding local permits client-originated TCP forwarding, which is the direction used here, and rejects remote forwarding. PermitOpen restricts its destination to the exact host-and-port spelling supplied. It does not perform address lookups to treat different names as equivalent. Keep the target entry and rule consistent; a hostname and its IP address are not interchangeable strings for this restriction. For an IPv6 literal in PermitOpen, use [address]:22 as the documented host-and-port form.

MaxSessions 0 prevents shell, login and subsystem sessions while still permitting forwarding. PermitTTY no alone would not prevent a noninteractive command, so it is not the main shell restriction. Disabling agent, X11, Unix-socket and tunnel forwarding removes additional channels from this example. Do not set DisableForwarding yes for the forwarding account: that would defeat its intended job.

The maintenance account remains a privileged trust boundary. These SSH directives are not a sandbox for an administrator who can change the configuration. Keep the bastion dedicated to access, restrict its network reach to the necessary targets and maintenance dependencies, and avoid placing unrelated applications on it.

Validate before activating the change

On the bastion, run sudo sshd -t. Successful validation returns without diagnostics; an error means stop and repair the configuration. Next inspect the applicable account using sudo sshd -T -C user=bastion-user,addr=ADMIN_ADDRESS,host=admin-workstation, substituting the real account, source address and applicable client hostname. This applies matching rules and prints the effective settings.

For the forwarding account, inspect authentication methods, allowed users, allowtcpforwarding local, the exact permitopen, maxsessions 0 and disabled auxiliary forwarding. Repeat the effective-configuration inspection for the maintenance account and confirm it is not accidentally subject to the forwarding-only session limit. Also inspect address-dependent rules for the actual source you will use. Syntax validity alone does not prove these values are the intended ones.

After validation succeeds, run sudo systemctl restart ssh.service on the bastion. Ubuntu uses this service restart to activate SSH configuration changes. Keep the recovery access available, then open a new maintenance connection rather than treating the old session as proof that new logins work.

From a separate workstation session, run ssh protected-target. The allowed test succeeds only when you authenticate to the intended target and can perform an authorised harmless check, such as whoami, with the expected target identity. Compare the actual host identity as well; reaching some shell is not enough. These are expected checks, not captured results.

Next test the restrictions from another fresh connection. ssh -W TARGET_ADDRESS:23 controlled-bastion, with the real target address substituted, requests a destination port outside the single :22 permission. It should be refused as an administratively prohibited forwarding request. The check is of SSH policy; it does not require a service listening on port 23. If it is forwarded instead, stop and inspect the effective policy before relying on the gateway.

Try ssh controlled-bastion using the forwarding account: authentication must not result in a usable shell because session channels are disabled. Separately attempt direct target access from a source that the network policy excludes; it must not yield an SSH login. A forwarding denial does not establish that direct network access is blocked. Test both paths, and ensure the authorised recovery path still works.

If a required test fails, use the retained maintenance or recovery access to restore the previous contents of the files changed, or remove only a newly introduced snippet that belongs to this change. Run sudo sshd -t again, then restart ssh.service only after validation succeeds. Open a fresh maintenance connection and repeat the affected access test. Do not disable the entire firewall or host-key checking as a rollback shortcut.

Operate the gateway after the first connection

On Ubuntu, sudo journalctl -fu ssh.service follows SSH service logs. Correlate bastion authentication events with target logs and the time of your allowed and denied tests. LogLevel VERBOSE increases SSH daemon logging; debug levels are not a sensible permanent substitute for an audit design and can expose private information.

A plain SSH bastion carrying the target's SSH connection does not automatically record the target terminal or every command. Its logs can help investigate authentication and daemon activity, but they are not a complete account of what an administrator did on the destination. Retain target-side evidence and define who reviews alerts, where logs are kept and how long they remain available.

Maintain an identity-to-key inventory. When access ends, remove the person's authorisation from both relevant systems and review other accepted authentication paths. OpenSSH also supports RevokedKeys for public-key revocation, but an unreadable revocation file can reject public-key authentication for everyone. Treat revocation changes with the same retained access, validation and new-connection tests as other SSH changes. Review active sessions separately from permission for future authentication.

Patch the operating system and SSH packages, keep the configuration and target allowlist under controlled change, and repeat access checks after changes. Monitor the useful path through the gateway, not merely whether its VM is powered on. A running bastion cannot complete a connection to a failed or unreachable target.

Symptom First distinction to investigate
Bastion authentication fails Wrong account/key, disallowed source, or effective authentication policy
Authentication works but forwarding is prohibited Literal destination mismatch, forwarding direction, or a conflicting restriction
Forwarding is allowed but the target cannot be reached Routing, target listener or network policy beyond the bastion
Target login fails Target credentials and permissions; bastion authentication is separate
Forwarding user gets no shell Expected with MaxSessions 0; maintenance uses its separate account
Swipe to view the full table

Plan for the bastion being unavailable or compromised. Keep a separately controlled recovery route to the bastion and targets, plus recoverable configuration, authorised-key records and host-identity information. Test that route before it becomes urgent. Do not respond to an outage by permanently opening every target to the internet. A single access gateway needs its own recovery plan even when it improves control of the normal path.

Know when the task needs more than an SSH bastion

JumpServer PAM is a named privileged-access-management platform, not another spelling of a generic jump server. Its repository describes a platform with a web interface, protocol connectors and access to several asset types. It also contains asset/account permission models and separate session, command and replay records. These are application capabilities beyond OpenSSH TCP forwarding.

Area SSH bastion JumpServer PAM
Access-control scope SSH identities and permitted forwarding destinations, with target permissions managed separately Platform asset/account permissions and protocol components
Credential and audit boundaries Client-held keys and daemon logs in this design; no automatic target-session recording Account records and session/command/replay facilities; connector and edition boundaries still matter
Operator responsibilities Maintain host, SSH policy, keys, network restrictions, logs and recovery Maintain the platform and its components, configure permissions and protect credential/audit state
Swipe to view the full table

JumpServer's repository distinguishes core and enterprise components, including enterprise protocol and recording services. Do not interpret the platform name as a promise that every connector or recording feature is available in every edition, or that every session is automatically captured. This comparison does not prescribe a JumpServer installation.

A VPN addresses network connectivity; the SSH bastion here constrains a particular administrative connection. If your actual requirement is broader network access, endpoint posture, per-application policy, central credential workflows or recorded privileged sessions, define that requirement before selecting a VPN, zero-trust access system or PAM platform. Do not relabel an unrestricted SSH relay as all of those capabilities.

Our Cloud VPS supplies self-managed infrastructure with root access. You design the network path and operate the bastion, identities, restrictions, updates and recovery. Use the completed allowed/denied checks and recovery plan as the acceptance criteria for your design; the presence of a jump server is not a security guarantee.

An editorial starting point for a modest SSH-only bastion. Confirm the route to your targets; you configure authentication, restrictions and recovery.

VPS S

VPS S: a starting configuration

VPS S: 3 CPU cores, 6 GB RAM and 50 GB NVMe. Check the resources your complete setup needs before choosing.

$ 5 .50 /month
  • ✓ 3 cores
  • ✓ 6 GB
  • ✓ 50 GB NVMe
  • ✓ Unlimited
View VPS S specifications
Peter French
About the author

Peter French

Peter French is the Managing Director at Virtarix, with over 17 years in the tech industry spanning cloud hosting, cybersecurity, and data protection.

View author profile