Keep provisioning credentials out of OEM installs and factory snapshots

Review findings from codex and Copilot:

- A rig-supplied credentials file in OEM mode now contributes only its LUKS
  passphrase: preserved users/root_enc_password fields would have created a
  hidden provisioning account despite OEM promising no user until first boot.
- The factory snapshot is taken writable, scrubbed of deployment credentials
  (staged authorized_keys, tailscale auth key and join unit, the dead OEM
  LUKS key and its initramfs drop-ins), then set read-only — a reset years
  later must not authorize the original deployment key or rejoin its tailnet.
- The missing-Node-tarball case is a hard install error on every install,
  not just OEM: the stash is what lets a factory reset finalize offline.
- The configurator's OEM gate checks the runtime package for the service
  unit as well as the setup binary.
- The test harness handles the deliberately skipped mode picker on blank
  disks when the bundled runtime lacks OEM support.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
David Heinemeier HanssonandClaude Fable 5 committed 2026-08-07 18:09:10 -07:00
1 parent 445db32792
commit e49ea2bc96
5 files changed
+110 -15

No files matched your search

+13 -5
View File
@@ -614,12 +614,20 @@ drive_configurator() {
capture_console "success-installer-02-install-disk"
press ret # the single virtio disk
wait_for_screen "installation mode" 60
capture_console "success-installer-03-install-mode"
if $OEM; then
press down # "OEM install" sits below "Full disk install" on a blank disk
# A blank disk shows the mode picker only when the bundled runtime supports
# OEM mode; otherwise the wizard goes straight to the overwrite confirm.
wait_for_screen "installation mode\|recovery possible" 60
if ocr_screen | grep -qi "installation mode"; then
capture_console "success-installer-03-install-mode"
if $OEM; then
press down # "OEM install" sits below "Full disk install" on a blank disk
fi
press ret
elif $OEM; then
capture_console "failure-installer-no-oem-choice"
echo "OEM flow requested but this ISO does not offer OEM mode" >&2
return 1
fi
press ret
wait_for_screen "recovery possible" 60
capture_console "success-installer-04-disk-warning-encrypted"
+2 -1
View File
@@ -791,7 +791,8 @@ oem_supported() {
OEM_SUPPORTED=true
if [[ -d $mirror ]]; then
pkg=$(ls "$mirror/$OMARCHY_RUNTIME_PACKAGE"-[0-9]*.pkg.tar.zst 2>/dev/null | head -1)
if [[ -z $pkg ]] || ! tar -tf "$pkg" usr/bin/omarchy-oem-setup &>/dev/null; then
if [[ -z $pkg ]] || ! tar -tf "$pkg" usr/bin/omarchy-oem-setup \
usr/share/omarchy/install/oem/omarchy-oem-setup.service &>/dev/null; then
OEM_SUPPORTED=false
fi
fi
@@ -65,6 +65,15 @@ class InstallContext:
else:
raise RuntimeError(f"credentials file missing: {creds_path}")
if oem:
# OEM promises no account exists until first boot. A supplied
# credentials file (a rig handing over its LUKS passphrase) must
# not smuggle in users or a root password.
encryption_password = user_credentials.get("encryption_password")
user_credentials = {"users": []}
if encryption_password:
user_credentials["encryption_password"] = encryption_password
arch_configuration = dict(user_configuration)
arch_configuration.pop("omarchy_install", None)
state_dir = Path(os.environ.get("OMARCHY_INSTALL_STATE_DIR", "/run/omarchy-install"))
@@ -1208,13 +1208,13 @@ def stage_oem_state(ctx: InstallContext) -> None:
def _stage_node_tarball(ctx: InstallContext, oem_dir) -> None:
tarballs = sorted(NODE_PACKAGES_DIR.glob("node-v*-linux-x64.tar.gz"))
if not tarballs:
if ctx.oem:
raise RuntimeError(
"no bundled Node tarball in /opt/packages — OEM first-boot setup "
"could not finalize the user offline"
)
info("› no bundled Node tarball to stash for factory reset; skipping")
return
# Hard error on every install, not just OEM: the stash is what lets a
# later factory reset finalize the next owner offline, and an ISO
# build always bundles the tarball — its absence means a broken build.
raise RuntimeError(
f"no bundled Node tarball in {NODE_PACKAGES_DIR} — first-boot setup "
"and factory reset could not finalize a user offline"
)
packages_dir = oem_dir / "packages"
packages_dir.mkdir(parents=True, exist_ok=True)
@@ -1820,13 +1820,41 @@ def create_factory_snapshot(ctx: InstallContext) -> None:
info("› snapshotting @ as @factory (read-only)")
subprocess.run(
["btrfs", "subvolume", "snapshot", "-r", str(root_subvol), str(factory)],
["btrfs", "subvolume", "snapshot", str(root_subvol), str(factory)],
check=True,
)
_scrub_factory_snapshot(factory)
subprocess.run(
["btrfs", "property", "set", "-ts", str(factory), "ro", "true"],
check=True,
)
finally:
subprocess.run(["umount", str(top)], check=False, capture_output=True)
# Provisioning credentials staged for THIS deployment's first boot must not
# survive into the factory image: a reset years later would otherwise hand the
# next owner the original deployment's SSH keys or rejoin its tailnet, and a
# stale OEM LUKS key (dead after the first re-key) has no business lingering.
# The mkinitcpio/cmdline drop-ins go with the keyfile — a reset rebuild would
# otherwise fail on FILES pointing at a scrubbed path.
FACTORY_SCRUB_PATHS = (
"var/lib/omarchy/oem/authorized_keys",
"var/lib/omarchy/oem/luks-key",
"etc/omarchy/oem.key",
"etc/limine-entry-tool.d/99-omarchy-oem-unlock.conf",
"etc/mkinitcpio.conf.d/99-omarchy-oem-key.conf",
"etc/tailscale/authkey",
"etc/systemd/system/omarchy-tailscale-join.service",
"etc/systemd/system/multi-user.target.wants/omarchy-tailscale-join.service",
)
def _scrub_factory_snapshot(factory: Path) -> None:
for rel in FACTORY_SCRUB_PATHS:
(factory / rel).unlink(missing_ok=True)
def _findmnt_value(path: Path, column: str) -> str | None:
res = subprocess.run(
["findmnt", "-no", column, str(path)],
+50 -1
View File
@@ -121,6 +121,22 @@ class ContextOemTest(unittest.TestCase):
"rig-secret",
)
def test_oem_discards_account_material_from_supplied_credentials(self):
# A rig credentials file must not smuggle in users or a root password.
self.write_config(self.base_config(oem=True))
self.write_creds({
"users": [{"username": "backdoor", "enc_password": "x", "sudo": True}],
"root_enc_password": "x",
"encryption_password": "rig-secret",
})
ctx = self.from_env()
self.assertEqual(ctx.user_credentials["users"], [])
self.assertNotIn("root_enc_password", ctx.user_credentials)
self.assertEqual(ctx.user_credentials["encryption_password"], "rig-secret")
synthesized = json.loads(ctx.creds_path.read_text())
self.assertEqual(synthesized["users"], [])
self.assertNotIn("root_enc_password", synthesized)
def test_oem_unencrypted_touches_nothing(self):
self.write_config(self.base_config(oem=True))
ctx = self.from_env()
@@ -194,6 +210,14 @@ class StageOemStateTest(unittest.TestCase):
with self.assertRaisesRegex(RuntimeError, "Node tarball"):
phases_impl.stage_oem_state(ctx)
def test_missing_node_tarball_fails_normal_installs_too(self):
# The stash is what makes a later factory reset work offline.
for tarball in self.packages.glob("*.tar.gz"):
tarball.unlink()
ctx = make_ctx(self.target, oem=False)
with self.assertRaisesRegex(RuntimeError, "Node tarball"):
phases_impl.stage_oem_state(ctx)
def test_oem_arms_first_boot_setup(self):
self.install_runtime_oem_support()
ctx = make_ctx(self.target)
@@ -357,11 +381,36 @@ class CreateFactorySnapshotTest(unittest.TestCase):
top = str(self.state_dir / "factory-top")
self.assertIn(["mount", "-o", "subvolid=5", "/dev/mapper/omarchy_root", top], self.calls)
self.assertIn(
["btrfs", "subvolume", "snapshot", "-r", f"{top}/@", f"{top}/@factory"],
["btrfs", "subvolume", "snapshot", f"{top}/@", f"{top}/@factory"],
self.calls,
)
self.assertIn(
["btrfs", "property", "set", "-ts", f"{top}/@factory", "ro", "true"],
self.calls,
)
self.assertIn(["umount", top], self.calls)
def test_factory_snapshot_scrubs_provisioning_credentials(self):
# Simulate the snapshot carrying deployment credentials; the scrub
# must drop them before the snapshot goes read-only.
top = self.state_dir / "factory-top"
factory = top / "@factory"
secrets = [
factory / "var/lib/omarchy/oem/authorized_keys",
factory / "etc/tailscale/authkey",
factory / "etc/omarchy/oem.key",
]
keep = factory / "var/lib/omarchy/oem/groups"
for path in [*secrets, keep]:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("secret")
phases_impl.create_factory_snapshot(self.ctx())
for path in secrets:
self.assertFalse(path.exists(), path)
self.assertTrue(keep.exists())
def test_non_btrfs_target_skips(self):
self.findmnt["FSTYPE"] = "ext4"
phases_impl.create_factory_snapshot(self.ctx())