fix: a clone must not inherit its source's identity (two boxes, one IP) #27

Merged
dan-claude-bot merged 1 commit from fix/clone-identity into main 2026-07-13 23:33:28 +00:00
dan-claude-bot commented 2026-07-13 23:32:48 +00:00 (Migrated from github.com)

Two boxes cloned from one snapshot came up holding the same IP address. Confirmed live on the drill host (#26):

archive: machine-id 11239d2358134ceeb3a8ee42b7b54551   MAC 10:66:6a:7e:94:f5
peer:    machine-id 11239d2358134ceeb3a8ee42b7b54551   MAC 10:66:6a:77:a9:91
         both: 10.87.0.128/24, valid_lft 3466sec — the same dnsmasq lease, to the second

Incus does regenerate the MAC — they differ. But /etc/machine-id rides along inside the disk image, and systemd derives its DHCP client identifier (DUID) from it. Same client-id → dnsmasq hands the clone its source's lease. (That machine-id is archive's original Incus UUID — baked in at its first boot, cloned ever since.)

Why this matters more than it looks

This breaks the workflow claudebox exists for. From the README: log in once, snapshot, clone forever. Every box cloned from that snapshot is, to the network, the same machine as its source and as its siblings. Two clones running at once collide; DHCP hands them one address between them.

It also silently sabotaged the audit: #15's sibling-isolation probe (A3) was aiming box A at box A's own address. Six runs, never fired — and had it fired, a self-connection would have been reported as an isolation failure.

The fix

Truncating /etc/machine-id makes systemd mint a fresh one on the next boot. So new --from now resets identity and restarts before handing the box over:

reset_identity() {
  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'
  incus restart "$i"; wait_agent "$i"
}

Costs one reboot on clone. The alternative is colliding boxes.

Verifying it

On a host with a snapshot to clone from:

claudebox new --name a --from <src>/<snap>
claudebox new --name b --from <src>/<snap>
incus list          # a and b must hold DIFFERENT 10.87.x addresses
for x in a b; do incus exec $x -- cat /etc/machine-id; done   # must differ

Found by the drill on a live host — the kind of bug a fresh test server would have hidden and a stubbed test could never have produced.

🤖 Generated with Claude Code

**Two boxes cloned from one snapshot came up holding the same IP address.** Confirmed live on the drill host (#26): ``` archive: machine-id 11239d2358134ceeb3a8ee42b7b54551 MAC 10:66:6a:7e:94:f5 peer: machine-id 11239d2358134ceeb3a8ee42b7b54551 MAC 10:66:6a:77:a9:91 both: 10.87.0.128/24, valid_lft 3466sec — the same dnsmasq lease, to the second ``` Incus **does** regenerate the MAC — they differ. But `/etc/machine-id` rides along inside the disk image, and systemd derives its **DHCP client identifier (DUID) from it**. Same client-id → dnsmasq hands the clone its source's lease. (That machine-id is `archive`'s original Incus UUID — baked in at its first boot, cloned ever since.) ## Why this matters more than it looks This breaks the workflow claudebox exists for. From the README: *log in once, snapshot, clone forever.* Every box cloned from that snapshot is, to the network, **the same machine as its source and as its siblings**. Two clones running at once collide; DHCP hands them one address between them. It also silently sabotaged the audit: **#15's sibling-isolation probe (A3) was aiming box A at box A's own address.** Six runs, never fired — and had it fired, a self-connection would have been reported as an isolation *failure*. ## The fix Truncating `/etc/machine-id` makes systemd mint a fresh one on the next boot. So `new --from` now resets identity and restarts before handing the box over: ```sh reset_identity() { 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' incus restart "$i"; wait_agent "$i" } ``` Costs one reboot on clone. The alternative is colliding boxes. ## Verifying it On a host with a snapshot to clone from: ```sh claudebox new --name a --from <src>/<snap> claudebox new --name b --from <src>/<snap> incus list # a and b must hold DIFFERENT 10.87.x addresses for x in a b; do incus exec $x -- cat /etc/machine-id; done # must differ ``` Found by the drill on a live host — the kind of bug a fresh test server would have hidden and a stubbed test could never have produced. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/box#27
No description provided.