feat(vm): signal guest CRNG reseed after clone/restore#77
Conversation
Cloned/restored guests share byte-identical CRNG state; push fresh entropy and a reseed order over vsock to cocoon-agent (>= v0.1.6) so random output diverges again. Clone also requests /etc/machine-id regeneration; restore does not, since the VM identity is unchanged. Non-fatal by design: agent version skew or a legacy VM without vsock only logs a warning and points at the new manual 'cocoon vm reseed' verb, never fails the clone/restore command itself.
Clone/DirectClone/Restore/DirectRestore return an in-process *types.VM that never passes through ToVM, so VsockSocket (and PID/SocketPath) stay zero-valued; signalReseed would silently no-op on every call. Re-Inspect via the new refreshVM helper before signaling, threading the Hypervisor handle through the direct-clone/restore call chains. Also fire the clone signal before the JSON-output early return so automation (JSON) callers get it too, not just human-CLI output.
Both exec and reseed resolved the hypervisor, inspected the VM, and required running state with identical wrapped-error ceremony; hoist it into one Handler helper (op prefixes the error).
The v0.1.6 tag was re-cut to include the dbus machine-id fix; the release binaries changed, so refresh the pinned SHA256s (version unchanged).
|
Update — machine-id regen now works end-to-end with v0.1.6. The agent dbus fix (cocoon-agent PR #7) landed and the So the earlier 'known follow-up needs v0.1.7' note is superseded: the security-critical CRNG reseed and |
Review follow-ups on the reseed signal: - reseedVM retried on ANY error, so a version-skew old agent (dial OK, reseed frame rejected) burned the full retry budget (~4s) on every clone during the rollout window. Retry only failed dials (the re-listen-after- resume case); once a live agent answers, its reply is final. Old-agent reseed now returns in ~2ms instead of ~4s, verified on bare metal. - fold refresh+signal into Handler.reseedAfterResume so a caller can't signal on a stale (pre-ToVM) VM; move refreshVM beside it and log the swallowed inspect error at Debug (was mislabeled 'vsock not configured'). - signalReseed reports whether a reseed was attempted, making the Windows/no-vsock skip guards assertable.
Full review pass run (was incomplete before — thanks for pushing)Ran senior /code walk + 3-lens /simplify (reuse/quality/efficiency) + tighten. Findings triaged and applied: Real defect fixed (efficiency lens + my own walk): Verified on bare metal (isolated store, agent version confirmed, cleaned up):
Refactors from the review:
Verdict from the lenses: retry loop was the one real defect; refreshVM extra Inspect (~100µs) is acceptable, sync-not-async is correct (a detached reseed would be killed on CLI exit, leaving duplicate CRNG state), threading |
reseedAfterResume (a method) was interleaved between reseedVM and signalReseed; move it up beside Reseed so methods are grouped and the standalone helpers follow, per the file-org convention.
Re-cutting the v0.1.6 tag changed the module content, but the Go proxy + checksum DB treat a version as immutable, so the recorded v0.1.6 hash no longer matches — a fresh checkout fails 'verifying ...@v0.1.6: checksum mismatch'. v0.1.7 is a clean version never before ingested. go.mod/go.sum and all os-image pins (ubuntu + android 14/15) move to v0.1.7.
Fixed — moved to cocoon-agent v0.1.7 (the go.sum poisoning was real)The reviewer is correct and this was a must-fix. Re-cutting the v0.1.6 tag changed the module content, but Go's proxy + checksum DB treat a version as immutable, so go.sum's recorded v0.1.6 hash no longer matched the re-cut content. GitHub checks passed only on a warm GOMODCACHE; a fresh checkout would fail Fix: cocoon-agent v0.1.7 (fresh version, same dbus-fix content as the re-cut v0.1.6, never before ingested by the Go proxy/sumdb):
Verified in the reviewer's exact clean-cache scenario (fresh empty GOMODCACHE, sumdb on): Also folded in a |
What
Closes the host side of M1 reseed. After clone/restore, N guests share byte-identical snapshot memory → byte-identical CRNG state (colliding TLS keys/UUIDs). cocoon now sends 32 bytes of
crypto/randentropy + a reseed order to cocoon-agent (v0.1.6,client.Reseed) over the existing hybrid-vsock channel.cocoon vm reseed VM [--machine-id]— manual verb (fails loudly)regen_machine_id=true) and restore (false— same VM identity, only CRNG rotates), as a non-fatalsignalReseed: skips Windows (agent verb is Linux-only) and legacy no-vsock VMs; on agent version skew logs exactly one WARN pointing at the manual verb, never fails the commandrefreshVM: the*types.VMthatClone/Restorereturn is built in-process and never passes throughToVM, soVsockSocketwas always zero →signalReseedwould silently skip on every real clone. Re-Inspect before signaling. (Bug found during e2e.)resolveRunningVMextracted —execandreseedshared the resolve+inspect+running-check ceremony verbatimFC guests also carry VMGenID (kernel auto-reseed on resume) so this is belt-and-suspenders there; on CH it's the only reseed.
Verification (e2e on bare-metal 79, isolated store, audit-logged)
/etc/machine-idbyte+mtime identical (regen=false correctly skips machine-id)expected first frame type "exec", got "reseed"), clone otherwise healthy--machine-idthreads throughmake lint0×2, 25 pkgs green,make fmt-checkcleanKnown follow-up (not this PR)
The CRNG entropy/reseed ioctls (the security-critical mechanism) work with agent v0.1.6. But
--machine-idregen does not yet change the id end-to-end: agent v0.1.6 truncates/etc/machine-idbut not the baked/var/lib/dbus/machine-id, sosystemd-machine-id-setupre-adopts the stale copy. Fixed separately in cocoon-agent (dbus copy removal) → needs a v0.1.7 bump. This PR's host plumbing is correct and version-independent; machine-id regen lights up once the agent is bumped.