ProxyJump
Reach a final SSH server through one or more SSH jump hosts before you open shell, exec, SFTP, or forwarding work on the final connection.
ProxyJump defines the SSH hop chain used to reach a final server through one or more bastion hosts.
+-----------+ +----------------------+ +---------------------+
| Local app | ---> | Jump host | ---> | Final SSH server |
+-----------+ +----------------------+ +---------------------+
| 1. Authenticate here |
| 2. Open direct-tcpip |
| to the next hop |
+----------------------+
What actually happens:
1. SSH connection #1 is established to the jump host
2. A direct-tcpip channel is opened through that jump host
3. SSH connection #2 is established inside that channel
4. The returned SSHConnection is the final target connectionExample: Reach An Internal Host Through A Bastion
import Traversio
let configuration = SSHClientConfiguration(
host: "db-admin.internal",
username: "deploy",
authentication: .password("final-target-password"),
hostKeyPolicy: .knownHostsFile("/Users/me/.ssh/known_hosts"),
compressionPreference: .delayedZlib,
proxyJumpHosts: [
SSHProxyJumpHost(
host: "bastion.example.com",
username: "deploy",
authentication: .password("bastion-password"),
hostKeyPolicy: .knownHostsFile("/Users/me/.ssh/known_hosts"),
compressionPreference: .delayedZlib
)
]
)
let result = try await SSHClient.withConnection(configuration: configuration) { connection in
try await connection.execute("hostname")
}Connection flow:
- Traversio opens an SSH connection to
bastion.example.com - Traversio authenticates and verifies that hop
- Traversio opens a
direct-tcpipchannel through that hop todb-admin.internal:22 - Traversio runs a second SSH handshake inside that channel
- the returned
SSHConnectionis the final target connection
Multi-Hop Example
proxyJumpHosts is an array because real networks often need more than one hop:
let configuration = SSHClientConfiguration(
host: "final.internal",
username: "deploy",
authentication: .password("final-password"),
hostKeyPolicy: .acceptAnyVerifiedHostKey,
proxyJumpHosts: [
SSHProxyJumpHost(
host: "bastion-a.example.com",
username: "jump-a",
authentication: .password("jump-a-password"),
hostKeyPolicy: .acceptAnyVerifiedHostKey
),
SSHProxyJumpHost(
host: "bastion-b.internal",
username: "jump-b",
authentication: .password("jump-b-password"),
hostKeyPolicy: .acceptAnyVerifiedHostKey
),
]
)Each hop has its own:
- host
- port
- username
- authentication
- host key policy
- compression preference
- automatic rekey policy
The final target keeps its transport settings on the root SSHClientConfiguration. Bastion hosts often use different credentials, trust rules, or transport policy than the final server.
Connection Proxies And ProxyJump
These features cover different layers of the route:
- ProxyJump: SSH to hop A first, then carry the next SSH hop inside A
- HTTP/SOCKS connection proxy: send the outer TCP connection through a non-SSH proxy before SSH starts
Traversio supports ProxyJump and user-specified HTTP CONNECT or SOCKS5 outer connection proxies through SSHClientConfiguration.connectionProxy.
If you set both:
connectionProxycontrols how Traversio reaches the first hopproxyJumpHostscontrols which SSH hop comes first after that tunnel exists
Validation
ProxyJump is validated through a real first-hop SSH connection plus a nested final-target SSH handshake through that hop.
The documented path also shares the broader Traversio connection validation story: host-key trust, authentication, compression, rekey, setup timeout, connection proxies, and structured diagnostics all run through the same public connection machinery.
Applications should still validate their own bastion topology, host-key policy, auth methods, and proxy environment before critical rollout.