Require a factory snapshot to reset a computer (#6680)
Machines without @factory fell back to a degraded reset that kept the current system and only wiped user state. Turn them away with an explanation instead, and drop the degraded staging path. The first-boot worker still honors a wipe-degraded marker so a reset staged by an older version finishes its scrub rather than handing the machine over with the seller's accounts intact. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
0b670cd13e
commit
4ab51df2b0
@@ -939,8 +939,8 @@ reset_limine_config() {
|
||||
}
|
||||
|
||||
cleanup_oem_state() {
|
||||
# Keep groups + packages: omarchy-system-factory-reset's degraded path (machines
|
||||
# without @factory) re-arms first-boot setup from the live copies.
|
||||
# Keep groups + packages: omarchy-system-factory-reset stages the bundled Node
|
||||
# tarball from these live copies when the factory snapshot predates it.
|
||||
rm -f "$PROVISIONING_DIR/pending" "$PROVISIONING_DIR/authorized_keys" "$PROVISIONING_DIR/setup-user"
|
||||
|
||||
rm -f /etc/systemd/system/multi-user.target.wants/omarchy-provision-owner.service
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
# Returns the machine to provisioning state — fully installed, no user, first-boot
|
||||
# setup pending — the state you want before selling or handing the machine on.
|
||||
#
|
||||
# On machines installed with an ISO that created the @factory snapshot, the
|
||||
# running root (@) is swapped for a fresh writable clone of @factory, so
|
||||
# user-installed packages and /etc drift are gone too. Machines without
|
||||
# @factory get a degraded reset: the current system is kept and only user
|
||||
# accounts, home data, and machine identity are wiped.
|
||||
# The running root (@) is swapped for a fresh writable clone of the @factory
|
||||
# snapshot, so user-installed packages and /etc drift are gone too. That
|
||||
# snapshot is taken by the Omarchy Quattro installer, so only machines
|
||||
# installed from that ISO can be reset; a machine upgraded to Quattro from an
|
||||
# earlier version has no baseline to return to and is turned away.
|
||||
#
|
||||
# The heavy lifting happens on the next boot via omarchy-system-factory-reset-finish, staged
|
||||
# here. On encrypted installs the LUKS volume is re-keyed to a throwaway
|
||||
@@ -99,9 +99,6 @@ generate_passphrase() {
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if (( ${swap_done:-0} == 0 )); then
|
||||
rollback_degraded_rekey
|
||||
fi
|
||||
if mountpoint -q "$TOP_MNT" 2>/dev/null; then
|
||||
if [[ -d $TOP_MNT/$NEXT_NAME && ${swap_done:-0} == 0 ]]; then
|
||||
btrfs subvolume delete --recursive "$TOP_MNT/$NEXT_NAME" >/dev/null 2>&1 || true
|
||||
@@ -113,23 +110,13 @@ cleanup() {
|
||||
trap cleanup EXIT
|
||||
|
||||
confirm_reset() {
|
||||
local degraded="$1"
|
||||
|
||||
echo
|
||||
gum style --bold --foreground 1 "Reset this computer to factory state?"
|
||||
echo
|
||||
gum style "This will permanently erase:"
|
||||
gum style " • All user accounts and everything in /home"
|
||||
if [[ $degraded == true ]]; then
|
||||
gum style --foreground 3 " • Machine identity (network connections, host keys, machine-id)"
|
||||
echo
|
||||
gum style --foreground 3 "This machine was installed before factory snapshots existed, so the"
|
||||
gum style --foreground 3 "current system (installed packages, /etc changes) is kept — only user"
|
||||
gum style --foreground 3 "accounts and machine state are wiped."
|
||||
else
|
||||
gum style " • All packages and system changes made since installation"
|
||||
gum style " • Machine identity (network connections, host keys, machine-id)"
|
||||
fi
|
||||
echo
|
||||
gum style "The next boot asks for a new user, exactly like a fresh install."
|
||||
gum style --foreground 8 "Note: on disks without encryption this is deletion, not secure erasure."
|
||||
@@ -141,8 +128,7 @@ confirm_reset() {
|
||||
}
|
||||
|
||||
# Re-key to a throwaway passphrase whose keyfile auto-unlocks boot during the
|
||||
# provisioning window. $1 is the root of the system the staging applies to ("" for the
|
||||
# running root in degraded mode).
|
||||
# provisioning window. $1 is the root of the staged factory system.
|
||||
stage_luks_rekey() {
|
||||
local next="$1" device passphrase
|
||||
|
||||
@@ -396,40 +382,21 @@ stage_full_reset() {
|
||||
sync
|
||||
}
|
||||
|
||||
stage_degraded_reset() {
|
||||
local unit_src="$OMARCHY_PATH/install/provisioning"
|
||||
[[ -f $unit_src/omarchy-provision-owner.service && -x /usr/bin/omarchy-provision-owner ]] ||
|
||||
fail "this Omarchy version does not ship provisioning; update first"
|
||||
# A reset is a clone of @factory, so a machine without one has nothing to
|
||||
# return to. Only the Quattro installer takes that snapshot.
|
||||
require_factory_snapshot() {
|
||||
[[ -d $TOP_MNT/@factory ]] && return 0
|
||||
|
||||
# Fallible staging (re-key, boot rebuild) happens before the wipe is armed:
|
||||
# a failure must leave the machine untouched, not schedule a wipe for a
|
||||
# reset that never finished staging. The degraded path stages into the LIVE
|
||||
# root, so a mid-staging failure must also undo the auto-unlock material it
|
||||
# already wrote — otherwise a later boot rebuild would silently embed it.
|
||||
install -d -m 755 "$PROVISIONING_DIR"
|
||||
if encrypted_install; then
|
||||
degraded_rekey_staged=1
|
||||
stage_luks_rekey ""
|
||||
log "Rebuilding boot files with the provisioning unlock key"
|
||||
if ! limine-update >>"$LOG_FILE" 2>&1; then
|
||||
fail "limine-update failed (see $LOG_FILE)"
|
||||
fi
|
||||
degraded_rekey_staged=0
|
||||
fi
|
||||
|
||||
install_provisioning_units "" "$unit_src"
|
||||
touch "$PROVISIONING_DIR/pending" "$PROVISIONING_DIR/wipe-pending" "$PROVISIONING_DIR/wipe-degraded"
|
||||
|
||||
swap_done=1
|
||||
}
|
||||
|
||||
# Undo the live-root auto-unlock material a failed degraded rekey left behind.
|
||||
rollback_degraded_rekey() {
|
||||
(( ${degraded_rekey_staged:-0} )) || return 0
|
||||
rm -f /etc/omarchy/provisioning.key "$PROVISIONING_DIR/luks-key" \
|
||||
/etc/limine-entry-tool.d/99-omarchy-provisioning-unlock.conf \
|
||||
/etc/mkinitcpio.conf.d/99-omarchy-provisioning-key.conf
|
||||
limine-update >>"$LOG_FILE" 2>&1 || true
|
||||
echo
|
||||
gum style --bold --foreground 3 "This machine has no factory snapshot to reset to."
|
||||
echo
|
||||
gum style "A reset restores the @factory snapshot the Omarchy Quattro installer takes"
|
||||
gum style "right after installing. Machines upgraded to Quattro from an earlier version,"
|
||||
gum style "or installed some other way, never got one, so there is no baseline here to"
|
||||
gum style "return to."
|
||||
echo
|
||||
gum style --foreground 8 "Reinstall from the Omarchy ISO to make this machine resettable."
|
||||
exit 1
|
||||
}
|
||||
|
||||
main() {
|
||||
@@ -448,10 +415,9 @@ main() {
|
||||
mkdir -p "$TOP_MNT"
|
||||
mountpoint -q "$TOP_MNT" || mount -o subvolid=5 "$device" "$TOP_MNT"
|
||||
|
||||
local degraded=false
|
||||
[[ -d $TOP_MNT/@factory ]] || degraded=true
|
||||
require_factory_snapshot
|
||||
|
||||
confirm_reset "$degraded"
|
||||
confirm_reset
|
||||
|
||||
# The running system's limine-snapper-sync must not rewrite the ESP's
|
||||
# limine.conf behind the staged rebuild (subvolume changes below can
|
||||
@@ -459,11 +425,7 @@ main() {
|
||||
systemctl mask --runtime --now limine-snapper-sync.service >/dev/null 2>&1 || true
|
||||
systemctl mask --runtime --now limine-snapper-sync.path >/dev/null 2>&1 || true
|
||||
|
||||
if [[ $degraded == true ]]; then
|
||||
stage_degraded_reset
|
||||
else
|
||||
stage_full_reset
|
||||
fi
|
||||
|
||||
umount -R "$TOP_MNT" 2>/dev/null || true
|
||||
|
||||
|
||||
@@ -9,13 +9,11 @@
|
||||
# Ordered before home.mount/var-log.mount so @home and @log can be dropped and
|
||||
# recreated as empty subvolumes instead of rm -rf'd file by file.
|
||||
#
|
||||
# Two modes:
|
||||
# - full reset: the root is already a fresh clone of @factory (staged by
|
||||
# The root is already a fresh clone of @factory (staged by
|
||||
# omarchy-system-factory-reset); this deletes the previous root (@omarchy-old-*),
|
||||
# recreates @home/@log, and repairs /.snapshots.
|
||||
# - degraded (wipe-degraded marker): machines installed before @factory
|
||||
# existed keep their current root; user accounts and identity state are
|
||||
# scrubbed here instead.
|
||||
# recreates @home/@log, and repairs /.snapshots. A reset staged by an older
|
||||
# Omarchy on a machine without @factory (wipe-degraded) kept its root instead,
|
||||
# and is scrubbed in place here.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
@@ -76,7 +74,13 @@ recreate_subvolume() {
|
||||
btrfs subvolume create "$TOP_MNT/$name"
|
||||
}
|
||||
|
||||
scrub_degraded_state() {
|
||||
# Only for a reset staged by an Omarchy that still had the degraded path: the
|
||||
# current system was kept, so accounts and identity are scrubbed in place here
|
||||
# instead of arriving pre-scrubbed in the factory clone. Reachable when a
|
||||
# degraded reset was staged, the reboot deferred, and this worker updated in
|
||||
# between — the reset must still finish, or provisioning hands the machine over
|
||||
# with the seller's accounts intact.
|
||||
scrub_legacy_degraded_state() {
|
||||
log "degraded reset: scrubbing user accounts and machine identity in place"
|
||||
|
||||
local user
|
||||
@@ -93,6 +97,15 @@ scrub_degraded_state() {
|
||||
|
||||
# Fresh machine identity from the next boot on.
|
||||
systemd-id128 new >/etc/machine-id 2>/dev/null || :>/etc/machine-id
|
||||
|
||||
# The kept root still carries its accumulated snapper snapshots (nested under
|
||||
# /.snapshots); a factory clone loses them with the old root.
|
||||
local snapshot
|
||||
for snapshot in /.snapshots/*/snapshot; do
|
||||
[[ -d $snapshot ]] || continue
|
||||
delete_subvolume "$snapshot"
|
||||
rm -rf "$(dirname "$snapshot")"
|
||||
done
|
||||
}
|
||||
|
||||
repair_snapshots_dir() {
|
||||
@@ -108,18 +121,6 @@ repair_snapshots_dir() {
|
||||
fi
|
||||
}
|
||||
|
||||
wipe_snapper_snapshots() {
|
||||
# Degraded resets keep the current root, so its accumulated snapper
|
||||
# snapshots (nested under /.snapshots) still exist. Full resets already lost
|
||||
# them with the old root.
|
||||
local snapshot
|
||||
for snapshot in /.snapshots/*/snapshot; do
|
||||
[[ -d $snapshot ]] || continue
|
||||
delete_subvolume "$snapshot"
|
||||
rm -rf "$(dirname "$snapshot")"
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
local device
|
||||
device=$(root_device)
|
||||
@@ -134,10 +135,7 @@ main() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f $PROVISIONING_DIR/wipe-degraded ]]; then
|
||||
scrub_degraded_state
|
||||
wipe_snapper_snapshots
|
||||
fi
|
||||
[[ -f $PROVISIONING_DIR/wipe-degraded ]] && scrub_legacy_degraded_state
|
||||
|
||||
local old
|
||||
for old in "$TOP_MNT"/@omarchy-old-*; do
|
||||
|
||||
@@ -22,7 +22,7 @@ if [[ -n $NODE_PACKAGE_DIR ]]; then
|
||||
NODE_TARBALL=$(find "$NODE_PACKAGE_DIR" -name "node-v*-linux-x64.tar.gz" -type f 2>/dev/null | head -n1)
|
||||
if [[ -z $NODE_TARBALL ]]; then
|
||||
if [[ ${OMARCHY_SETUP_CONTEXT:-} == "provision-owner" ]]; then
|
||||
# A degraded reset on an old install may not have the tarball staged.
|
||||
# A factory snapshot predating the bundled tarball may not have it staged.
|
||||
# Leave Node to the network rather than failing the whole first boot.
|
||||
echo "Warning: no bundled Node.js tarball in $NODE_PACKAGE_DIR; trying the network" >&2
|
||||
mise use -g node@latest || echo "Warning: Node.js install deferred (no network)" >&2
|
||||
|
||||
Reference in New Issue
Block a user