SSHKey

Forward a local private key into workstations as an ambient ssh-agent, so processes on the workstation can authenticate outbound.

An SSHKey makes one of your local private keys available inside selected workstations, as a forwarded ssh-agent, never as a file on the workstation. It is how a git clone from a private repo, a service, or an interactive shell running on the workstation authenticates outbound using a key that stays on your machine.

apiVersion: workstations.ringleader.dev/v1
kind: SSHKey

Not the same as SSH access to the workstation

SSHKey is about keys the workstation uses to reach other systems (GitHub, a package registry). Who may SSH into the workstation is a separate model: ownership plus Grants. Don’t confuse the two.

How it works

An SSHKey names a local private key path and an optional label selector. The daemon forwards every matching key into the workstation as an in-process ssh-agent keyring, exposed over its persistent connection at a canonical socket on the workstation and wired into an ambient SSH_AUTH_SOCK. That variable is set by sshd itself, before it execs your shell, so it reaches every SSH session whatever login shell the workstation runs: an interactive rl shell, a non-interactive remote command, a script, an on-box AI agent, and scp/rsync alike all see the same forwarded keys. That last step is best-effort: it needs the workstation to grant Ringleader’s delivery sudo rule and an sshd whose configuration Includes /etc/ssh/sshd_config.d/, both of which hold on every image Ringleader ships.

On a custom image, rl troubleshoot my-box reports it: an ambient agent line in the controller view names the precondition that stopped the delivery. It is written only when something is missing, so a workstation that got both files shows no such line. An ssh-agent-env probe under system probes confirms the working case from inside the same non-login session the failure appears in. See rl shell for the full check.

The private keys live only in the daemon’s memory, or, for a key with source: agent, in your own ssh-agent. They are never written to the workstation’s disk, and the agent socket is live only while your daemon holds the connection.

Selection uses Kubernetes-style label matching: an SSHKey attaches to a workstation when its selector.matchLabels are a subset of the workstation’s labels, and an empty or absent selector matches every workstation (an unconditional key).

Because keys are device-local, an SSHKey is a device-local kind: it never mirrors or routes to another origin, and it only ever forwards from the device that declares it.

Example

Forward your GitHub key into every tier: dev workstation:

apiVersion: workstations.ringleader.dev/v1
kind: SSHKey
metadata:
  name: github
  namespace: dev
spec:
  path: ~/.ssh/id_ed25519
  selector:
    matchLabels:
      tier: dev

An unconditional key (forwarded into every workstation) simply omits the selector:

spec:
  path: ~/.ssh/id_ed25519

Keeping the key in your ssh-agent

By default the daemon reads the private key file and holds it itself, so the file must be readable without a passphrase. To use a key protected by a passphrase, or one on a hardware token, set source: agent. The key then stays in the ssh-agent on your machine, and the daemon passes each signing request from the workstation to it.

With source: agent, path still names the private key. The daemon reads <path>.pub to know which agent key to use. Keep <path>.pub next to the private key. The daemon skips an agent key whose <path>.pub it cannot read.

spec:
  path: ~/.ssh/id_ed25519_sk
  source: agent

Or from the command line:

rl sshkey create github --path ~/.ssh/id_ed25519_sk --source agent

The key has to be loaded in your agent (ssh-add) for a workstation to use it.

The daemon reaches your agent through the SSH_AUTH_SOCK it started with. A daemon started without one cannot use a source: agent key. When that leaves a workstation with no key, rl troubleshoot says so. Restart the daemon from a shell that has an agent, or use source: file.

Spec fields

FieldTypeDescription
pathstringRequired. Path to the local private key on this device. A leading ~ expands to your home directory. With source: agent, the daemon reads <path>.pub instead.
sourcestringfile (the default): the daemon reads the key file and holds the key. agent: the key stays in your ssh-agent, which signs each request.
selector.matchLabelsmapWhich workstations receive the forwarded key. Empty/absent matches every workstation.
enabledboolMaster on/off switch (default true; an absent field reads as enabled). When false, the key is not forwarded into any workstation, even one its selector matches. Selection still records the match, but forwarding is paused.

Creating a key from the command line

rl sshkey create builds an SSHKey from flags and applies it:

rl sshkey create github --path ~/.ssh/id_ed25519
rl sshkey create github --path ~/.ssh/id_ed25519 --dry-run -o yaml   # print, don't apply

With --source agent, it prints a note if it cannot read <path>.pub.

FlagDescription
--path <path>Required. Path to the private key. With --source agent, the daemon reads <path>.pub.
--source <source>file (default) or agent.
--dry-runPrint the generated object instead of applying it.
-o, --output <format>With --dry-run, output format (yaml).
-n, --namespace <ns>Namespace.
--as <subject>Impersonate a subject.
--home <dir>Data directory.

Enabling and disabling a key

Flip the enabled toggle from the command line without editing the manifest, by name, or across every matching key with a label selector:

rl sshkey enable github
rl sshkey disable github
rl sshkey disable -l tier=dev     # every matching key
FlagDescription
-l, --selector <selector>Label selector (key=value[,key!=value,key in (a,b),key,!key]) instead of a name.
-n, --namespace <ns>Namespace.
--as <subject>Impersonate a subject.
--home <dir>Data directory.

Status

None. An SSHKey has no status: it is a device-local selection rule the daemon acts on directly.