← SSH: secure access and production diagnosis
12 / 12 · 60 MIN

SSH bastions and operational acceptance

Locate failures between jump and target with ProxyJump and prepare access acceptance covering job context and authorized boundaries.

Distinguish identities at each hop

ProxyJump reaches a target through an SSH connection to an intermediate hop. The client authenticates to the jump, requests a TCP channel to the target, and establishes the final SSH session through that path. The target retains its own host identity verification and account authentication. Copying a private key to the bastion is unnecessary. The lab uses two explicitly selected local identities, IdentityAgent none, and ForwardAgent no. The jump and target alias configurations specify different ports and IdentityFile values. Options supplied to the target generally do not apply to the jump. For a job, compare both contexts: executing local account, files read, arguments, identities, remote accounts, and trust references.

Use negative controls to locate the stage

jumpAllowed authenticates to both local servers and the target prints fixedTarget=true. jumpWrongKey offers the wrong key to the first hop: there is no jump acceptance or final execution. targetWrongKey accepts the first hop, but the final server rejects the identity selected for target. jumpForwardDenied accepts the jump account and blocks the TCP channel before target authentication. The four results have different diagnostic consequences. Channel refusal points to policies such as AllowTcpForwarding and PermitOpen; final key rejection points to account and authorization at the target. Do not use status 255 alone to assign cause. Correlate client messages with separate logs from both servers.

Turn the lab into change criteria

The two servers use loopback on one machine and share a disposable host key to simplify the fixture. This does not represent network isolation, identity management, or trust in real banking infrastructure. The lab demonstrates mechanisms and stages without accepting a production migration. For an authorized change, identify each host, port, account, trust reference, client identity, and permitted destination. Add positive and negative tests, window, owner, observation, and reversal. Acceptance must include scheduler context and an application operation with an expected result. Also confirm time limits, failure handling, and partial effects before retrying financial operations. The bank examples are fictional and do not represent BNP Paribas policies.

Practice the decision and prepare RUN handover

Use the guide below in a forty-minute session: eight minutes to map endpoints, twelve to compare failures, twelve to decide corrections, and eight to prepare acceptance. The support owner locates the stage; the manager coordinates owner, scope, and window; the reviewer requires evidence of success and refusal. Start with Ria, where the listener is healthy and the backend unavailable. Move to Farol, where only final authentication fails, and Lago, where remote listening was not authorized. Deliver an access map, a justified decision, and RUN handover criteria. Execution with participants has not yet occurred. Always summarize what was observed, what was inferred, and what remains to be validated in the real environment. Handover should state who receives each alert and which evidence distinguishes unavailable access from an unavailable application. Record a review deadline for temporary access and the owner responsible for removing it.

FORTY-MINUTE GUIDE: FORWARDING AND BASTIONS
Fictional cases; these do not represent any bank’s policies.
Complete code: SSH forwarding and service evidence lesson.
Prerequisites: Python 3, POSIX, usable non-root local account,
five matching OpenSSH 10.5p1 binaries. Executed build excludes PAM.
Replace /path/to with actual authorized executable paths.
The runner opens loopback only and removes temporary processes and keys.

EXECUTION
python3 run.py \
 --ssh /path/to/openssh-10.5p1/ssh \
 --sshd /path/to/openssh-10.5p1/sshd \
 --sshd-session /path/to/openssh-10.5p1/sshd-session \
 --sshd-auth /path/to/openssh-10.5p1/sshd-auth \
 --keygen /path/to/openssh-10.5p1/ssh-keygen \
 --output evidence.json

0–8 MINUTES: MAP
Per endpoint: listener, destination, port, account, identity, trust.
State who initiates the final connection for -L and -R.

8–20 MINUTES: EVIDENCE
Ria: live listener, Connection refused, backend not listening.
Farol: jump Accepted publickey, target Failed publickey.
Lago: accepted account, remote port forwarding failed, PermitListen none.
For each case: observed stage, hypothesis, confirmation still needed.

20–32 MINUTES: DECISION
Owner, correction within scope, positive and negative control.
Do not broaden to any without a new authorized access decision.
Do not retry financial operations without checking partial effects.

32–40 MINUTES: ACCEPTANCE
Authorized operation in job context, result, deadline, and reversal.
Distinguish fixture marker from TLS and application acceptance.
Deliver: map, decision, evidence, and remaining RUN gaps.
Guide has not yet been executed with participants.
IN PRACTICE

Farol accepts the jump account and rejects the target key. Diagnosis retains the working jump and compares target alias configuration in scheduler context.

Common pitfalls

Rotating the jump key when failure is at the target, enabling agent forwarding unnecessarily, confusing bastion trust with target trust, or claiming real acceptance from loopback.

Related topics: Change management · L3 production support

Take this idea with you

A bastion connection contains distinct trust, authentication, and channel decisions. RUN handover needs evidence of the real path and consuming operation.

Create account

Reference: OpenSSH forwarding and jump hosts · OpenSSH concepts and OpenBSD-current manuals consulted 2026-09-29; distribution defaults vary