Skip to content

SSH and Remote Execution

or1k edited this page Aug 6, 2026 · 1 revision

SSH and Remote Execution

Shared plumbing in src/ssh_exec.rs (a session + exec wrapper around ssh2) and src/ssh_tunnel.rs (a local-port-forward built on top of it). Used directly by sshuser, clickhouse, logs_mgr, sslcert, and by the DB managers (mysql_mgr, postgres_mgr) when a connection profile has "use tunnel" checked.

ssh_exec::Credentials

pub struct Credentials {
    pub user: String,
    pub password: String,          // used if non-empty
    pub private_key_path: String,  // used if password is empty; run through config::expand_path internally
}

Password auth wins if both are set; otherwise falls back to the key. A Credentials with neither set is a caller error (SshSession::connect returns Err("no auth method provided ...")).

One-shot commands

ssh_exec::run_commands(host, port, &creds, &commands: &[String]) -> Result<(String, String), String>

Connects, joins commands with &&, runs them inside bash -lc '...' in one round trip, returns (stdout, stderr). This is what the sshuser CLI subcommand uses for its one-off "add/remove user" invocations — reach for this when a tool just needs to fire off a batch of commands and doesn't need the session to stay open.

A longer-lived session

let sess = ssh_exec::SshSession::connect(host, port, &creds)?;
sess.exec_raw(cmd) -> Result<(stdout, stderr, exit_status), String>       // doesn't fail on non-zero exit
sess.exec_checked(cmd) -> Result<(stdout, stderr), String>                // Err if exit_status != 0
sess.exec_batch(&commands) -> Result<(stdout, stderr), String>            // same joining as run_commands

Use this when a tool needs to run several separate commands against the same host without reconnecting each time (e.g. the SSL Certificate Manager detecting the web server, then reading its config, then checking the cert — several distinct exec calls, one session).

Escaping: ssh_exec::escape_single_quotes(s) is what exec_batch already uses internally to safely embed a joined command inside bash -lc '...' — if you're building a command string by hand instead of going through exec_batch, run any untrusted/user-supplied piece of it through this first. Never string-format user input directly into a shell command.

Tunneling to a DB behind a jump host (ssh_tunnel.rs)

ssh_tunnel::open(ssh_host, ssh_port, &creds, remote_host, remote_port: u16) -> Result<Tunnel, String>

Opens an SSH direct-tcpip channel (SshSession::direct_tcpip) and pumps bytes between it and a freshly bound local TCP listener, returning a Tunnel { local_port: u16, .. }. Point your DB driver at 127.0.0.1:<local_port> instead of the real host — see mysql_mgr::client::connect for the full pattern: if the connection profile has use_tunnel set, open the tunnel first and connect to 127.0.0.1 + the tunnel's local port; otherwise connect directly to cfg.host. This is the standard way any new "connect to a database/service that's only reachable from inside the remote network" tool should work — don't shell out to a real ssh -L subprocess for this, use ssh_tunnel.

Note remote_host here is resolved from the SSH jump host's side — it's often an internal hostname/IP the DB isn't reachable at directly from wherever atk itself is running, which is the whole reason a tunnel is needed. Make sure any "Host" field in a new tool's connection form is documented as such if it has the same shape.

Clone this wiki locally