mirror of
https://github.com/NiklasGollenstede/nixos-installer.git
synced 2025-08-16 19:57:05 +02:00
better CLI argument handling, some small fixes
This commit is contained in:
@ -41,7 +41,7 @@ function gen-key-constant {( set -eu # 1: _, 2: value
|
||||
## Obtains a key by prompting for a password.
|
||||
function gen-key-password {( set -eu # 1: usage
|
||||
usage=$1
|
||||
( prompt-new-password "as key for @{config.networking.hostName}/$usage" || exit 1 )
|
||||
( prompt-new-password "as key for @{config.networking.hostName}:$usage" || exit 1 )
|
||||
)}
|
||||
|
||||
## Generates a key by prompting for (or reusing) a »$user«'s password, combining it with »$keystore/home/$user.key«.
|
||||
@ -50,7 +50,8 @@ function gen-key-home-composite {( set -eu # 1: usage, 2: user
|
||||
if [[ ${!userPasswords[@]} && ${userPasswords[$user]:-} ]] ; then
|
||||
password=${userPasswords[$user]}
|
||||
else
|
||||
password=$(prompt-new-password "that will be used as component of the key for @{config.networking.hostName}/$usage")
|
||||
password=$(prompt-new-password "that will be used as component of the key for »@{config.networking.hostName}:$usage«")
|
||||
if [[ ! $password ]] ; then exit 1 ; fi
|
||||
fi
|
||||
( cat "$keystore"/home/"$user".key && cat <<<"$password" ) | sha256sum | head -c 64
|
||||
)}
|
||||
@ -63,16 +64,18 @@ function gen-key-home-yubikey {( set -eu # 1: usage, 2: serialAndSlotAndUser(as
|
||||
if [[ ${!userPasswords[@]} && ${userPasswords[$user]:-} ]] ; then
|
||||
password=${userPasswords[$user]}
|
||||
else
|
||||
password=$(prompt-new-password "as YubiKey challenge for @{config.networking.hostName}/$usage")
|
||||
password=$(prompt-new-password "as YubiKey challenge for »@{config.networking.hostName}:$usage«")
|
||||
if [[ ! $password ]] ; then exit 1 ; fi
|
||||
fi
|
||||
gen-key-yubikey-challenge "$usage" "$serial:$slot:home-$password" true "${user}'s password for ${usage}"
|
||||
gen-key-yubikey-challenge "$usage" "$serial:$slot:home-$user=$password" true "»${user}«'s password (for key »${usage}«)"
|
||||
)}
|
||||
|
||||
## Generates a reproducible secret by prompting for a pin/password and then challenging slot »$slot« of YubiKey »$serial«.
|
||||
function gen-key-yubikey-pin {( set -eu # 1: usage, 2: serialAndSlot(as »serial:slot«)
|
||||
usage=$1 ; serialAndSlot=$2
|
||||
password=$(prompt-new-password "/ pin as challenge to YubiKey »$serialAndSlot« as key for @{config.networking.hostName}/$usage")
|
||||
gen-key-yubikey-challenge "$usage" "$serialAndSlot:$password" true "pin for ${usage}"
|
||||
password=$(prompt-new-password "/ pin as challenge to YubiKey »$serialAndSlot« as key for »@{config.networking.hostName}:$usage«")
|
||||
if [[ ! $password ]] ; then exit 1 ; fi
|
||||
gen-key-yubikey-challenge "$usage" "$serialAndSlot:$password" true "password / pin as key for »@{config.networking.hostName}:$usage«"
|
||||
)}
|
||||
|
||||
## Generates a reproducible secret for a certain »$use«case and optionally »$salt« on a »$host« by challenging slot »$slot« of YubiKey »$serial«.
|
||||
@ -91,19 +94,19 @@ function gen-key-yubikey-challenge {( set -eu # 1: _, 2: serialAndSlotAndChallen
|
||||
serial=$( <<<"$args" cut -d: -f1 ) ; slot=$( <<<"$args" cut -d: -f2 )
|
||||
challenge=${args/$serial:$slot:/}
|
||||
|
||||
if [[ "$serial" != "$(@{native.yubikey-personalization}/bin/ykinfo -sq)" ]] ; then printf 'Please insert / change to YubiKey with serial %s!\n' "$serial" 1>&2 ; fi
|
||||
if [[ "$serial" != "$( @{native.yubikey-personalization}/bin/ykinfo -sq )" ]] ; then printf 'Please insert / change to YubiKey with serial %s!\n' "$serial" 1>&2 ; fi
|
||||
if [[ ! "${3:-}" ]] ; then
|
||||
read -p 'Challenging YubiKey '"$serial"' slot '"$slot"' twice with '"${message:-challenge »"$challenge":1/2«}"'. Enter to continue, or Ctrl+C to abort:'
|
||||
else
|
||||
read -p 'Challenging YubiKey '"$serial"' slot '"$slot"' once with '"${message:-challenge »"$challenge"«}"'. Enter to continue, or Ctrl+C to abort:'
|
||||
fi
|
||||
if [[ "$serial" != "$(@{native.yubikey-personalization}/bin/ykinfo -sq)" ]] ; then printf 'YubiKey with serial %s not present, aborting.\n' "$serial" 1>&2 ; exit 1 ; fi
|
||||
if [[ "$serial" != "$( @{native.yubikey-personalization}/bin/ykinfo -sq )" ]] ; then printf 'YubiKey with serial %s not present, aborting.\n' "$serial" 1>&2 ; exit 1 ; fi
|
||||
|
||||
if [[ ! "${3:-}" ]] ; then
|
||||
secret="$(@{native.yubikey-personalization}/bin/ykchalresp -"$slot" "$challenge":1)""$(@{native.yubikey-personalization}/bin/ykchalresp -2 "$challenge":2)"
|
||||
secret="$( @{native.yubikey-personalization}/bin/ykchalresp -"$slot" "$challenge":1 )""$( sleep .5 || : ; @{native.yubikey-personalization}/bin/ykchalresp -"$slot" "$challenge":2 || @{native.yubikey-personalization}/bin/ykchalresp -"$slot" "$challenge":2 )" # the second consecutive challenge tends to fail if it follows immediately
|
||||
if [[ ${#secret} != 80 ]] ; then printf 'YubiKey challenge failed, aborting.\n' "$serial" 1>&2 ; exit 1 ; fi
|
||||
else
|
||||
secret="$(@{native.yubikey-personalization}/bin/ykchalresp -"$slot" "$challenge")"
|
||||
secret="$( @{native.yubikey-personalization}/bin/ykchalresp -"$slot" "$challenge" )"
|
||||
if [[ ${#secret} != 40 ]] ; then printf 'YubiKey challenge failed, aborting.\n' "$serial" 1>&2 ; exit 1 ; fi
|
||||
fi
|
||||
printf %s "$secret" | head -c 64
|
||||
|
@ -36,7 +36,8 @@ function do-disk-setup { # 1: diskPaths
|
||||
## Partitions the »diskPaths« instances of all »config.wip.fs.disks.devices« to ensure that all specified »config.wip.fs.disks.partitions« exist.
|
||||
# Parses »diskPaths«, creates and loop-mounts images for non-/dev/ paths, and tries to abort if any partition already exists on the host.
|
||||
function partition-disks { { # 1: diskPaths
|
||||
beQuiet=/dev/null ; if [[ ${args[debug]:-} ]] ; then beQuiet=/dev/stdout ; fi
|
||||
beLoud=/dev/null ; if [[ ${args[debug]:-} ]] ; then beLoud=/dev/stdout ; fi
|
||||
beSilent=/dev/stderr ; if [[ ${args[quiet]:-} ]] ; then beSilent=/dev/null ; fi
|
||||
declare -g -A blockDevs=( ) # this ends up in the caller's scope
|
||||
local path ; for path in ${1//:/ } ; do
|
||||
local name=${path/=*/} ; if [[ $name != "$path" ]] ; then path=${path/$name=/} ; else name=primary ; fi
|
||||
@ -75,26 +76,28 @@ function partition-disks { { # 1: diskPaths
|
||||
if [[ ${disk[serial]} != "$actual" ]] ; then echo "Block device $blockDev's serial ($actual) does not match the serial (${disk[serial]}) declared for ${disk[name]}" ; exit 1 ; fi
|
||||
fi
|
||||
# can (and probably should) restore the backup:
|
||||
( PATH=@{native.gptfdisk}/bin ; set -x ; sgdisk --zap-all --load-backup=@{config.wip.fs.disks.partitioning}/"${disk[name]}".backup ${disk[allowLarger]:+--move-second-header} "${blockDevs[${disk[name]}]}" >$beQuiet )
|
||||
( PATH=@{native.gptfdisk}/bin ; ${_set_x:-:} ; sgdisk --zap-all --load-backup=@{config.wip.fs.disks.partitioning}/"${disk[name]}".backup ${disk[allowLarger]:+--move-second-header} "${blockDevs[${disk[name]}]}" >$beLoud 2>$beSilent )
|
||||
#partition-disk "${disk[name]}" "${blockDevs[${disk[name]}]}"
|
||||
done
|
||||
@{native.parted}/bin/partprobe "${blockDevs[@]}"
|
||||
@{native.parted}/bin/partprobe "${blockDevs[@]}" &>$beLoud
|
||||
@{native.systemd}/bin/udevadm settle -t 15 || true # sometimes partitions aren't quite made available yet
|
||||
|
||||
# ensure that filesystem creation does not complain about the devices already being occupied by a previous filesystem
|
||||
wipefs --all "@{config.wip.fs.disks.partitions!attrNames[@]/#/'/dev/disk/by-partlabel/'}" >$beQuiet
|
||||
wipefs --all "@{config.wip.fs.disks.partitions!attrNames[@]/#/'/dev/disk/by-partlabel/'}" >$beLoud 2>$beSilent
|
||||
)}
|
||||
|
||||
## Given a declared disk device's »name« and a path to an actual »blockDev« (or image) file, partitions the device as declared in the config.
|
||||
function partition-disk {( set -eu # 1: name, 2: blockDev, 3?: devSize
|
||||
name=$1 ; blockDev=$2
|
||||
beLoud=/dev/null ; if [[ ${args[debug]:-} ]] ; then beLoud=/dev/stdout ; fi
|
||||
beSilent=/dev/stderr ; if [[ ${args[quiet]:-} ]] ; then beSilent=/dev/null ; fi
|
||||
eval 'declare -A disk='"@{config.wip.fs.disks.devices[$name]}"
|
||||
devSize=${3:-$( @{native.util-linux}/bin/blockdev --getsize64 "$blockDev" )}
|
||||
|
||||
declare -a sgdisk=( --zap-all ) # delete existing part tables
|
||||
if [[ ${disk[gptOffset]} != 0 ]] ; then
|
||||
sgdisk+=( --move-main-table=$(( 2 + ${disk[gptOffset]} )) ) # this is incorrectly documented as --adjust-main-table in the man pages (at least versions 1.05 to 1.09 incl)
|
||||
sgdisk+=( --move-backup-table=$(( devSize/512 - 1 - 32 - ${disk[gptOffset]} )) )
|
||||
sgdisk+=( --move-backup-table=$(( devSize/${disk[sectorSize]} - 1 - 32 - ${disk[gptOffset]} )) )
|
||||
fi
|
||||
sgdisk+=( --disk-guid="${disk[guid]}" )
|
||||
|
||||
@ -117,7 +120,7 @@ function partition-disk {( set -eu # 1: name, 2: blockDev, 3?: devSize
|
||||
sgdisk+=( --hybrid "${disk[mbrParts]}" ) # --hybrid: create MBR in addition to GPT; ${disk[mbrParts]}: make these GPT part 1 MBR parts 2[3[4]]
|
||||
fi
|
||||
|
||||
( PATH=@{native.gptfdisk}/bin ; set -x ; sgdisk "${sgdisk[@]}" "$blockDev" >$beQuiet ) # running all at once is much faster
|
||||
( PATH=@{native.gptfdisk}/bin ; ${_set_x:-:} ; sgdisk "${sgdisk[@]}" "$blockDev" >$beLoud ) # running all at once is much faster
|
||||
|
||||
if [[ ${disk[mbrParts]:-} ]] ; then
|
||||
printf "
|
||||
@ -126,7 +129,7 @@ function partition-disk {( set -eu # 1: name, 2: blockDev, 3?: devSize
|
||||
|
||||
# move the selected »mbrParts« to slots 1[2[3]] instead of 2[3[4]] (by re-creating part1 in the last sector, then sorting)
|
||||
n;p;1 # new ; primary ; part1
|
||||
$(( ($devSize/512) - 1)) # start (size 1sec)
|
||||
$(( ($devSize/${disk[sectorSize]}) - 1)) # start (size 1sec)
|
||||
x;f;r # expert mode ; fix order ; return
|
||||
d;$(( (${#disk[mbrParts]} + 1) / 2 + 1 )) # delete ; part(last)
|
||||
|
||||
@ -137,7 +140,7 @@ function partition-disk {( set -eu # 1: name, 2: blockDev, 3?: devSize
|
||||
|
||||
${disk[extraFDiskCommands]}
|
||||
p;w;q # print ; write ; quit
|
||||
" | @{native.gnused}/bin/sed -E 's/^ *| *(#.*)?$//g' | @{native.gnused}/bin/sed -E 's/\n\n+| *; */\n/g' | tee >((echo -n '++ ' ; tr $'\n' '|' ; echo) 1>&2) | ( PATH=@{native.util-linux}/bin ; set -x ; fdisk "$blockDev" &>$beQuiet )
|
||||
" | @{native.gnused}/bin/sed -E 's/^ *| *(#.*)?$//g' | @{native.gnused}/bin/sed -E 's/\n\n+| *; */\n/g' | tee >((echo -n '++ ' ; tr $'\n' '|' ; echo) 1>&2) | ( PATH=@{native.util-linux}/bin ; ${_set_x:-:} ; fdisk "$blockDev" &>$beLoud )
|
||||
fi
|
||||
)}
|
||||
|
||||
@ -154,7 +157,8 @@ function is-partition-on-disks {( set -eu # 1: partition, ...: blockDevs
|
||||
|
||||
## For each filesystem in »config.fileSystems« whose ».device« is in »/dev/disk/by-partlabel/«, this creates the specified file system on that partition.
|
||||
function format-partitions {( set -eu
|
||||
beQuiet=/dev/null ; if [[ ${args[debug]:-} ]] ; then beQuiet=/dev/stdout ; fi
|
||||
beLoud=/dev/null ; if [[ ${args[debug]:-} ]] ; then beLoud=/dev/stdout ; fi
|
||||
beSilent=/dev/stderr ; if [[ ${args[quiet]:-} ]] ; then beSilent=/dev/null ; fi
|
||||
for fsDecl in "@{config.fileSystems[@]}" ; do
|
||||
eval 'declare -A fs='"$fsDecl"
|
||||
if [[ ${fs[device]} == /dev/disk/by-partlabel/* ]] ; then
|
||||
@ -162,7 +166,7 @@ function format-partitions {( set -eu
|
||||
elif [[ ${fs[device]} == /dev/mapper/* ]] ; then
|
||||
if [[ ! @{config.boot.initrd.luks.devices!catAttrSets.device[${fs[device]/'/dev/mapper/'/}]:-} ]] ; then echo "LUKS device ${fs[device]} used by mount ${fs[mountPoint]} does not point at one of the device mappings ${!config.boot.initrd.luks.devices!catAttrSets.device[@]}" ; exit 1 ; fi
|
||||
else continue ; fi
|
||||
( PATH=@{native.e2fsprogs}/bin:@{native.f2fs-tools}/bin:@{native.xfsprogs}/bin:@{native.dosfstools}/bin:$PATH ; set -x ; mkfs.${fs[fsType]} ${fs[formatOptions]} "${fs[device]}" >$beQuiet )
|
||||
( PATH=@{native.e2fsprogs}/bin:@{native.f2fs-tools}/bin:@{native.xfsprogs}/bin:@{native.dosfstools}/bin:$PATH ; ${_set_x:-:} ; mkfs.${fs[fsType]} ${fs[formatOptions]} "${fs[device]}" >$beLoud 2>$beSilent )
|
||||
@{native.parted}/bin/partprobe "${fs[device]}" || true
|
||||
done
|
||||
for swapDev in "@{config.swapDevices!catAttrs.device[@]}" ; do
|
||||
@ -171,7 +175,7 @@ function format-partitions {( set -eu
|
||||
elif [[ $swapDev == /dev/mapper/* ]] ; then
|
||||
if [[ ! @{config.boot.initrd.luks.devices!catAttrSets.device[${swapDev/'/dev/mapper/'/}]:-} ]] ; then echo "LUKS device $swapDev used for SWAP does not point at one of the device mappings @{!config.boot.initrd.luks.devices!catAttrSets.device[@]}" ; exit 1 ; fi
|
||||
else continue ; fi
|
||||
( set -x ; mkswap "$swapDev" >$beQuiet )
|
||||
( set -x ; mkswap "$swapDev" >$beLoud 2>$beSilent )
|
||||
done
|
||||
)}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
## Entry point to the installation, see »./README.md«.
|
||||
function install-system {( set -eu # 1: blockDev
|
||||
trap - EXIT # start with empty traps for sub-shell
|
||||
prepare-installer "$@"
|
||||
do-disk-setup "${argv[0]}"
|
||||
install-system-to $mnt
|
||||
@ -15,7 +16,7 @@ function prepare-installer { # ...
|
||||
|
||||
generic-arg-parse "$@"
|
||||
|
||||
beQuiet=/dev/null ; if [[ ${args[debug]:-} ]] ; then set -x ; beQuiet=/dev/stdout ; fi
|
||||
if [[ ${args[debug]:-} ]] ; then set -x ; fi
|
||||
|
||||
: ${argv[0]:?"Required: Target disk or image paths."}
|
||||
|
||||
@ -32,9 +33,15 @@ function prepare-installer { # ...
|
||||
if @{native.zfs}/bin/zfs get -o value -H name "$poolName" &>/dev/null ; then echo "ZFS pool »$poolName« is already imported. Export the pool before running the installer." ; exit 1 ; fi
|
||||
done
|
||||
|
||||
if [[ ${SUDO_USER:-} ]] ; then function nix {( set +x ; declare -a args=("$@") ; PATH=$hostPath su - "$SUDO_USER" -c "$(declare -p args)"' ; nix "${args[@]}"' )} ; fi
|
||||
if [[ ${SUDO_USER:-} ]] ; then # use Nix as the user who called this script, as Nix may not be set up for root
|
||||
function nix {( set +x ; declare -a args=("$@") ; PATH=$hostPath su - "$SUDO_USER" -c "$(declare -p args)"' ; nix "${args[@]}"' )}
|
||||
else # use Nix by absolute path, as it won't be on »$PATH«
|
||||
PATH=$PATH:@{native.nix}/bin
|
||||
fi
|
||||
|
||||
if [[ ${args[debug]:-} ]] ; then set +e ; set -E ; trap 'code= ; cat 2>/dev/null || true ; @{native.bashInteractive}/bin/bash --init-file @{config.environment.etc.bashrc.source} || code=$? ; if [[ $code ]] ; then exit $code ; fi' ERR ; fi # On error, instead of exiting straight away, open a shell to allow diagnosing/fixing the issue. Only exit if that shell reports failure (e.g. CtrlC + CtrlD). Unfortunately, the exiting has to be repeated for level of each nested sub-shells. The cat eats anything lined up on stdin, which would otherwise be run in the shell (TODO: but it blocks if there is nothing on stdin, requiring Ctrl+D to be pressed).
|
||||
_set_x='set -x' ; if [[ ${args[quiet]:-} ]] ; then _set_x=: ; fi
|
||||
|
||||
if [[ ${args[debug]:-} ]] ; then set +e ; set -E ; trap 'code= ; timeout .2s cat &>/dev/null || true ; @{native.bashInteractive}/bin/bash --init-file @{config.environment.etc.bashrc.source} || code=$? ; if [[ $code ]] ; then exit $code ; fi' ERR ; fi # On error, instead of exiting straight away, open a shell to allow diagnosing/fixing the issue. Only exit if that shell reports failure (e.g. CtrlC + CtrlD). Unfortunately, the exiting has to be repeated for each level of each nested sub-shells. The »timeout cat« eats anything lined up on stdin, which would otherwise be sent to bash and interpreted as commands.
|
||||
|
||||
export PATH=$PATH:@{native.util-linux}/bin # Doing a system installation requires a lot of stuff from »util-linux«. This should probably be moved into the individual functions that actually use the tools ...
|
||||
|
||||
@ -48,9 +55,9 @@ function nixos-install-cmd {( set -eu # 1: mnt, 2: topLevel
|
||||
|
||||
## Copies the system's dependencies to the disks mounted at »$mnt« and installs the bootloader. If »$inspect« is set, a root shell will be opened in »$mnt« afterwards.
|
||||
# »$topLevel« may point to an alternative top-level dependency to install.
|
||||
function install-system-to {( set -eu # 1: mnt, 2?: topLevel
|
||||
function install-system-to {( set -eu # 1: mnt
|
||||
mnt=$1 ; topLevel=${2:-}
|
||||
targetSystem=@{config.system.build.toplevel}
|
||||
targetSystem=${args[toplevel]:-@{config.system.build.toplevel}}
|
||||
trap - EXIT # start with empty traps for sub-shell
|
||||
|
||||
# Link/create files that some tooling expects:
|
||||
@ -79,7 +86,7 @@ function install-system-to {( set -eu # 1: mnt, 2?: topLevel
|
||||
|
||||
# Copy system closure to new nix store:
|
||||
if [[ ${SUDO_USER:-} ]] ; then chown -R $SUDO_USER: $mnt/nix/store $mnt/nix/var ; fi
|
||||
( set -x ; time nix --extra-experimental-features nix-command copy --no-check-sigs --to $mnt ${topLevel:-$targetSystem} ) ; rm -rf $mnt/nix/var/nix/gcroots
|
||||
( cmd=( nix --extra-experimental-features nix-command --offline copy --no-check-sigs --to $mnt ${topLevel:-$targetSystem} ) ; if [[ ${args[quiet]:-} ]] ; then "${cmd[@]}" ; else set -x ; time "${cmd[@]}" ; fi ) ; rm -rf $mnt/nix/var/nix/gcroots
|
||||
# TODO: if the target has @{config.nix.autoOptimiseStore} and the host doesn't (there is no .links dir?), optimize now
|
||||
if [[ ${SUDO_USER:-} ]] ; then chown -R root:root $mnt/nix $mnt/nix/var ; chown :30000 $mnt/nix/store ; fi
|
||||
|
||||
|
@ -35,7 +35,10 @@ function populate-keystore { { # (void)
|
||||
done
|
||||
for usage in "${!methods[@]}" ; do
|
||||
if [[ "${methods[$usage]}" == home-composite || "${methods[$usage]}" == copy ]] ; then continue ; fi
|
||||
gen-key-"${methods[$usage]}" "$usage" "${options[$usage]}" | write-secret "$keystore"/"$usage".key || return 1
|
||||
for attempt in 2 3 x ; do
|
||||
if gen-key-"${methods[$usage]}" "$usage" "${options[$usage]}" | write-secret "$keystore"/"$usage".key ; then break ; fi
|
||||
if [[ $attempt == x ]] ; then return 1 ; fi ; echo "Retrying ($attempt/3):"
|
||||
done
|
||||
done
|
||||
for usage in "${!methods[@]}" ; do
|
||||
if [[ "${methods[$usage]}" != home-composite ]] ; then continue ; fi
|
||||
@ -57,10 +60,10 @@ function create-luks-layers {( set -eu # (void)
|
||||
primaryKey="$keystore"/luks/"$luksName"/0.key
|
||||
|
||||
keyOptions=( --pbkdf=pbkdf2 --pbkdf-force-iterations=1000 )
|
||||
( PATH=@{native.cryptsetup}/bin ; set -x ; cryptsetup --batch-mode luksFormat --key-file="$primaryKey" "${keyOptions[@]}" -c aes-xts-plain64 -s 512 -h sha256 "$rawDev" )
|
||||
( PATH=@{native.cryptsetup}/bin ; ${_set_x:-:} ; cryptsetup --batch-mode luksFormat --key-file="$primaryKey" "${keyOptions[@]}" -c aes-xts-plain64 -s 512 -h sha256 "$rawDev" )
|
||||
for index in 1 2 3 4 5 6 7 ; do
|
||||
if [[ -e "$keystore"/luks/"$luksName"/"$index".key ]] ; then
|
||||
( PATH=@{native.cryptsetup}/bin ; set -x ; cryptsetup luksAddKey --key-file="$primaryKey" "${keyOptions[@]}" "$rawDev" "$keystore"/luks/"$luksName"/"$index".key )
|
||||
( PATH=@{native.cryptsetup}/bin ; ${_set_x:-:} ; cryptsetup luksAddKey --key-file="$primaryKey" "${keyOptions[@]}" "$rawDev" "$keystore"/luks/"$luksName"/"$index".key )
|
||||
fi
|
||||
done
|
||||
done
|
||||
@ -74,7 +77,7 @@ function open-luks-layers { # (void)
|
||||
rawDev=@{config.boot.initrd.luks.devices!catAttrSets.device[$luksName]}
|
||||
primaryKey="$keystore"/luks/"$luksName"/0.key
|
||||
|
||||
( PATH=@{native.cryptsetup}/bin ; set -x ; cryptsetup --batch-mode luksOpen --key-file="$primaryKey" "$rawDev" "$luksName" ) &&
|
||||
( PATH=@{native.cryptsetup}/bin ; ${_set_x:-:} ; cryptsetup --batch-mode luksOpen --key-file="$primaryKey" "$rawDev" "$luksName" ) &&
|
||||
prepend_trap "@{native.cryptsetup}/bin/cryptsetup close $luksName" EXIT
|
||||
done
|
||||
}
|
||||
|
@ -74,10 +74,11 @@ function run-qemu {( set -eu # 1: diskImages
|
||||
done
|
||||
|
||||
if [[ @{config.boot.loader.systemd-boot.enable} || ${args[efi]:-} ]] ; then # UEFI. Otherwise it boots something much like a classic BIOS?
|
||||
#qemu+=( -bios @{pkgs.OVMF.fd}/FV/OVMF.fd ) # This works, but is a legacy fallback that stores the EFI vars in /NvVars on the EFI partition (which is really bad).
|
||||
qemu+=( -drive file=@{pkgs.OVMF.fd}/FV/OVMF_CODE.fd,if=pflash,format=raw,unit=0,readonly=on )
|
||||
qemu+=( -drive file=/tmp/qemu-@{config.networking.hostName}-VARS.fd,if=pflash,format=raw,unit=1 )
|
||||
if [[ ! -e /tmp/qemu-@{config.networking.hostName}-VARS.fd ]] ; then cat @{pkgs.OVMF.fd}/FV/OVMF_VARS.fd > /tmp/qemu-@{config.networking.hostName}-VARS.fd ; fi
|
||||
ovmf=$( @{native.nixVersions.nix_2_9}/bin/nix --extra-experimental-features 'nix-command flakes' build --no-link --print-out-paths @{inputs.nixpkgs}'#'legacyPackages.@{pkgs.system}.OVMF.fd )
|
||||
#qemu+=( -bios ${ovmf}/FV/OVMF.fd ) # This works, but is a legacy fallback that stores the EFI vars in /NvVars on the EFI partition (which is really bad).
|
||||
qemu+=( -drive file=${ovmf}/FV/OVMF_CODE.fd,if=pflash,format=raw,unit=0,readonly=on )
|
||||
qemu+=( -drive file="${args[efi-vars]:-/tmp/qemu-@{config.networking.hostName}-VARS.fd}",if=pflash,format=raw,unit=1 )
|
||||
if [[ ! -e "${args[efi-vars]:-/tmp/qemu-@{config.networking.hostName}-VARS.fd}" ]] ; then cat ${ovmf}/FV/OVMF_VARS.fd > "${args[efi-vars]:-/tmp/qemu-@{config.networking.hostName}-VARS.fd}" ; fi
|
||||
# https://lists.gnu.org/archive/html/qemu-discuss/2018-04/msg00045.html
|
||||
fi
|
||||
if [[ @{config.wip.preface.hardware} == aarch64 ]] ; then
|
||||
@ -87,7 +88,7 @@ function run-qemu {( set -eu # 1: diskImages
|
||||
# Add »config.boot.kernelParams = [ "console=tty1" "console=ttyS0" ]« to log to serial (»ttyS0«) and/or the display (»tty1«), preferring the last »console« option for the initrd shell (if enabled and requested).
|
||||
logSerial= ; if [[ ' '"@{config.boot.kernelParams[@]}"' ' == *' console=ttyS0'@( |,)* ]] ; then logSerial=1 ; fi
|
||||
logScreen= ; if [[ ' '"@{config.boot.kernelParams[@]}"' ' == *' console=tty1 '* ]] ; then logScreen=1 ; fi
|
||||
if [[ $logSerial ]] ; then
|
||||
if [[ ! ${args[no-serial]:-} && $logSerial ]] ; then
|
||||
if [[ $logScreen || ${args[graphic]:-} ]] ; then
|
||||
qemu+=( -serial mon:stdio )
|
||||
else
|
||||
|
@ -19,6 +19,40 @@ function generic-arg-parse { # ...
|
||||
shift ; done
|
||||
}
|
||||
|
||||
## Shows the help text for a program and exits, if »--help« was passed as argument and parsed, or does nothing otherwise.
|
||||
# Expects to be called between parsing and verifying the arguments.
|
||||
# Uses »allowedArgs« for the list of the named arguments (the values are the descriptions).
|
||||
# »name« should be the program name/path (usually »$0«), »args« the form/names of any positional arguments expected (e.g. »SOURCE... DEST«) and is included in the "Usage" description,
|
||||
# »description« the introductory text shown before the "Usage", and »suffix« any text printed after the argument list.
|
||||
function generic-arg-help { # 1: name, 2?: args, 3?: description, 4?: suffix
|
||||
if [[ ! ${args[help]:-} ]] ; then : ${allowedArgs[help]:=1} ; return 0 ; fi
|
||||
[[ ! ${3:-} ]] || echo "$3"
|
||||
printf 'Usage:\n %s [ARG[=value]]... [--] %s\n\nWhere »ARG« may be any of:\n' "$1" "${2:-}"
|
||||
local name ; while IFS= read -r name ; do
|
||||
printf ' %s\n %s\n' "$name" "${allowedArgs[$name]}"
|
||||
done < <( printf '%s\n' "${!allowedArgs[@]}" | LC_ALL=C sort )
|
||||
printf ' %s\n %s\n' "--help" "Do nothing but print this message and exit with success."
|
||||
[[ ! ${4:-} ]] || echo "$4"
|
||||
exit 0
|
||||
}
|
||||
|
||||
## Performs a basic verification of the named arguments passed by the user and parsed by »generic-arg-parse« against the names in »allowedArgs«.
|
||||
# Entries in »allowedArgs« should have the form »[--name]="description"« for boolean flags, and »[--name=VAL]="description"« for string arguments.
|
||||
# »description« is used by »generic-arg-help«. Boolean flags may only have the values »1« (as set by »generic-ags-parse« for flags without value) or be empty.
|
||||
# »VAL« is purely nominal. Any argument passed that is not in »allowedArgs« raises an error.
|
||||
function generic-arg-verify { # 1: exitCode
|
||||
local exitCode=${1:-1}
|
||||
local names=' '"${!allowedArgs[@]}"
|
||||
for name in "${!args[@]}" ; do
|
||||
if [[ ${allowedArgs[--$name]:-} ]] ; then
|
||||
if [[ ${args[$name]} == '' || ${args[$name]} == 1 ]] ; then continue ; fi
|
||||
echo "Argument »--$name« should be a boolean, but its value is: ${args[$name]}" ; return $exitCode
|
||||
fi
|
||||
if [[ $names == *' --'"$name"'='* || $names == *' --'"$name"'[='* ]] ; then continue ; fi
|
||||
echo "Unexpected argument »--$name«.${allowedArgs[help]:+ Call with »--help« for a list of valid arguments.}" ; return $exitCode
|
||||
done
|
||||
}
|
||||
|
||||
## Prepends a command to a trap. Especially useful fo define »finally« commands via »prepend_trap '<command>' EXIT«.
|
||||
# NOTE: When calling this in a sub-shell whose parents already has traps installed, make sure to do »trap - trapName« first. On a new shell, this should be a no-op, but without it, the parent shell's traps will be added to the sub-shell as well (due to strange behavior of »trap -p« (in bash ~5.1.8)).
|
||||
function prepend_trap { # 1: command, ...: trapNames
|
||||
|
@ -20,7 +20,7 @@ function create-zpools { # 1: mnt
|
||||
if ! is-partition-on-disks "$part" "${blockDevs[@]}" ; then echo "Partition alias $part used by zpool ${pool[name]} does not point at one of the target disks ${blockDevs[@]}" ; exit 1 ; fi
|
||||
fi
|
||||
done
|
||||
( PATH=@{native.zfs}/bin ; set -x ; zpool create "${args[@]}" -R "$mnt" "${pool[name]}" "${vdevs[@]}" )
|
||||
( PATH=@{native.zfs}/bin ; ${_set_x:-:} ; zpool create "${args[@]}" -R "$mnt" "${pool[name]}" "${vdevs[@]}" )
|
||||
) && {
|
||||
prepend_trap "@{native.zfs}/bin/zpool export '$poolName'" EXIT
|
||||
} ; done &&
|
||||
@ -62,7 +62,7 @@ function ensure-datasets {( set -eu # 1: mnt, 2?: filterExp
|
||||
names=$(IFS=, ; echo "${!props[*]}") ; values=$(IFS=$'\n' ; echo "${props[*]}")
|
||||
if [[ $values != "$($zfs get -o value -H "$names" "${dataset[name]}")" ]] ; then (
|
||||
declare -a args=( ) ; for name in "${!props[@]}" ; do args+=( "${name}=${props[$name]}" ) ; done
|
||||
( PATH=@{native.zfs}/bin ; set -x ; zfs set "${args[@]}" "${dataset[name]}" )
|
||||
( PATH=@{native.zfs}/bin ; ${_set_x:-:} ; zfs set "${args[@]}" "${dataset[name]}" )
|
||||
) ; fi
|
||||
|
||||
if [[ $cryptRoot && $($zfs get -o value -H encryptionroot "${dataset[name]}") != "$cryptRoot" ]] ; then ( # inherit key from parent (which the parent would also already have done if necessary)
|
||||
@ -72,28 +72,28 @@ function ensure-datasets {( set -eu # 1: mnt, 2?: filterExp
|
||||
if [[ $($zfs get -o value -H keystatus "${dataset[name]}") != available ]] ; then
|
||||
$zfs load-key -L file://"$cryptKey" "${dataset[name]}" # will unload with cryptRoot
|
||||
fi
|
||||
( PATH=@{native.zfs}/bin ; set -x ; zfs change-key -i "${dataset[name]}" )
|
||||
( PATH=@{native.zfs}/bin ; ${_set_x:-:} ; zfs change-key -i "${dataset[name]}" )
|
||||
) ; fi
|
||||
|
||||
else ( # create dataset
|
||||
if [[ ${props[keyformat]:-} == ephemeral ]] ; then
|
||||
props[encryption]=aes-256-gcm ; props[keyformat]=hex ; props[keylocation]=file:///dev/stdin ; explicitKeylocation=file:///dev/null
|
||||
declare -a args=( ) ; for name in "${!props[@]}" ; do args+=( -o "${name}=${props[$name]}" ) ; done
|
||||
</dev/urandom tr -dc 0-9a-f | head -c 64 | ( PATH=@{native.zfs}/bin ; set -x ; zfs create "${args[@]}" "${dataset[name]}" )
|
||||
</dev/urandom tr -dc 0-9a-f | head -c 64 | ( PATH=@{native.zfs}/bin ; ${_set_x:-:} ; zfs create "${args[@]}" "${dataset[name]}" )
|
||||
$zfs unload-key "${dataset[name]}"
|
||||
else
|
||||
if [[ $cryptRoot && $cryptRoot != ${dataset[name]} && $($zfs get -o value -H keystatus "$cryptRoot") != available ]] ; then
|
||||
$zfs load-key -L file://"$cryptKey" "$cryptRoot" ; trap "$zfs unload-key $cryptRoot || true" EXIT
|
||||
fi
|
||||
declare -a args=( ) ; for name in "${!props[@]}" ; do args+=( -o "${name}=${props[$name]}" ) ; done
|
||||
( PATH=@{native.zfs}/bin ; set -x ; zfs create "${args[@]}" "${dataset[name]}" )
|
||||
( PATH=@{native.zfs}/bin ; ${_set_x:-:} ; zfs create "${args[@]}" "${dataset[name]}" )
|
||||
fi
|
||||
if [[ ${props[canmount]} != off ]] ; then (
|
||||
mount -t zfs -o zfsutil "${dataset[name]}" $tmpMnt ; trap "umount '${dataset[name]}'" EXIT
|
||||
chmod 000 "$tmpMnt" ; ( chown "${dataset[uid]}:${dataset[gid]}" -- "$tmpMnt" ; chmod "${dataset[mode]}" -- "$tmpMnt" )
|
||||
) ; fi
|
||||
if [[ $explicitKeylocation && $explicitKeylocation != "${props[keylocation]:-}" ]] ; then
|
||||
( PATH=@{native.zfs}/bin ; set -x ; zfs set keylocation="$explicitKeylocation" "${dataset[name]}" )
|
||||
( PATH=@{native.zfs}/bin ; ${_set_x:-:} ; zfs set keylocation="$explicitKeylocation" "${dataset[name]}" )
|
||||
fi
|
||||
$zfs snapshot -r "${dataset[name]}"@empty
|
||||
) ; fi
|
||||
@ -101,7 +101,7 @@ function ensure-datasets {( set -eu # 1: mnt, 2?: filterExp
|
||||
eval 'declare -A allows='"${dataset[permissions]}"
|
||||
for who in "${!allows[@]}" ; do
|
||||
# »zfs allow $dataset« seems to be the only way to view permissions, and that is not very parsable -.-
|
||||
( PATH=@{native.zfs}/bin ; set -x ; zfs allow -$who "${allows[$who]}" "${dataset[name]}" >&2 )
|
||||
( PATH=@{native.zfs}/bin ; ${_set_x:-:} ; zfs allow -$who "${allows[$who]}" "${dataset[name]}" >&2 )
|
||||
done
|
||||
done
|
||||
|
||||
|
Reference in New Issue
Block a user