Connecting with SSH¶
The ssh module provides a secure client to connect to remote hosts. Connections are configured once using the ssh.config() function and are automatically managed by the runtime, reusing active sessions to eliminate connection handshake overhead.
Configuring the Client¶
To establish a connection, build an SSH client by specifying target hosts, the SSH username, and the path to your private key in the auth dictionary:
def main():
# Configure the SSH client
client = ssh.config(
hosts = ["alice-node-1.local"],
auth = {
"user": "alice",
"key": "~/.ssh/id_ed25519",
},
timeout = "10s",
)
print("SSH client configured for:", client.hosts)
The hosts parameter accepts a list of target hostnames or IP addresses (or a single hostname/IP string). The client will establish connections to all named hosts.
Configuration Parameters¶
You can customize connection behavior using the following arguments in ssh.config():
auth: A dictionary specifying target host credentials (user,key,passphrase,password,use_agent,prompt).jump: An optional dictionary specifying bastion jump host configuration (host,port,user,key,passphrase,password,use_agent,prompt).port: The target SSH port (defaults to22).timeout: A duration string (e.g.,"10s","30s") defining the connection timeout.host_key_check: A boolean (defaults toTrue) to verify the remote host's signature against yourknown_hostsfile.max_retries: The number of reconnection attempts if a connection is dropped.
Bastion Jump Host Routing¶
To connect to private nodes behind an edge bastion, configure the jump dictionary:
client = ssh.config(
hosts = ["10.0.1.10", "10.0.1.11"],
auth = {
"user": "pi",
"key": "~/.ssh/cluster_key",
},
jump = {
"host": "bastion.corp.net",
"user": "vladimir",
"key": "~/.ssh/bastion_key",
},
)
All connection dials, execution sessions, file transfers, and key operations route transparently through the encrypted bastion tunnel.
Host Key Discovery (ssh.scan_host_keys)¶
When connecting with host_key_check = True (the secure default), connections fail if remote host keys are not already in ~/.ssh/known_hosts.
Use ssh.scan_host_keys() (or alias ssh.keyscan()) to retrieve and persist remote public host keys before initiating operations:
# Discover and append host keys to ~/.ssh/known_hosts (supports bastions)
ssh.scan_host_keys(
hosts = ["10.0.1.10", "10.0.1.11"],
save = True,
jump = {"host": "bastion.corp.net", "user": "vladimir"},
)
Public Key Distribution (client.copy_id)¶
To bootstrap or distribute public keys onto fleet nodes, use client.copy_id():
# Install public key (probes first via RFC 4252; skips if already authorized)
client.copy_id(
key = "~/.ssh/id_ed25519.pub",
check_first = True,
sudo = True,
)
When check_first = True (or key_check = True) is passed, Starkite probes each node using the SSH protocol without requiring passwords or shell logins. Hosts that already authorize the key are skipped automatically. By default, copy_id targets the connected user's home directory and sets correct file permissions.
Configuring Default Privileges (Sudo)¶
If your automation primarily performs administrative operations (such as installing software, restarting system daemons, or modifying system configurations), you can enable sudo globally at the client level.
What sudo = True Does under the Hood¶
When you configure sudo = True in ssh.config(), Starkite establishes a client-wide default that automatically prefixes all subsequent remote commands with sudo. By default, sudo elevates the execution context of the command to the root user.
Combining with the SSH User¶
Starkite establishes the initial SSH connection using the SSH user specified in the configuration (e.g., auth = {"user": "alice"}).
When sudo = True is enabled, the runtime authenticates as alice and executes the command as sudo <cmd>.
[!IMPORTANT] This assumes that the SSH user
aliceis configured in the remote host'ssudoersfile and has passwordlesssudoprivileges. If the remote host prompts for a password, the execution will block or fail.
Combining with Target Users (as_user)¶
If you need to run commands as a specific non-root user (e.g., executing database commands as the postgres user or running web server actions as www-data), you can combine the client-wide sudo = True setting with the as_user parameter during command execution:
def run_database_maintenance():
# 1. Connect as 'alice' with global sudo enabled
client = ssh.config(
hosts = ["alice-node-1.local"],
auth = {
"user": "alice",
"key": "~/.ssh/id_ed25519",
},
sudo = True,
)
# 2. Execute a command as 'postgres'
# Starkite translates this to: sudo -u postgres vacuumdb -a
client.exec("vacuumdb -a", as_user="postgres")
For more details on executing commands and overriding defaults on a per-call basis, see the Executing host commands guide.
Required Permissions¶
Establishing network connections over SSH requires explicit authorization. Starkite gates the ssh module behind the allow-net permission profile to prevent scripts from initiating unauthorized network connections.
To run a script that connects to remote hosts over SSH, you must pass the --permissions=allow-net flag at the command line:
Under the default deny-all profile, any attempt to call ssh.config() or interact with the ssh module will be blocked by the runtime.