Back to blog

Reaching hosts that only exist behind a bastion

September 5, 2026 7 min read Kubexer Team
SSHBastionJump Host

Someone hands you an address: db-primary.internal. You put it into an SSH client, press Enter, and get a name resolution failure. The address is not wrong. It simply does not mean anything on your machine.

The problem is who resolves the name

When you type ssh db-primary.internal, your own machine performs the DNS lookup and opens the TCP connection. If that name only exists in a private zone served inside a VPC, your resolver has nothing to return. The same applies to a private IP such as 10.0.4.18: the address is syntactically fine and routes to nothing from where you are sitting.

This is worth stating precisely because the common workarounds address the wrong half of the problem. Copying the key onto the bastion and running ssh from there works, but now your private key lives on a shared machine. Opening a manual tunnel first and connecting to localhost works too, but it is a second thing to remember and it breaks the moment the tunnel drops.

ProxyJump: let the last hop resolve it

OpenSSH's ProxyJump (the -J flag) says: connect to the bastion first, and from inside that session, open the connection to the final host.

ssh -J deploy@bastion.example.com ops@db-primary.internal

The important detail is that the bastion resolves db-primary.internal, not your laptop. A name that means nothing where you are is still the correct name from the machine that has to look it up. This is why ProxyJump is the right tool here and a general-purpose HTTP proxy is not: the destination is named and dialled by the last hop in the chain.

Chains are not limited to one hop. Each hop connects onward from inside the previous session, so you can go through a jump host on the edge, then a jump host inside the environment, and finally to the target.

Storing the route on the host, not on the connection

A bastion route is a property of the host. It does not change depending on whether you are opening a shell, browsing files, or forwarding a port — the path to that machine is the path to that machine.

Kubexer Terminal models it that way: the jump route is saved on the host record in your vault. Anything that connects to that host inherits the same definition — a terminal session, an SFTP file browser, a port forward. You describe the route once.

Routes compose. A host can declare that it is reached "via B", while B itself declares that it is reached "via A". Opening the host expands the full chain to A → B → host without you restating it. That matters when a bastion moves: you edit one record and every host behind it follows.

What each hop protects, and what it does not

A jump chain is not one connection routed through machines. Each hop is a separate, independently encrypted SSH session opened inside the previous one. Your session to the final host is negotiated end to end with that host — the bastion in the middle carries ciphertext it holds no key for. It relays bytes it cannot read.

That property is what makes bastions acceptable as shared infrastructure, and it depends entirely on you talking to the machine you think you are talking to. If an intermediate hop could substitute itself for the next one, the guarantee is gone. So every hop gets its own host-key verification: the bastion's key is checked, and the final host's key is checked, separately. A chain is only as trustworthy as its least-verified link.

Two things this does not give you. It does not hide from the bastion that you connected onward, or to where — connection metadata is visible to every hop by construction. And it does not protect you if the final host's key is unknown and you approve it blindly; the check only helps if you read it.

In practice

The workflow ends up being unremarkable, which is the point: you save the target host with its real internal address, point it at the bastion it lives behind, and open it. The chain is built for you, each hop is verified, and the shell comes up on the machine you actually wanted.

Jump chains are a Pro feature in Kubexer Terminal.