← All docs

Connect via an SSH tunnel

When the database isn't reachable directly from the agent but a bastion host is, the agent can open an SSH channel to the bastion and tunnel the database connection through it. Nothing about the database needs to be exposed to the internet — only SSH to the bastion.

agent ──SSH──▶ bastion ──▶ database (private)

1. Prepare the bastion

2. Create a key pair for the agent

ssh-keygen -t ed25519 -f shaft_tunnel_key -N "" -C "shaft-agent"

Add the public key to the bastion user's authorized keys:

sudo tee -a ~shaft-tunnel/.ssh/authorized_keys < shaft_tunnel_key.pub
sudo chmod 600 ~shaft-tunnel/.ssh/authorized_keys
sudo chown -R shaft-tunnel:shaft-tunnel ~shaft-tunnel/.ssh

Optionally lock the key down to port-forwarding only (no shell) by prefixing the authorized_keys line with:

no-pty,permitopen="DB_HOST:5432",command="/bin/false" ssh-ed25519 AAAA...

3. Give the private key to the agent

Copy the private key onto the agent host, readable only by the agent user:

sudo install -o shaft -g shaft -m 600 shaft_tunnel_key /opt/shaft-agent/tls/ssh_key

4. Configure the connector in the portal

On the connector's Configure page, choose Connection → SSH tunnel and set:

| Field | Value | |-------|-------| | SSH host | the bastion's hostname or IP | | SSH port | usually 22 | | SSH user | shaft-tunnel | | SSH private key | /opt/shaft-agent/tls/ssh_key (or an env: reference) | | Known hosts | how to verify the bastion (see step 5) |

Keep the connector's JDBC URL pointing at the database as seen from the bastion (e.g. jdbc:postgresql://db.internal:5432/appdb). The agent opens the tunnel, forwards a local port to that host/port, and connects through it — the tunnel stays up for the life of the task and is torn down when the task stops.

5. Verify the bastion's host key

The agent checks the bastion against known_hosts and refuses to connect to an unrecognised host key, so record the key up front. Capture it with ssh-keyscan:

ssh-keyscan -t ed25519 BASTION_HOST
# e.g. bastion.example.com ssh-ed25519 AAAAC3Nza...

Then set the Known hosts field to either:

Verify the fingerprint out-of-band before trusting it. If no known_hosts entry matches, the tunnel fails closed — the agent never trusts an unknown key.

More guides