A bastion host is the server you expect to be attacked. NIST defines it as “a special purpose computer on a network specifically designed and configured to withstand attacks” (NIST CSRC glossary). In practice it’s the one machine exposed to an outside network that admins SSH or RDP into before they go anywhere else, which is why it’s also called a jump server, a jump box or a jump host. Every other host stays private and accepts admin connections only when they come from the bastion.

What is a bastion host used for?

Opening SSH (port 22) or RDP (port 3389) on every machine puts every one of them on the internet’s scan list. A bastion host is used to give administrators one controlled way into a private network instead: the port is open on a single hardened machine, and everything else accepts admin connections only from it. AWS states the goal plainly: the bastion host “remains the only source of SSH traffic to your Linux instances.”

That buys a smaller attack surface, one machine where strong authentication is enforced, and one place to look when you need to know who logged in at 2 a.m., as long as the bastion’s logs are shipped somewhere its own users can’t edit.

What a bastion doesn’t do on its own is choose which host each person may reach from it, or keep any record of the commands they run there.

How a bastion host works

Bastion host architecture in a VPC

AWS’s guide to recording SSH sessions through a bastion host, published in June 2016 and still the clearest description of the pattern, has three pieces. The bastion runs on an EC2 instance “typically in a public subnet,” with a security group that admits SSH from outside. The Linux instances sit in a subnet that isn’t publicly reachable, and their security group allows SSH only from the bastion’s security group. A leaked server key alone gets nobody to them from the internet.

If engineers also run kubectl from the bastion against a private cluster, the same reasoning applies to the Kubernetes API; our Kubernetes access page covers that path.

On premises: the DMZ

Before cloud VPCs, the bastion lived in a DMZ, a small segment between an outer firewall facing the internet and an inner firewall in front of the internal network. The outer firewall lets admin traffic reach the bastion. The inner one lets the bastion, and nothing else from the DMZ, through to internal systems. In a VPC, a public subnet with tight security groups plays the DMZ’s part.

Two ways through the hop

Engineers rarely want to stop on the bastion. They want a shell on the server behind it, and OpenSSH offers two common ways to get there that behave very differently.

With agent forwarding (ssh -A), you log in to the bastion and run ssh again from there, and the second login uses the key held by the agent on your laptop. The private key never leaves your machine. The OpenSSH manual still warns that agent forwarding “should be enabled with caution”: anyone on the bastion who can bypass file permissions on the agent’s socket, root for instance, can use your loaded keys to authenticate as you for as long as you stay connected.

With ProxyJump (ssh -J bastion target), your client connects to the bastion and asks it for a TCP forwarding to the target, then runs the real SSH session to the target through that forwarded connection. This is the setup most people mean by an SSH jump host. Your keys stay on your laptop and nothing on the bastion can use them.

The price is visibility: the bastion relays encrypted bytes and has no way to log the commands inside them.

Bastion host vs jump server

Is a bastion host the same as a jump box? In everyday use, yes. Bastion host, bastion server, jump server, jump box and jump host all name the hardened machine you pass through to reach systems you can’t reach directly, and most teams use whichever word they learned first.

The words do lean in different directions. “Bastion” stresses exposure: a host facing an untrusted network, hardened because it will be attacked. “Jump server” stresses the hop between two security zones, and neither zone has to be the internet. A jump box that admins on the corporate network use to reach a production segment may never be reachable from outside, and few people would call it a bastion.

In a cloud VPC the two roles land on one machine. The host in the public subnet is the exposed bastion and the hop into the private subnet at the same time, so the rest of this guide uses bastion host for both.

Bastion hosts on AWS and Azure

The EC2 bastion pattern

The AWS guide above is worth reading for what it takes to add the one thing a bare bastion lacks, a record of what each user ran. Its recipe changes the bastion’s sshd_config in three ways. ForceCommand makes every login run a wrapper that starts the shell under script, which writes the terminal session to a log file. AllowTcpForwarding no and X11Forwarding no go in because those features “would allow users to create a direct connection between their local computer and the Linux instances, thereby bypassing the solution.” And the wrapper refuses any login that supplies a command, since, in the script’s own comment, “the ‘script’ program could be circumvented with some commands (e.g. bash, nc).”

Put those together and the cost shows. With TCP forwarding off, ProxyJump stops working through that bastion, so engineers go back to logging in and hopping. With commands refused, scp, rsync and remote one-liners through the bastion stop too. The logs are copied to S3, and each user gets the minimum privileges on the bastion so they can’t tamper with the recording.

The recipe’s test step shows a second thing. A single key pair, sshuser, logs in “to both the bastion host as a bastion host user and the Linux instance in the private subnet as the privileged ec2-user user,” and the test makes the second hop with agent forwarding. With that setup the bastion knows who logged in, and every server behind it only ever sees ec2-user.

AWS Systems Manager Session Manager

AWS’s current answer is to skip the bastion. Session Manager opens a shell on an instance through the SSM Agent “without the need to open inbound ports, maintain bastion hosts, or manage SSH keys.” Who may connect is an IAM policy question, which lets you give on-call engineers access to production servers for the length of their rotation. Session traffic is encrypted with TLS 1.2, connection requests are signed with SigV4, and session output can go to S3 or CloudWatch Logs, with CloudTrail recording the API calls and EventBridge reacting to session events.

Check one note in that documentation before relying on the logs: “Logging isn’t available for Session Manager sessions that connect through port forwarding or SSH.” When an engineer runs SSH over Session Manager, which is how tools like VS Code Remote SSH and scp usually reach an instance through it, Session Manager is only a tunnel for an encrypted stream. That’s the position a bastion is in with ProxyJump.

We know that path well. Our engineer Aditya Saligrama ended up forking AWS’s session-manager-plugin to fix concurrency bugs, so that SSH port forwarding over SSM would hold up under VS Code Remote SSH.

Azure Bastion

On Azure, Microsoft runs the bastion for you. Azure Bastion is a “fully managed PaaS service” deployed into your virtual network. You reach VMs over RDP or SSH through it on TLS, and the VMs need no public IP address, agent or special client software. As of February 2026, Microsoft’s overview lists four SKUs. Developer runs on shared infrastructure; Basic, Standard and Premium need a dedicated AzureBastionSubnet and a public IP, and Premium can also be deployed private-only. Native SSH and RDP clients work from Standard up. Session recording is a Premium feature.

Bastion host security best practices

If you run a bastion today, these are the bastion host best practices we’d check first. Most come from the AWS recipe and the OpenSSH manual.

  • Run nothing else on it. AWS’s hardening advice covers “disabling unnecessary applications or services, tuning the network stack, and the like.” No build agents, no shared tooling, no data.
  • Keep it the only path. Security groups or firewall rules on every server should admit SSH and RDP from the bastion and from nowhere else. Test it by trying a direct connection.
  • Limit who can reach the bastion to known address ranges, your corporate network or your VPN.
  • Give every person their own account and key. AWS’s reason is “to determine the origin of any action”; a shared login answers that question with a username everyone used.
  • Require MFA for the bastion login.
  • Prefer ProxyJump to agent forwarding when you don’t record on the bastion. If you do record with a shell wrapper, turn TCP and X11 forwarding off and accept what that breaks.
  • Ship logs off the machine, to storage the bastion’s own users can’t write to.
  • Patch it automatically, or rebuild it from a fresh image on a schedule.
  • Make access expire. A bastion account that lasts as long as someone’s employment is standing access. Just-in-time access ties each grant to a task and an expiry, so it lapses by itself.

What are the limits of a bastion host?

The main limit of a bastion host is that its control ends at login. A bastion decides who gets onto the private network, but by itself it keeps no record of what people run once they’re through, it doesn’t restrict which servers they reach, and it often sits in front of a privileged account shared by everyone.

Start with the audit trail. The bastion’s sshd logs show that a key logged in from an address. Commands typed on the servers behind it go unrecorded unless you build recording yourself, and the EC2 recipe above shows how much that takes and what it breaks. With ProxyJump, recording on the bastion isn’t possible at all.

Then the accounts behind it. Personal keys on the bastion don’t carry through to the servers if each server has one admin account and a shared SSH key that everybody uses, as in the AWS test. That shared account is a standing privilege, the kind our zero standing privileges guide sets out to remove, because it works the same for whoever holds it, on any day.

Reach is the third problem. Once someone is on the bastion, the network decides what they can get to. Security groups and firewall rules are written per subnet and per port, so a person admitted to the bastion for one server can usually open a session to every other machine it can reach, and so can anyone holding their stolen key.

