Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Phase 406 — authenticated SSH proof

ItemValue
Commandmake phase 406
Underlying make target/scriptsvm/phase4/materialize-etc.sh --ssh, then vm/phase4/ssh-probe.sh
Mutates disk/image?Yes, it mounts artifacts/onix-image/onix.raw and installs a bootstrap SSH server, user, keys, and service
Boots QEMU?Yes, it runs an automated SSH proof through QEMU port forwarding
Main proofThe host authenticates with an SSH key to onix@127.0.0.1:7626 and receives ONIX_SSH_OK user=onix uid=1000.

What this phase proves

Phase 405 proved host-to-guest TCP reachability, but it was not authenticated.

Phase 406 proves a stronger thing:

the host can authenticate to the booted ONIX VM over SSH using a public key

This is the first real remote-access proof.

Why Dropbear, not OpenSSH yet?

Both pkgsMusl.dropbear and pkgsMusl.openssh exist in the pinned Nix world.

Phase 406 chooses Dropbear because it is small and good for bootstrap systems.

That fits the current image better:

tiny booted base
temporary bootstrap services
prove the concept first
final policy later

OpenSSH may still be the final choice later. This phase does not decide that forever.

Authentication policy

Phase 406 is intentionally stricter than Phase 405.

The Dropbear service is started with:

-s    disable password logins
-w    disallow root logins
-j    disable local port forwarding
-k    disable remote port forwarding

So the proof is:

public-key auth only
non-root user only
no password auth
no root SSH login

The bootstrap user is:

user   onix
uid    1000
gid    100
home   /home/onix
shell  /bin/sh

The root account remains non-interactive from the normal account database point of view:

root ... /usr/sbin/nologin

That means Phase 406 does not silently undo the safety decision from Phase 402.

Where the key lives

The host-side proof key is:

vm/state/id_ed25519
vm/state/id_ed25519.pub

If the key does not exist, Phase 406 generates it.

The guest-side authorized key is installed under the persistent home tree:

/persist/home/onix/.ssh/authorized_keys

Why /persist/home instead of /home while building the image?

Because at boot ONIX bind-mounts:

/persist/home -> /home

So if we wrote only to the root filesystem’s /home, it would be hidden after the real persistent home mount appears.

This is the same kind of mount lesson we learned earlier with /persist/nix.

Host keys

SSH servers need host keys too.

The host key identifies the guest server to clients.

Phase 406 generates:

/etc/dropbear/dropbear_ed25519_host_key

If that file already exists, it is preserved.

This is still bootstrap policy. A final installed system needs a better story for host-key lifecycle and persistence.

QEMU forwarding

The guest listens on the normal SSH port:

guest 0.0.0.0:22

QEMU forwards a host-local test port:

host 127.0.0.1:7626 -> guest :22

So the host proof command uses:

ssh -i vm/state/id_ed25519 -p 7626 onix@127.0.0.1 ...

The automated proof

make phase 406 first installs the pieces:

./materialize-etc.sh --ssh

That installs:

pkgsMusl.dropbear closure
/usr/lib/sysusers.d/onix-ssh.conf
/persist/home/onix/.ssh/authorized_keys
/etc/dropbear/dropbear_ed25519_host_key
/usr/lib/onix/bootstrap-ssh-status
/usr/lib/onix/bootstrap-ssh-proof
onix-bootstrap-dropbear.service

Then it boots QEMU:

./ssh-probe.sh

The probe uses the serial shell first to ask the guest:

/usr/lib/onix/bootstrap-network-proof &&
/usr/lib/onix/bootstrap-ssh-proof

The guest must answer:

ONIX_SSH_READY user=onix port=22

Then the host runs SSH:

ssh -i vm/state/id_ed25519 -p 7626 onix@127.0.0.1 \
  'printf "ONIX_SSH_OK user=$(/bin/id -un) uid=$(/bin/id -u) ...\n"'

The host must receive:

ONIX_SSH_OK user=onix uid=1000

That proves:

  • Dropbear started in the guest,
  • QEMU forwarded the host port to the guest,
  • the onix user exists,
  • /home/onix/.ssh/authorized_keys is visible through /persist/home,
  • password auth is not needed,
  • public-key auth succeeds.

Run it

From the repo root:

make phase 406

Expected output includes:

policy   : /usr/lib/sysusers.d/onix-ssh.conf
ssh-auth : /persist/home/onix/.ssh/authorized_keys
ssh-host : /etc/dropbear/dropbear_ed25519_host_key generated
unit     : /nix/store/.../onix-bootstrap-dropbear.service
unit     : /persist/nix/store/.../onix-bootstrap-dropbear.service

Expected proof output includes:

ONIX_SSH_READY user=onix port=22
ONIX_SSH_OK user=onix uid=1000 home=/home/onix shell=/bin/sh host=onix kernel=Linux

==> success
Phase 406 proved authenticated SSH access through QEMU port forwarding.

What this phase does not do

Phase 406 does not finalize remote administration.

Still open:

  • whether final ONIX uses Dropbear or OpenSSH,
  • how users are created on first boot,
  • where user-provided SSH keys come from,
  • whether root SSH is always forbidden,
  • where persistent host keys should live long-term,
  • firewall policy,
  • SSH hardening policy,
  • whether remote access is enabled by default.

Phase 406 proves the essential mechanism:

authenticated key-based SSH into the booted ONIX image works