Configure SSH Bastion Jumps and Connection Multiplexing
Centralize jump-host access with ProxyJump, reuse connections with ControlMaster, and keep per-host keys instead of a shared bastion credential.
Task
Set up one-command SSH access to internal hosts through a bastion with multiplexed connections and least-privilege keys.
When to use this
Engineers type two chained SSH commands and a password for every internal host. Some copy a shared bastion private key onto laptops to automate it.
Prerequisites
- A bastion host reachable from your network
- SSH client 7.3+ supporting ProxyJump and ControlMaster
- Per-user key access to both bastion and targets
Steps
1. Configure the jump in ~/.ssh/config.
Host bastion
HostName bastion.example.com
User you
IdentityFile ~/.ssh/id_ed25519
Host int-*
ProxyJump bastion
User deploy
IdentityFile ~/.ssh/id_ed25519_internal
Expected output: Typing ssh int-db01 connects through the bastion in one command with the right key for each hop.
2. Enable connection multiplexing for speed.
Host int-*
ControlMaster auto
ControlPath ~/.ssh/cm-%r@%h:%p
ControlPersist 10m
Expected output: The first connection authenticates; subsequent ssh, scp, and rsync reuse the socket for ten minutes with zero re-auth.
3. Verify the socket is actually reused.
time ssh int-db01 true && time ssh int-db01 trueExpected output: The second invocation completes in tens of milliseconds instead of a full handshake and authentication round trip.
4. Restrict what each key can do on the bastion.
# authorized_keys on bastion
restrict,pty ssh-ed25519 AAAA... you@laptop
Expected output: The bastion key permits only port forwarding to permitted targets, not shell access, shrinking the blast radius of a stolen laptop key.
5. Prove there is no shared credential left.
grep -rL "IdentityFile" ~/.ssh/config; ssh -G int-db01 | grep -i identityfileExpected output: Effective config resolution shows per-host keys, and no shared private key exists on any laptop.
Troubleshooting
Problem
Multiplexed sessions hang after network changes
Solution
Stale control sockets survive wi-fi switches; remove them with ControlMaster auto plus a low ControlPersist, or ssh -O exit to close them cleanly.
Problem
Jump works interactively but fails in scripts
Solution
Batch mode disables password prompts; ensure keys are loaded in the agent, and add BatchMode yes to fail fast instead of hanging on prompts.
Problem
Port forwarding through the bastion is denied
Solution
The bastion restricts PermitOpen; add the specific target ports you need to its sshd configuration rather than opening everything.
Limitations
- Multiplexing shares one TCP connection; a network path reset breaks all sessions on that socket simultaneously.
- Bastion availability becomes the availability of all internal access; monitor it like production.
One command, two hops, no shared secrets
Chained SSH commands invite automation shortcuts, and the worst shortcut is a shared bastion key copied onto laptops. ProxyJump declares the hop in configuration, per-host IdentityFiles keep credentials separated, and ControlMaster turns repeated connections into socket reuse that authenticates once per ten minutes. The result is faster than the manual dance and safer than the workaround it replaces.
Host bastion
HostName bastion.example.com
IdentityFile ~/.ssh/id_ed25519
Host int-*
ProxyJump bastion
IdentityFile ~/.ssh/id_ed25519_internal
ControlMaster auto
ControlPath ~/.ssh/cm-%r@%h:%p
ControlPersist 10m
Restrict the key, not just protect it
The bastion authorized_keys entry can carry a restrict option that denies shell access and permits only forwarding to named targets. A stolen laptop key then buys an attacker a tunnel to servers they could already reach indirectly, not a shell on the one host that touches everything.
Fail fast in scripts
Interactive SSH waits for passwords; scripted SSH should never hang waiting for one. BatchMode yes converts any missing credential into an immediate error, which keeps automation logs readable and incident timelines honest. Combined with agent-loaded keys, scripts stay unattended and still authenticate per hop.