Setting Up Jump Hosts
A jump host (bastion host) is a single server exposed to the internet that acts as a gateway to your private network. Instead of exposing every server to the public internet, you expose one hardened machine and tunnel through it to reach everything else.
ZestSSH has built-in jump host support via the Connect Via field, so you do not need to manage SSH config files or remember complex command-line syntax.
How It Works
Section titled “How It Works”When you set Connection B’s Connect Via to Connection A, ZestSSH:
- Establishes an SSH connection to Connection A (the jump host).
- Opens a direct-tcpip channel through Connection A to Connection B’s host and port.
- Runs the SSH handshake for Connection B over that channel.
The result is an end-to-end encrypted connection to the destination server, with the jump host acting only as a network relay. The jump host never sees the plaintext of your session with the destination.
Step 1: Configure the Jump Host
Section titled “Step 1: Configure the Jump Host”Create a connection for your WAN-exposed bastion:
- Label:
Bastion - Host: Your public IP or DNS name (e.g.
bastion.example.com) - Port: 22 (or a non-standard port if you have moved it)
- Identity: An SSH key dedicated to this jump host
Hardening Checklist
Section titled “Hardening Checklist”Your bastion is the single point of entry. Secure it:
- Disable password authentication (
PasswordAuthentication noinsshd_config) - Use SSH key authentication only
- Install fail2ban or similar rate-limiting
- Restrict which users can log in (
AllowUsers jumpuser) - Disable agent forwarding on the bastion itself unless needed
- Run on a non-standard port to reduce noise
- Keep the OS and OpenSSH fully patched
Step 2: Configure Private Servers
Section titled “Step 2: Configure Private Servers”For each server behind the jump host, create a ZestSSH connection:
- Label:
DB Primary - Host:
10.0.1.5(private IP, not reachable from the internet) - Port: 22
- Identity: An SSH key for the destination server (this can be the same or different from the bastion key)
- Connect Via: Select
Bastion
When you tap Connect on “DB Primary”, ZestSSH connects to the bastion first, then tunnels through to 10.0.1.5:22.
Step 3: Port Forwarding Through a Jump Host
Section titled “Step 3: Port Forwarding Through a Jump Host”Port forwarding works through jump hosts. If your database server runs PostgreSQL on port 5432, you can create a local port forward:
- Go to the port forward rules for the
Bastionconnection (or the destination connection). - Add a rule:
- Type: Local
- Local Port: 5432
- Remote Host: 10.0.1.5
- Remote Port: 5432
After connecting, your local machine can reach the database at localhost:5432, even though the database is on a private network behind the bastion.
This works because the port forward is established on the bastion’s SSH connection, and the bastion has direct network access to 10.0.1.5.
Multi-Hop Chaining
Section titled “Multi-Hop Chaining”ZestSSH supports chaining jump hosts. This is useful in environments with multiple security zones:
Internet --> Bastion (public) --> DMZ Server --> Internal ServerSet up:
- Bastion: Direct connection to the public IP.
- DMZ Server: Connect Via =
Bastion - Internal Server: Connect Via =
DMZ Server
ZestSSH builds the chain automatically: it connects to the Bastion, tunnels to the DMZ Server, then tunnels again to the Internal Server. Each hop is its own encrypted SSH session.
Performance Considerations
Section titled “Performance Considerations”Each hop adds latency (the SSH handshake at each level). For two or three hops, the added delay is typically under a second. Beyond three hops, you may notice a lag. Keep hop counts reasonable.
Zlib compression (available in the connection editor) can help on slow links but adds CPU overhead at each hop.
Identity Management for Jump Hosts
Section titled “Identity Management for Jump Hosts”There are two common approaches to key management with jump hosts:
Approach A: Separate Keys Per Hop (Recommended)
Section titled “Approach A: Separate Keys Per Hop (Recommended)”Use a different SSH key for the bastion and for each destination server:
- Bastion identity:
bastion-key(Ed25519, stored in ZestSSH) - DB Primary identity:
db-key(Ed25519, stored in ZestSSH)
ZestSSH sends the correct key at each hop automatically based on the identity assigned to each connection. The bastion never needs access to your destination server keys.
Approach B: Agent Forwarding
Section titled “Approach B: Agent Forwarding”Enable Forward Agent on the bastion connection. This lets the bastion use your locally loaded SSH keys to authenticate to destination servers, without the keys being stored on the bastion.
Agent forwarding is convenient but has a security trade-off: anyone with root access on the bastion can use your forwarded agent to impersonate you. Use it only on bastion servers you fully trust.
Using Startup Commands with Jump Hosts
Section titled “Using Startup Commands with Jump Hosts”Startup commands work normally with jump host connections. If you set a startup command like tmux new-session -A -s main on a jump-host-connected server, it runs on the final destination --- not on the jump host.
This is the correct behavior: the startup command is sent to the shell on the server you are ultimately connecting to.
Automation Through Jump Hosts
Section titled “Automation Through Jump Hosts”ZestSSH’s automation API (deep links, Tasker, Shortcuts) works with jump host connections. When you trigger:
zestssh://execute?connection=DB Primary&command=pg_dump mydb > backup.sql&key=KEYZestSSH automatically routes through the bastion to reach DB Primary, runs the command, and returns the result. The automation caller does not need to know about the jump host topology.
Troubleshooting
Section titled “Troubleshooting”“Connection refused” on the destination: The bastion connected fine, but the destination server is rejecting the connection. Verify:
- The destination server’s SSH daemon is running.
- The destination host/port are correct (remember, these are resolved from the bastion’s network, not yours).
- Firewall rules on the destination allow connections from the bastion’s IP.
“Host key verification failed” on the destination: ZestSSH verifies host keys for each hop independently. If the destination server’s host key has changed, you will see a warning. Investigate before accepting --- changed host keys can indicate a man-in-the-middle attack.
Slow connections: Each hop adds an SSH handshake. If latency is a concern, consider using Ed25519 keys (faster handshake than RSA) and enabling keep-alive to avoid repeated reconnections.
Timeout errors: If the bastion connects but the destination times out, the bastion may not have network access to the destination. SSH into the bastion manually and try ssh destination-ip to verify connectivity.