sshd

package
v0.4.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 20, 2026 License: BSD-3-Clause Imports: 17 Imported by: 0

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

func LoadOrCreateHostKey(path string) (ssh.Signer, error)

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

func (s *AuthStore) Authorize(key ssh.PublicKey) (name string, ok bool)

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

func (s *AuthStore) Replace(ks sshkeys.KeySet) (accepted, rejected int)

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.

func (*AuthStore) Size

func (s *AuthStore) Size() int

Size returns the number of currently-authorised keys. Useful for log lines + the sshd's "ready" announcement.

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).

func NewServer

func NewServer(store *AuthStore, hostKey ssh.Signer, shell string, logger *log.Logger) (*Server, error)

NewServer fails when any required field is unset. The early-fail pattern beats discovering it on first connection.

func (*Server) Serve

func (s *Server) Serve(ln net.Listener) error

Serve accepts on ln until it errors (closed listener -> return nil). Each accepted connection runs in its own goroutine ; errors at the per-connection layer are logged, never propagated to Serve's return.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL