Concepts / Remote access

Remote access

Bastion host

Also called jump host, ssh -J, ProxyJump

One hardened machine that is the only way into a private network.

It sits on both networks; everything else refuses connections from outside. ssh -J bastion backend crosses it in one command. One machine to harden and one log to read, and backends that accept SSH from the bastion only.

How a bastion host works, step by step

In the bastion lab the user sits on outsideNetwork (10.0.1.0/24, user .11), and the backend on internalNetwork (10.0.2.0/24, backend .11). The bastion has a card on each (10.0.1.10 and 10.0.2.10). The gateway routes between them but forwards nothing new from outside in: its FORWARD chain accepts established replies and traffic from 10.0.2.0/24, and drops the rest.

  1. The direct path is closed. The user's SYN to 10.0.2.11:22 reaches the gateway and is dropped. No reset comes back, so ssh waits until ConnectTimeout and gives up.
  2. First hop: user to bastion. 10.0.1.11 to 10.0.1.10:22, on the same network, no firewall between them. The bastion authenticates the user (keys, often a second factor) and logs it.
  3. Second hop: bastion to backend. The bastion reaches 10.0.2.11:22 over its own internal card, not through the gateway. The backend sees the connection come from 10.0.2.10.
  4. End to end with ProxyJump. ssh -J admin@10.0.1.10 admin@10.0.2.11 does both hops in one command. The bastion only opens a TCP channel (a direct-tcpip channel, RFC 4254) to the backend's port 22; the second SSH session runs inside it, end to end from the user's machine. The bastion never sees the backend session's keys or the user's private key, unlike agent forwarding (ssh -A), which lets the bastion use the user's agent.

The result: one machine exposed, one place to harden, one log of who came in.

What it looks like on a Linux machine

From outside, the backend does not answer:

$ ssh -o ConnectTimeout=5 admin@10.0.2.11 hostname
ssh: connect to host 10.0.2.11 port 22: Connection timed out

The bastion does, and from it the backend is a neighbour. The lab nests the two hops:

$ ssh admin@10.0.1.10 "ssh admin@10.0.2.11 'hostname; curl -s http://localhost/'"
backend
backend: the crown jewels

In daily use, ssh -J or an entry in ~/.ssh/config does the same, and ssh backend then jumps by itself:

Host bastion
    HostName 10.0.1.10
    User admin
Host backend
    HostName 10.0.2.11
    User admin
    ProxyJump bastion

The backend's log names only the bastion's address:

$ grep Accepted /var/log/netlab-sshd.log
Accepted password for admin from 10.0.2.10 port 51634 ssh2

On the bastion itself, sshd hardening is short: PasswordAuthentication no, PermitRootLogin no, AllowUsers or AllowGroups for the people who may jump, and AllowTcpForwarding limited (with PermitOpen 10.0.2.11:22 for example) so it forwards only to SSH on the backends.

What goes wrong, and how you see it

The second hop is refused. The first hop works, the second fails fast: ssh: connect to host 10.0.2.11 port 22: Connection refused. Fast means a reset, so a REJECT rule or nothing listening on the backend. On the backend, iptables -S NETLAB-INPUT shows a rule for 10.0.2.10.

A back door around the bastion. Someone adds a gateway rule that accepts SSH from outside to the backend, and a direct login works. The backend's log shows from 10.0.1.11, an outside address. Remove the rule; the jump does not need it, because the bastion uses its own internal card.

ProxyJump refused by the bastion. With AllowTcpForwarding no on the bastion, ssh -J fails with channel 0: open failed: administratively prohibited: open failed. Allow forwarding, limited with PermitOpen.

The bastion is down. Every way in goes with it. Run two bastions, and keep an out-of-band path for the devices themselves.

Questions people ask

Bastion host vs jump host vs jump box? Different names for the same machine: one hardened host that is the only way into a private network. "Bastion" stresses the hardening, "jump host" the extra hop.

ProxyJump vs agent forwarding? -J keeps your keys on your own machine and runs SSH end to end through the bastion. -A exposes your agent to anyone with root on the bastion, so prefer -J.

Bastion vs VPN? A VPN puts your machine on the private network, for every protocol. A bastion gives a shell or a port to selected hosts, and logs each login. Many networks use both. See SSH for host keys and key logins.

See it on real machines

Learn it on real machines.

Every lab is a small network of real Linux machines in your browser. Start with a free account: Google or email, no card.

Open this lab