pysnap.ssh package

ssh access to guests that have no console on COM1.

PySnap runs the system OpenSSH client (ssh) with its own key pair and its own known_hosts files, so the user’s keys and ~/.ssh/known_hosts are never read or changed.

Submodules

pysnap.ssh.client module

Run the system ssh client against a forwarded guest port.

Every call uses the PySnap key, the known_hosts file of the VM with StrictHostKeyChecking=accept-new (the first key is trusted, a changed key is refused) and no user configuration (-F with the null device), so the behaviour does not depend on ~/.ssh. Non-interactive calls run in batch mode: they never ask for a password.

class pysnap.ssh.client.SshTarget(vm_name, vm_uuid, port, user)[source]

Bases: object

Describe where and as whom to connect.

Parameters:
  • vm_name (str) – VM name, used in messages.

  • vm_uuid (str) – VM UUID, selects the known_hosts file.

  • port (int) – Host port forwarded to the guest ssh server.

  • user (str) – Guest user.

vm_name: str
vm_uuid: str
port: int
user: str
class pysnap.ssh.client.AccessCheck(status, message='')[source]

Bases: object

Result of a key login attempt.

Parameters:
  • status (str) – "ok", "denied" (the server refused the key) or "unreachable" (no ssh server answered).

  • message (str) – Error output of ssh.

status: str
message: str = ''
pysnap.ssh.client.ssh_command(target, store, *, batch=True, tty=False, remote_command=None, extra=())[source]

Build an ssh command line.

Parameters:
  • target (SshTarget) – Connection target.

  • store (SshKeyStore) – PySnap key store.

  • batch (bool) – Use the PySnap key only and never ask for a password.

  • tty (bool) – Request a terminal on the guest (-tt).

  • remote_command (str | None) – Command for the guest shell; a login shell without.

  • extra (Sequence[str]) – Further ssh options placed before the destination.

Returns:

Argument list.

Return type:

list[str]

pysnap.ssh.client.check_key_access(target, store)[source]

Try to log in with the PySnap key.

Parameters:
Returns:

Whether the key works, is refused or no server answers.

Return type:

AccessCheck

pysnap.ssh.client.run_remote(target, store, command, *, timeout=None)[source]

Run one command on the guest with the PySnap key.

Parameters:
  • target (SshTarget) – Connection target.

  • store (SshKeyStore) – PySnap key store.

  • command (str) – Shell command line for the guest.

  • timeout (float | None) – Seconds to wait.

Returns:

Completed process with bytes output.

Return type:

CompletedProcess

async pysnap.ssh.client.open_terminal(target, store, *, columns, rows)[source]

Start an interactive guest shell on a pseudo terminal.

The guest terminal gets the given size first; output of ssh itself (errors) is merged into the stream.

Parameters:
  • target (SshTarget) – Connection target.

  • store (SshKeyStore) – PySnap key store.

  • columns (int) – Terminal width.

  • rows (int) – Terminal height.

Returns:

Reader, writer and the ssh process.

Return type:

tuple[StreamReader, StreamWriter, Process]

pysnap.ssh.client.install_script(public_key, *, with_root)[source]

Return the guest shell script that adds the PySnap key.

The key is appended to ~/.ssh/authorized_keys unless it is there already. With with_root, a user other than root then runs the same for root through su; the guest asks for the root password, and an empty or wrong one only skips root.

Parameters:
  • public_key (str) – Public key line.

  • with_root (bool) – Also install the key for root.

Returns:

Script for sh -c.

Return type:

str

pysnap.ssh.client.install_key_interactively(target, store)[source]

Log in with a password in the current terminal and add the key.

ssh asks for the password of target.user; the guest may ask for the root password. Nothing is captured, the user sees the whole dialogue.

Parameters:
  • target (SshTarget) – Connection target with the password login user.

  • store (SshKeyStore) – PySnap key store.

Returns:

Exit status of ssh.

Return type:

int

pysnap.ssh.client.check_or_raise(target, store)[source]

Raise a helpful error unless the PySnap key logs in.

Parameters:
Raises:

PySnapError – When the key is refused or no server answers.

Return type:

None

pysnap.ssh.keys module

The PySnap ssh key pair and per-VM known_hosts files.

Everything lives in ~/.pysnap/ssh (mode 0700): the key pair pysnap_vm_access_ed25519 with its .pub file, created once by ssh-keygen without a passphrase, and known_hosts.d/<VM UUID>, one known_hosts file per VM. Both the directory and the file names are unique to PySnap, and existing files are never overwritten.

pysnap.ssh.keys.find_executable(name)[source]

Return the path of an OpenSSH program.

Parameters:

name (str) – ssh or ssh-keygen.

Returns:

Executable path.

Raises:

PySnapError – When the program is not installed.

Return type:

str

class pysnap.ssh.keys.SshKeyStore(root=None)[source]

Bases: object

Locate and create the PySnap key pair and known_hosts files.

Parameters:

root (Path | None)

property private_key: Path[source]

Return the private key path.

property public_key: Path[source]

Return the public key path.

known_hosts(vm_uuid)[source]

Return the known_hosts file of one VM, creating its directory.

Parameters:

vm_uuid (str) – VirtualBox UUID of the VM.

Returns:

File path; the file itself is written by ssh.

Raises:

PySnapError – For an unusable UUID.

Return type:

Path

ensure_key()[source]

Return the private key, creating the key pair on first use.

Returns:

Private key path.

Raises:

PySnapError – When only one half of the pair exists, when a path is taken by something else or ssh-keygen fails.

Return type:

Path

public_key_text()[source]

Return the public key line, creating the key pair if needed.

Returns:

One ssh-ed25519 ... comment line.

Return type:

str