-
-
Notifications
You must be signed in to change notification settings - Fork 0
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.
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 ...")).
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.
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_commandsUse 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.
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.