Skip to content

feat: vendor ssh in openshell snap and remove ssh-keys interface #2277

Description

@olivercalder

Problem Statement

Currently, the openshell snap uses the ssh-keys interface to get access to the host's ssh binary, which is used for sandbox connect/exec/forward. However, ssh-keys is a privileged interface which also grants access to the public and private ssh keys on the host. As such, it requires manual connection in order to be used.

This weakens the security sandbox of the snap, and hurts the UX of installing it.

Proposed Design

We should be able to vendor the ssh binary within the openshell snap, thus isolating it from the host environment and improve the installation UX.

Snapcraft provides a simple means of staging packages into the snap from the ubuntu releases corresponding to the base (currently core24, so Ubuntu 24.04). So let's do that with the package providing the ssh binary (openssh-client).

This should be safe because:

  • OpenShell never uses host SSH credentials
    • Every ssh invocation in the codebase passes StrictHostKeyChecking=no, UserKnownHostsFile=/dev/null, and GlobalKnownHostsFile=/dev/null (crates/openshell-cli/src/ssh.rs:148-167, crates/openshell-tui/src/lib.rs:921/1082/1529)
    • No ~/.ssh/id_* private keys, ~/.ssh/known_hosts, or authorized_keys are ever consulted
    • The sandbox SSH daemon (russh in openshell-supervisor-process) generates an ephemeral ED25519 host key per boot and authenticates a bearer token relayed over a Unix socket -- it does not use host credentials
  • The ProxyCommand is self-contained
    • The ProxyCommand invokes the openshell binary's own ssh-proxy subcommand (openshell-core/src/forward.rs:786-801), which is a pure-Rust gRPC proxy to the gateway -- it does not shell out to ssh
    • So the outer ssh client only needs to reach the ProxyCommand; no host SSH infrastructure is required
  • russh is fully independent
    • The sandbox supervisor (openshell-supervisor-process/src/ssh.rs) and the gateway client (openshell-server/src/grpc/sandbox.rs) use the russh crate, and does not depend on the ssh binary or the ssh-keys interface
  • ~/.ssh/config is only for OpenShell-managed aliases
    • install_ssh_config (crates/openshell-cli/src/ssh.rs:1570-1587) writes Include + Host openshell-<name> blocks pointing at the OpenShell ProxyCommand
    • resolve_ssh_hostname (crates/openshell-bootstrap/src/metadata.rs:153-194) runs ssh -G only to resolve OpenShell-registered aliases
    • Neither reads host-defined SSH config for credentials, host keys, or external hosts -- the config is entirely OpenShell-authored metadata
  • Under snap confinement, ~/.ssh/config resolves to snap-private space
    • With the default HOME=$SNAP_USER_DATA (~/snap/openshell/current), the bundled ssh reads/writes ~/.ssh/config inside the snap's private data directory — writable without ssh-keys, invisible to the host, and preserved across refreshes by snapd
    • The OpenShell-managed config file lives at $XDG_CONFIG_HOME/openshell/ssh_config ($SNAP_USER_COMMON/.config/openshell/ssh_config), also snap-private
  • The ssh-keys interface provided no unique capability
    • The interface granted read access to ~/.ssh/ dotfiles -- which OpenShell never reads (no keys, no known_hosts) -- and access to the host /usr/bin/ssh binary
    • Vendoring openssh-client into the snap (priming only usr/bin/ssh) replaces the binary dependency
    • The dynamic library dependencies (libcrypto.so.3, libz.so.1, libselinux.so.1, libkrb5.so.3, etc.) are all satisfied by the core24 base snap, which ships the same Ubuntu 24.04 archive versions.

Alternatives Considered

The only downside I see of vendoring the ssh binary and saving the ssh configuration inside the snap is that external tools (e.g. VSCode remote-ssh) can no longer auto-discover Host openshell-<name> blocks in the host ~/.ssh/config. Instead, users may run openshell sandbox ssh-config <name> and paste the output into their host ssh config.

Agent Investigation

No response

Checklist

  • I've reviewed existing issues and the architecture docs
  • This is a design proposal, not a "please build this" request

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions