Skip to content

layer: export every layer with an empty machine-id - #15

Merged
marcos-mendez merged 1 commit into
19.xfrom
fix/empty-machine-id
Sep 30, 2026
Merged

marcos-mendez merged 1 commit into
19.xfrom
fix/empty-machine-id

Conversation

@marcos-mendez

Copy link
Copy Markdown
Collaborator

Problem

Every Keel image ships the same /etc/machine-id, f0e97605ab594989b4d78f4a126b5b37 (templateB2: mode 0444, plus /var/lib/dbus/machine-id as a regular file with the same id). It comes from the fab bootstrap (/turnkey/fab-keel/bootstraps/trixie-amd64/etc/machine-id, dated 2026-09-22). systemd keeps a populated file, and systemd's postinst runs systemd-machine-id-setup on every configure, so a child layer that upgrades systemd writes one of its own. Every LXC or VM made from one image then shares its DHCP DUID/IAID, journal and networkd identity, and keel's MariaDB server_id: a replica test found two containers with the same one. This is the defect in docs/traps.md, "Every appliance built from core has the same machine-id".

Fix

bt-layer runs the new layer_reset_machine_id on root.patched after layer_check_build. That is before the rootfs copy that children start from and before the tarball, so every layer gets it, whatever its build did:

  • /etc/machine-id becomes an empty file (0444). Debian's cloud images ship it this way (config_space/scripts/LAST/10-cleanup), and machine-id(5) recommends it. The file is not left missing or set to uninitialized, because either one makes the first boot a systemd first boot, and that presets every unit.
  • If /var/lib/dbus/machine-id exists, it becomes a link to /etc/machine-id, which is what tmpfiles.d/dbus.conf makes. When /etc/machine-id is empty, systemd falls back to the D-Bus file, so a regular D-Bus file would bring the old id back.
  • A tree already in that state is not written, so a child does not copy its parent's empty file into its own tarball. If the reset fails, the export stops.

TurnKey does the same in neither buildtasks (patches/container or bt-container) nor bootstrap, and its tracker has no issue about it.

Evidence

  • tests/layer: tests were written first and failed before the fix. They cover each case of the function, the core tarball and rootfs of a build that wrote an id, and a bt-layer run whose id cannot be reset. Each of five mutations turns the suite red. Coverage: layer-lib 383/384, bt-layer 111/112, the same one uncovered line each as on 19.x.
  • Real run on the test VM:
    • On an overlay deck, a child that wrote an id ends with an empty 0444 file and the link in its upper directory. One that did not write an id leaves its upper directory empty.
    • The templateB2 rootfs as published boots with f0e976.... Reset with this function and booted from scratch twice in LXC, it came up with 0308a979... and then 0bbc049e.... Each id was kept across a reboot, the D-Bus link followed it, no first boot was detected, and the same 41 units were enabled as in the published image.

Deploying

Merging deploys nothing. /turnkey/buildtasks-keel on the build host has to be updated by hand before the next build. Images and machines that already exist keep the shared id. To fix a machine, run rm -f /etc/machine-id /var/lib/dbus/machine-id && systemd-machine-id-setup && ln -s /etc/machine-id /var/lib/dbus/machine-id, then reboot and run keel apply again. This changes that node's server_id.

Every image made from a Keel layer shared one /etc/machine-id,
f0e97605ab594989b4d78f4a126b5b37: the fab bootstrap carries it, systemd
keeps a populated file, and systemd's postinst runs systemd-machine-id-setup
on every configure, so a child layer that upgrades systemd writes its own.
Two machines from one image then share their DHCP DUID and IAID, their
journal and networkd identity, and keel's MariaDB server_id, which stopped
a replica test.

bt-layer now runs layer_reset_machine_id on root.patched after
layer_check_build, before the rootfs copy children start from and before
the tarball: /etc/machine-id becomes an empty file, as Debian's cloud images
ship it, and /var/lib/dbus/machine-id, when present, the link to it that
tmpfiles.d/dbus.conf makes. The link matters because systemd falls back to
the D-Bus file when /etc/machine-id is empty and would bring the old id
back. Empty rather than missing or "uninitialized", which would make the
first boot a first boot to systemd and preset every unit. A tree already in
that state is not written, so a child layer does not copy its parent's
empty file up into its own tarball; a reset that fails stops the export.

Verified on the test VM: on an overlay deck, a child that wrote an id ends
with an empty 0444 file and the link in its upper directory, and one that
did not leaves the upper empty. The templateB2 rootfs, reset this way and
booted twice in LXC, came up with two different ids, each kept across a
reboot, no first boot detected and the same 41 enabled units as the image
as published, which boots with f0e97605ab594989b4d78f4a126b5b37.
@marcos-mendez
marcos-mendez merged commit f1501f9 into 19.x Sep 30, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant