fix(install): swap=0 and unattended LUKS fail-closed
All checks were successful
Check / eval (push) Successful in 3m4s

Pass bare swapSize "0" when the user wants no swap (was "0G", which
still created @swap). Accept "0"/"0G" in disko-config. Unattended
installs require NOMARCHY_LUKS_PASSPHRASE or explicit NOMARCHY_NO_LUKS=1
instead of silently installing cleartext.

Verified: bash -n; nix-instantiate subvolume sets for 0/0G/16G.
VISION § v1.0 install golden path (workflow test iteration).
This commit is contained in:
Bernardo Magri
2026-07-09 09:40:07 +01:00
parent 02d7baeb7c
commit 208b8d4444
4 changed files with 35 additions and 26 deletions

View File

@@ -15,12 +15,14 @@
# proved fragile twice before.
{ mainDrive
, withLuks ? true
, swapSize ? "0" # "0" = no swap; otherwise e.g. "16G" (sized for hibernation)
, swapSize ? "0" # "0" or "0G" = no swap; otherwise e.g. "16G" (hibernation-sized)
, ...
}:
let
btrfsMountOptions = [ "compress=zstd" "noatime" ];
# Installer historically always appended "G"; accept both "0" and "0G".
noSwap = swapSize == "0" || swapSize == "0G";
rootBtrfs = {
type = "btrfs";
@@ -32,7 +34,7 @@ let
"@log" = { mountpoint = "/var/log"; mountOptions = btrfsMountOptions; };
# snapper timeline snapshots (nomarchy.system.snapper.enable)
"@snapshots" = { mountpoint = "/.snapshots"; mountOptions = btrfsMountOptions; };
} // (if swapSize == "0" then { } else {
} // (if noSwap then { } else {
# Hibernation-ready swapfile on its own subvolume; disko's
# mkswapfile handles the BTRFS NOCOW requirements.
"@swap" = {

View File

@@ -18,8 +18,11 @@
# [NOMARCHY_TIMEZONE=UTC] [NOMARCHY_LUKS_PASSPHRASE=...] \
# [NOMARCHY_LOCALE=en_US.UTF-8] [NOMARCHY_KB_LAYOUT=us] [NOMARCHY_KB_VARIANT=] \
# [NOMARCHY_SWAP_GB=N (default: RAM size; 0 = none)] \
# [NOMARCHY_LUKS_PASSPHRASE=... | NOMARCHY_NO_LUKS=1] \
# [NOMARCHY_HW="auto"|"none"|"mod1 mod2"] [NOMARCHY_FINISH=none|reboot|poweroff]
# nomarchy-install
# Unattended encryption is fail-closed: set a passphrase, or explicit
# NOMARCHY_NO_LUKS=1 — never silently install cleartext.
set -euo pipefail
@@ -126,7 +129,15 @@ section "Disk encryption"
# logs you straight into the desktop (the passphrase already gates access).
LUKS_PASSPHRASE=""
if [[ "$UNATTENDED" == "1" ]]; then
LUKS_PASSPHRASE="${NOMARCHY_LUKS_PASSPHRASE:-}"
# Fail-closed: unattended without a passphrase used to install
# cleartext (easy CI footgun). Require an explicit opt-out.
if [[ "${NOMARCHY_NO_LUKS:-}" == "1" ]]; then
LUKS_PASSPHRASE=""
elif [[ -n "${NOMARCHY_LUKS_PASSPHRASE:-}" ]]; then
LUKS_PASSPHRASE="$NOMARCHY_LUKS_PASSPHRASE"
else
fail "Unattended install needs NOMARCHY_LUKS_PASSPHRASE or NOMARCHY_NO_LUKS=1"
fi
elif gum confirm --default=yes "Encrypt the disk with LUKS? (default — also enables passwordless desktop login)"; then
while true; do
p1=$(gum input --password --placeholder "LUKS passphrase (min 8 chars)")
@@ -317,11 +328,19 @@ if [[ $WITH_LUKS == true ]]; then
unset LUKS_PASSPHRASE
fi
# disko-config treats exact "0" as no-swap; "${SWAP_GB}G" would pass "0G"
# and still create a useless @swap subvolume (layout vs resume disagreed).
if [[ "$SWAP_GB" == "0" ]]; then
DISKO_SWAP_SIZE="0"
else
DISKO_SWAP_SIZE="${SWAP_GB}G"
fi
disko_log=$(mktemp --suffix=.disko.log)
if ! disko --mode destroy,format,mount --yes-wipe-all-disks \
--argstr mainDrive "$TARGET_DISK" \
--arg withLuks "$WITH_LUKS" \
--argstr swapSize "${SWAP_GB}G" \
--argstr swapSize "$DISKO_SWAP_SIZE" \
"$SHARE/disko-config.nix" >"$disko_log" 2>&1; then
tail -n 30 "$disko_log"
fail "disko failed — full log: $disko_log"