Documentation
¶
Overview ¶
Package sshd is the embedded SSH server that runs inside the microVM (in the ramdisk's weft-microvm-agent process). Replaces the "depend on whatever sshd ships in the container image" approach : Docker / scratch / distroless images don't carry sshd, so ops access via SSH would otherwise require the workload owner to bake one in. With this server, ops always reach the VM via a dedicated :2222 listener on wg0, regardless of what's inside the container.
Auth is wired through the AuthStore declared here — the existing pkg/sshkeys subscriber (commit 032f346) keeps it fresh via NATS pushes ; the sshd reads from it on every connection. No authorized_keys file in the loop ; the source of truth is the in-memory store.
Shell exec'd is the VM's own shell (PID-1 namespace), not the container's. Clean separation : SSH = the VM's runtime, `weft-microvm exec <container> sh` = the workload. Operators get both axes.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func LoadOrCreateHostKey ¶
LoadOrCreateHostKey returns the ed25519 host key for the SSH server. On first boot it generates a fresh key + persists the PEM-encoded private half to path with mode 0600 ; on subsequent boots it loads the existing key so the fingerprint stays stable (operators add `vmname,ip ssh-ed25519 ...` to their known_hosts once and trust it across reboots).
path must point at a writable directory the agent has access to ; /var/lib/weft is the convention. The parent directory is created with 0700 on first run.
Types ¶
type AuthStore ¶
type AuthStore struct {
// contains filtered or unexported fields
}
AuthStore holds the current authorised public keys for the VM. Updated atomically (replace-set) by the sshkeys subscriber on every NATS push ; queried by the sshd's PublicKeyCallback on every connection. The lookup map is keyed by the SSH wire-format fingerprint of the key (ssh.FingerprintSHA256) for O(1) match.
Empty store = no key authorised, which is a legitimate "revoked all" state — every connection is refused.
func NewAuthStore ¶
func NewAuthStore() *AuthStore
NewAuthStore returns an empty store. The first Replace populates it ; until then every Authorize returns false.
func (*AuthStore) Authorize ¶
Authorize answers "is this public key in the current set?". On match, returns the human-readable name (the OpenSSH comment) so the caller can log "session opened by <name>".
Equality is by fingerprint — comparing ssh.PublicKey values directly would be byte-by-byte over the wire encoding, which is what the fingerprint already covers.
func (*AuthStore) Replace ¶
Replace swaps the authorised set for the contents of ks. Atomic at the store level — concurrent Authorize calls either see the old or the new set, never a partial mix. Unparseable lines are silently dropped (the host-side catalogue should have caught them already ; this is the guest's defence in depth).
Returns the count of accepted keys + the count of rejected lines so the subscriber's log can surface a partial push.
type Server ¶
type Server struct {
// AuthStore is consulted on every PublicKeyCallback. Must be
// non-nil ; an empty store means "no key authorised" (every
// connection refused).
AuthStore *AuthStore
// HostKey is the server's identity. Generate via
// LoadOrCreateHostKey ; the same key across reboots makes
// known_hosts entries durable.
HostKey ssh.Signer
// Shell is the path the session execs in a PTY. /bin/sh by
// convention ; operator can override (e.g. /bin/ash on Alpine).
Shell string
// Logger receives connection + error lines. Defaults to the
// stdlib log when nil.
Logger *log.Logger
}
Server is the per-VM SSH listener. Construct via NewServer ; call Serve on a net.Listener (which the caller binds to wg0:2222 in production).