Start here

Getting started

Connect inside the disposable guest: identities, authorization, host trust, and a first session.

Source snapshot 60c72c2bView original
On this page

This walkthrough connects to a daemon on guest loopback. Build and install in the disposable VM first. Run the following commands inside the guest, reached with:

tools/vm/vm.sh ssh

The SSH session is root in the disposable guest. Start in /work/psh.

Prepare the server

Install the binaries and configuration template:

cd /work/psh
make install

The default template authorizes alice. It must be an existing non-root Linux account with a usable home, executable login shell, and no account or password expiry. UID 65534 is reserved for network workers and cannot log in. If this test account does not yet exist, create it in the disposable guest:

useradd --create-home --shell /bin/bash alice

Generate a dedicated host identity explicitly:

psh keygen -f /etc/psh/host_psh_hybrid.pem --no-passphrase

The daemon never generates keys on startup. Host keys are unencrypted for unattended startup and must remain root-owned with mode 0600 or stricter. The public companion is /etc/psh/host_psh_hybrid.pem.pub. Each identity contains an Ed25519 and an ML-DSA-87 key; no X.509 certificate is needed. Key generation refuses to overwrite existing files.

Create and authorize a client identity

For this walkthrough the guest’s root account runs the client and logs in remotely as alice. The client identity is separate from the host identity:

install -d -m 0700 "$HOME/.config/psh"
psh keygen -f "$HOME/.config/psh/id_psh_hybrid.pem"
install -m 0644 "$HOME/.config/psh/id_psh_hybrid.pem.pub" \
  /etc/psh/authorized/alice.pub.pem

Key generation prompts twice for a nonempty local passphrase. Use --no-passphrase explicitly for an unattended test client. Only the public companion belongs in the authorized directory. These are paired SPKI PEM files, not OpenSSH authorized_keys lines.

Check /etc/psh/pshd.yaml; the installed template contains:

version: 1
listen:
  - '0.0.0.0:2222'
  - '[::]:2222'
host_key: host_psh_hybrid.pem
users:
  alice:
    authorized_keys:
      - authorized/alice.pub.pem

Paths in YAML resolve relative to that configuration file. Keep the server configuration and public authorization files root-owned and not group/world writable, and their directories administrator-controlled. Neither password authentication nor root login has an enabling option.

Validate the configuration and all referenced keys, then start the daemon:

/usr/local/sbin/pshd -f /etc/psh/pshd.yaml --check-config
/usr/local/sbin/pshd -f /etc/psh/pshd.yaml

The daemon stays in the foreground and logs to stderr. Leave this session open. It needs Linux 5.14 or newer and writable cgroup v2 support; see runtime requirements .

Configure and connect

Open a second guest SSH session with tools/vm/vm.sh ssh. Create ~/.config/psh/config.yaml, owned by the client account and not group/world writable:

version: 1
identity: id_psh_hybrid.pem
known_hosts: known_hosts
hosts:
  lab:
    hostname: 127.0.0.1
    user: alice
    port: 2222
chmod 0600 "$HOME/.config/psh/config.yaml"

Before connecting, obtain the server fingerprint through a trusted channel. In this single-guest walkthrough, inspect the public file directly:

psh fingerprint /etc/psh/host_psh_hybrid.pem.pub
psh lab

Compare the displayed complete-pair fingerprint and type yes at the first-use prompt only after verification. Subsequent connections use the saved pin. Changed host keys always fail.

psh lab -- 'uname -a; id'
psh -t lab -- 'stty size'
psh -T lab -- cat < input.bin > output.bin

Command arguments are joined with spaces and interpreted by the remote account’s shell using -c; quote a complete command when it contains shell syntax. With no command, psh requests a login shell. PTYs are automatic only when no command is supplied and local stdin is a terminal. Use -t to force one or -T for separate binary streams.

Provision trust for automation

Obtain the complete host public identity through a trusted channel. Pin the actual hostname and port used by the connection; host-management commands do not expand YAML aliases or read the client configuration.

Batch mode needs an unencrypted client identity generated with the explicit --no-passphrase option earlier, plus either a saved pin or a matching --host-key file. It cannot unlock an encrypted identity or prompt for unknown hosts. With that prerequisite:

psh host trust 127.0.0.1 --host-key /etc/psh/host_psh_hybrid.pem.pub
psh --batch lab -- 'id -u'

On a separate machine, copy only the verified host public file to a client-owned protected location.

To deliberately rotate a pin, verify the replacement independently, then use:

psh host forget 127.0.0.1 -p 2222
psh host trust 127.0.0.1 -p 2222 --host-key replacement-host.pem.pub

If configuration uses a custom trust store, supply that same --known-hosts path to both commands. Keep its protected .lock sidecar alongside the store.

Operate a session and daemon

Keepalives run every 15 seconds. After 120 seconds without authenticated peer traffic, the connection expires and its remote process group is terminated. Client address changes preserve a live session after encrypted return-path validation. SIGUSR1 requests a fresh client UDP socket; SIGWINCH forwards terminal dimensions. Reinvoking psh starts a new session, not a reattachment.

Send SIGHUP to the daemon to validate and reload configuration and keys. Invalid replacements leave the old configuration active. Removed identities terminate their sessions, pending authentications are cancelled, and host-key rotation closes existing sessions. Listener changes require a restart. SIGINT or SIGTERM shuts down the daemon and its sessions.

For the complete option, signal, and exit-status reference, see psh(1) , pshd(8) , and psh_config(5) .

END OF DOCUMENT / psh/3All documentation