fix: a clone must not inherit its source's identity

Two boxes cloned from one snapshot came up holding the SAME IP address —
10.87.0.128, on the same dnsmasq lease, matching to the second on the
lease timer. Incus does regenerate the MAC (they differed), but
/etc/machine-id rides along inside the disk image, and systemd derives
its DHCP client identifier from it. Same client-id, same lease.

This breaks the workflow claudebox exists for: log in once, snapshot,
clone forever. Every clone of a snapshot is, to the network, the same
machine as its source and as its siblings.

Truncating /etc/machine-id makes systemd mint a fresh one at next boot,
so the clone path now resets it and restarts before handing the box
over. Costs one reboot; the alternative is colliding boxes.

Found by the drill on a live host (#26) — and it also explains why the
sibling-isolation probe (#15 A3) could never work: it was aiming box A
at box A's own address.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
claude-hdb 2026-07-13 23:31:32 +00:00
parent d8731b4772
commit 793f499178

View file

@ -405,6 +405,27 @@ wait_agent() {
done
}
# A clone must not BE its source. Incus regenerates the MAC, but /etc/machine-id
# rides along inside the disk — and systemd derives its DHCP client identifier
# (DUID) from it. Same client-id, same dnsmasq lease: two boxes, one IP address,
# to the second on the lease timer. Every box cloned from one snapshot collided
# on the network, which is exactly the workflow claudebox exists for (log in
# once, snapshot, clone forever).
#
# Truncating /etc/machine-id makes systemd mint a fresh one on the next boot, so
# the reset costs one reboot. Do it before handing the box over, never after.
reset_identity() {
local i="$1"
echo "claudebox: giving the clone its own identity (machine-id, DHCP lease)..."
incus exec "$i" -- sh -c '
: >/etc/machine-id
rm -f /var/lib/dbus/machine-id
ln -sf /etc/machine-id /var/lib/dbus/machine-id
' || die "could not reset the clone's machine-id"
incus restart "$i"
wait_agent "$i"
}
cmd_new() {
[ -n "$name" ] || usage_error "usage: $(synopsis_of new)"
local instance; instance="$(iname_of "$name")"
@ -415,6 +436,7 @@ cmd_new() {
incus copy "$srcref" "$instance"
incus start "$instance"
wait_agent "$instance"
reset_identity "$instance"
echo "claudebox: cloned $srcref — isolation and Claude auth carry over from the source."
else
local m extra=(); m="$(pick_mode)"