And it’s one more machine you have to run. It needs patching, monitoring, high availability if people depend on it during incidents, and a way to keep accounts in step with the team. The AWS recipe handles that last part with a cron job that runs every 5 minutes, reads public keys from S3, and creates or deletes local users to match.

Are bastion hosts still used?

Yes. Bastion hosts are still a common way into private networks, and they still do their main job of keeping management ports off the internet. What has changed is that both AWS and Azure now offer ways not to run one: AWS describes Session Manager as “freeing you from managing SSH keys and certificates, bastion hosts, and jump boxes,” and Azure Bastion makes the bastion a service you deploy instead of a VM you patch.

Our view: if your instances are on AWS and engineers only need shells, Session Manager removes the open port and the key distribution, and a self-run bastion has little left to do. Before switching, look at how your team actually connects. If most sessions are SSH carried over Session Manager, they won’t show up in its session logs.

Alternatives to a bastion host or jump server

The replacements fall into three families, and what separates them is what sits between the engineer and the target host.

Cloud-native session tools such as Session Manager and Azure Bastion move the hop into the cloud provider. They remove the inbound port, and in Session Manager’s case the SSH keys as well.

An identity-aware proxy applies zero trust to the hop. Each user signs in through the identity provider at the proxy, and the proxy connects them to one named service instead of placing them on the network, so there’s no network-wide reach to abuse after login.

Privileged access management (PAM) proxies go further for privileged sessions. The proxy holds the server credential, so engineers never receive a key or password for the host. It applies policy to each session, and because it terminates the engineer’s connection and opens its own to the server, it can record what happens inside. Our PAM guide covers the category.

Classic bastion hostAWS Session ManagerAzure BastionPAM proxy that terminates the session
Inbound port on the serversOpen, to the bastion onlyNoneVMs need no public IPNone for users; the proxy connects to the servers
SSH keysKeys and accounts on every server, managed by youNot neededNot covered in Microsoft’s overviewHeld by the proxy
Session recordingBuild it yourself; users can get around itShell sessions to S3 or CloudWatch Logs; not SSH or port-forwarding tunnelsPremium SKU onlyPossible, because the proxy sees the decrypted session
Where it runsA VM you run in a public subnet or DMZAWS service, plus the SSM Agent on each nodeManaged by Microsoft in your virtual networkIn your network

When we compare any of these, the question we start with is where the session is decrypted. If the only answers are the laptop and the target host, nothing in between can record a command or stop one.

How Formal replaces a bastion host

We built the Formal Connector to sit where the bastion sits. It’s a protocol-aware reverse proxy that you deploy in your own VPC, on ECS Fargate or any Kubernetes cluster. Engineers SSH to the Connector as their Formal identity (idp:formal:human:<email>) and pick the target with formal connect: a host over SSH, or an EC2 instance or ECS task over SSM, so those need no inbound SSH at all. The Control Plane manages configuration and shows logs, but it can’t interact with your data.

Unlike a bastion, the Connector terminates the engineer’s SSH connection and opens its own to the server. The login for the server lives on the Connector as a Native User: either a password or an SSH private key, which can carry a certificate signed by your CA, or an IAM role for SSM targets. Engineers never see it. A CEL expression can map admins to one Native User and everyone else to a read-only one. Session policies are evaluated as the connection opens, with access to the requested remote command, the EC2 instance’s tags or the ECS task, so production can be blocked by default and opened for one person for a limited time. Every session is recorded, too. The Sessions page plays each recording back alongside the commands pulled out of it, a 1-to-5 risk rating and a short written summary. Session Monitors watch SSH sessions for scenarios you write in plain English (the docs’ example is ssh_curl_to_bash), and on a match a policy can stop the session or quarantine the user.

Two limits to know. SSH policies are evaluated when the session starts, and only monitors act during it; monitors also only work once an AI provider is set up on the Connector. On laptops, the Formal Endpoint’s Transparent Mode can intercept SSH to the real host instead, and it refuses any SSH connection it can’t proxy. AI agents connect like engineers do, as machine users with their own identity, so no agent environment holds a key to your hosts.

The SSH session recording page covers setup, policies and the clients that keep working, and Formal PAM covers the databases and Kubernetes clusters the same Connector fronts. Cursor replaced its homegrown access proxies with Formal Connector and secures SSH, PostgreSQL, Redis, ClickHouse, and Kubernetes through it.

“With Formal, we can finally manage least privilege access across our entire infrastructure through a single control plane.”

— Tom Daniels, CISO @ Cursor