add --skip-formatting flag,

open debug shells with variables+functions
This commit is contained in:
Niklas Gollenstede 2023-08-14 18:59:47 +02:00
parent 41c75410e1
commit b2cbacc21e
12 changed files with 96 additions and 59 deletions

View File

@ -59,6 +59,7 @@
"extra_isize", // ext4 option
"fdisk", // program
"fetchurl", // nix function
"fido2", // protocol
"filesystems", // plural
"fmask", // mount
"foldl", // nix (fold left)
@ -203,5 +204,6 @@
"yubikey", // program
"YubiKeys", // plural
"zfsutil", // program / function
"zpool", "zpools", // zfs
],
}

Binary file not shown.

View File

@ -36,7 +36,7 @@ in rec {
{ networking.hostName = name; }
]; _file = "${dirname}/nixos.nix#modules"; } ];
extraModules = (nixosArgs.extraModules or [ ]) ++ modules ++ extraModules ++ [ ({ config, ... }: { imports = [ ({
extraModules = (nixosArgs.extraModules or [ ]) ++ modules ++ extraModules ++ [ { imports = [ (args: {
# These are passed as »extraModules« module argument and can thus be reused when defining containers and such (so define as much stuff as possible here).
# There is, unfortunately, no way to directly pass modules into all containers. Each container will need to be defined with »config.containers."${name}".config.imports = extraModules«.
# (One could do that automatically by defining »options.security.containers = lib.mkOption { type = lib.types.submodule (cfg: { options.config = lib.mkOption { apply = _:_.extendModules { modules = extraModules; }; }); }«.)
@ -47,7 +47,7 @@ in rec {
system.nixos.revision = lib.mkIf (inputs?nixpkgs.rev) inputs.nixpkgs.rev; # (evaluating the default value fails under some circumstances)
}) ]; _file = "${dirname}/nixos.nix#mkNixosConfiguration-extraModule"; }) ];
}) ]; _file = "${dirname}/nixos.nix#mkNixosConfiguration-extraModule"; } ];
specialArgs = (nixosArgs.specialArgs or { }) // { inherit inputs; };
# (This is already set during module import, while »_module.args« only becomes available during module evaluation (before that, using it causes infinite recursion). Since it can't be ensured that this is set in every circumstance where »extraModules« are being used, it should generally not be used to set custom arguments.)
@ -77,7 +77,7 @@ in rec {
inherit name mainModule;
moduleArgs = (moduleArgs // { inherit preface; });
nixosArgs = (args.nixosArgs or { }) // { specialArgs = (args.nixosArgs.specialArgs or { }) // { inherit preface; }; }; # make this available early, and only for the main evaluation (+specialisations, -containers)
extraModules = (args.extraModules or [ ]) ++ [ { imports = [ ({
extraModules = (args.extraModules or [ ]) ++ [ { imports = [ (args: {
options.${preface'} = {
instances = lib.mkOption { description = "List of host names to instantiate this host config for, instead of just for the file name."; type = lib.types.listOf lib.types.str; readOnly = true; } // (lib.optionalAttrs (!preface?instances) { default = instances; });
id = lib.mkOption { description = "This system's ID. If set, »mkSystemsFlake« will ensure that the ID is unique among all »moduleArgs.nodes«."; type = lib.types.nullOr (lib.types.either lib.types.int lib.types.str); readOnly = true; apply = id: if id == null then null else toString id; } // (lib.optionalAttrs (!preface?id) { default = null; });
@ -171,7 +171,7 @@ in rec {
description = ''
Call per-host setup and maintenance commands. Most importantly, »install-system«.
'';
ownPath = if (system.config.${installer}.outputName != null) then "nix run REPO#${system.config.${installer}.outputName} --" else "$0";
ownPath = if (system.config.${installer}.outputName != null) then "nix run REPO#${system.config.${installer}.outputName} --" else builtins.placeholder "out";
usageLine = ''
Usage:
%s [sudo] [bash] [--FLAG[=value]]... [--] [COMMAND [ARG]...]
@ -207,13 +207,14 @@ in rec {
tools = lib.unique (map (p: p.outPath) (lib.filter lib.isDerivation pkgs.stdenv.allowedRequisites));
esc = lib.escapeShellArg;
in pkgs.writeShellScript "scripts-${name}" ''
self=${builtins.placeholder "out"}
# if first arg is »sudo«, re-execute this script with sudo (as root)
if [[ ''${1:-} == sudo ]] ; then shift ; exec sudo --preserve-env=SSH_AUTH_SOCK -- "$0" "$@" ; fi
if [[ ''${1:-} == sudo ]] ; then shift ; exec sudo --preserve-env=SSH_AUTH_SOCK -- "$self" "$@" ; fi
# if the (now) first arg is »bash« or there are no args, re-execute this script as bash »--init-file«, starting an interactive bash in the context of the script
if [[ ''${1:-} == bash ]] || [[ $# == 0 && $0 != ${pkgs.bashInteractive}/bin/bash ]] ; then
shift ; exec ${pkgs.bashInteractive}/bin/bash --init-file <(cat << "EOS"${"\n"+''
shift ; exec ${pkgs.bashInteractive}/bin/bash --init-file <(echo '
# prefix the script to also include the default init files
! [[ -e /etc/profile ]] || . /etc/profile
for file in ~/.bash_profile ~/.bash_login ~/.profile ; do
@ -222,8 +223,9 @@ in rec {
# add active »hostName« to shell prompt
PS1=''${PS1/\\$/\\[\\e[93m\\](${name})\\[\\e[97m\\]\\$}
''}EOS
cat $0) -i -s ':' "$@"
source "'"$self"'" ; PATH=$hostPath
') -i -s ':' "$@"
fi
# provide installer tools (not necessarily for system.pkgs.config.hostPlatform)
@ -238,19 +240,18 @@ in rec {
shopt -s expand_aliases # enable aliases in non-interactive bash
for control in return exit ; do alias $control='{
status=$? ; if ! (( status )) ; then '$control' 0 ; fi # control flow return
if ! ${pkgs.bashInteractive}/bin/bash --init-file ${system.config.environment.etc.bashrc.source} ; then '$control' $status ; fi # »|| '$control'« as an error-catch
if ! PATH=$hostPath "$self" bash ; then '$control' $status ; fi # »|| '$control'« as an error-catch
#if ! ${pkgs.bashInteractive}/bin/bash --init-file ${system.config.environment.etc.bashrc.source} ; then '$control' $status ; fi # »|| '$control'« as an error-catch
}' ; done
fi
declare -g -A allowedArgs=( ) allowedArgCtx=( ) ; function declare-flag { # 1: context, 2: name, 3?: value, 4: description
if [[ ''${allowedArgCtx[$2]:-} && ''${allowedArgCtx[$2]:-} != "$1" ]] ; then echo "Flag $2 was declared in conflicting contexts ''${allowedArgCtx[$2]} and $1" >&2 ; \exit 1 ; fi
allowedArgCtx[$2]=$1
declare -g -A allowedArgs=( ) ; function declare-flag { # 1: context, 2: name, 3?: value, 4: description
local name=--$2 ; if [[ $3 ]]; then name+='='$3 ; fi ; allowedArgs[$name]="($1) $4"
}
declare-flag global command "" 'Interpret the first positional argument as bash script (instead of the name of a single command) and »eval« it (with access to all commands and internal functions and variables).'
declare-flag global debug "" 'Hook into any »|| exit« / »|| return« statements and open a shell if they are triggered by an error. Implies »--trace«.'
declare-flag global trace "" "Turn on bash's »errtrace« option before running »COMMAND«."
declare-flag global quiet "" "Try to suppress all non-error output. May also swallow some error related output."
declare-flag '*' command "" 'Interpret the first positional argument as bash script (instead of the name of a single command) and »eval« it (with access to all commands and internal functions and variables).'
declare-flag '*' debug "" 'Hook into any »|| exit« / »|| return« statements and open a shell if they are triggered by an error. Implies »--trace«.'
declare-flag '*' trace "" "Turn on bash's »errtrace« option before running »COMMAND«."
declare-flag '*' quiet "" "Try to suppress all non-error output. May also swallow some error related output."
declare -g -A allowedCommands=( ) ; function declare-command { allowedCommands[$@]=$(< /dev/stdin) ; }
${system.config.${installer}.build.scripts { native = pkgs; }}
if [[ ''${args[help]:-} ]] ; then (

View File

@ -3,22 +3,36 @@
# Disk Partitioning and Formatting
##
declare-flag install-system skip-formatting "" "Skip partitioning, formatting, and their post-commands. Instead, assume that all required disks/images/zpools are correctly partitioned/formatted, and simply (unlock/import and) mount them. This is useful to skip the destruktive part of the installation, but still do the (largely idempotent) part of copying and linking the current system generation and installing the bootloader -- i.e, repair an installation."
## Prepares the disks of the target system for the copying of files.
function do-disk-setup { # 1: diskPaths
ensure-disks "$1" || return
prompt-for-user-passwords || return
export mnt=/tmp/nixos-install-@{config.networking.hostName} && mkdir -p "$mnt" && prepend_trap "rmdir $mnt" EXIT || return # »mnt=/run/user/0/...« would be more appropriate, but »nixos-install« does not like the »700« permissions on »/run/user/0«
if [[ ${args[skip-formatting]:-} ]] ; then
if [[ @{config.setup.keystore.enable} ]] ; then
mount-keystore-luks-primary || return
else
populate-keystore || return # (this may be insufficient)
fi
open-luks-layers || return
if [[ $(LC_ALL=C type -t import-zpools) == function ]] ; then import-zpools $mnt || return ; fi
else
populate-keystore || return
mnt=/tmp/nixos-install-@{config.networking.hostName} && mkdir -p "$mnt" && prepend_trap "rmdir $mnt" EXIT || return # »mnt=/run/user/0/...« would be more appropriate, but »nixos-install« does not like the »700« permissions on »/run/user/0«
partition-disks || return
create-luks-layers && open-luks-layers || return # other block layers would go here too (but figuring out their dependencies would be difficult)
create-luks-layers || return
open-luks-layers || return
# other block layers would go here too (but figuring out their dependencies would be difficult)
run-hook-script 'Post Partitioning' @{config.installer.commands.postPartition!writeText.postPartitionCommands} || return
format-partitions || return
if [[ $(LC_ALL=C type -t create-zpools) == function ]] ; then create-zpools $mnt || return ; fi
run-hook-script 'Post Formatting' @{config.installer.commands.postFormat!writeText.postFormatCommands} || return
fi
fix-grub-install || return
@ -52,7 +66,7 @@ function ensure-disks { # 1: diskPaths, 2?: skipLosetup
fi
local name ; for name in "@{!config.setup.disks.devices[@]}" ; do
if [[ ! @{config.setup.disks.devices!catAttrSets.partitionDuringInstallation[$name]} ]] ; then continue ; fi
if [[ ! @{config.setup.disks.devices!catAttrSets.partitionDuringInstallation[$name]} ]] ; then unset blockDevs[$name] ; continue ; fi
if [[ ! ${blockDevs[$name]:-} ]] ; then echo "Path for block device $name not provided" 1>&2 ; \return 1 ; fi
eval 'local -A disk='"@{config.setup.disks.devices[$name]}"
if [[ ${blockDevs[$name]} != /dev/* ]] ; then

View File

@ -103,7 +103,7 @@ function reexec-in-qemu {
echo 'Performing the installation in a cross-ISA qemu system VM; this will be very, very slow (many hours) ...'
output=@{inputs.self}'#'nixosConfigurations.@{config.installer.outputName:?}.config.system.build.vmExec-@{pkgs.buildPackages.system}
fi
local scripts=$0 ; if [[ @{pkgs.system} != "@{native.system}" ]] ; then
local scripts=$self ; if [[ @{pkgs.system} != "@{native.system}" ]] ; then
scripts=$( build-lazy @{inputs.self}'#'apps.@{pkgs.system}.@{config.installer.outputName:?}.derivation ) || return
fi
local command="$scripts install-system $( printf '%q ' "${newArgs[@]}" ) || exit"
@ -129,7 +129,7 @@ declare-flag install-system toplevel "" "Optional replacement for the actual »c
declare-flag install-system no-inspect "" "Do not inspect the (successfully) installed system before unmounting its filesystems."
declare-flag install-system inspect-cmd "script" "Instead of opening an interactive shell for the post-installation inspection, »eval« this script."
## 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.
## Copies the system's dependencies to the disks mounted at »$mnt« and installs the bootloader. By default, 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 -u # 1: mnt, 2?: topLevel
targetSystem=${args[toplevel]:-@{config.system.build.toplevel}}
@ -200,7 +200,7 @@ function install-system-to {( set -u # 1: mnt, 2?: topLevel
else
( set +x ; echo "Installation done! This shell is in a chroot in the mounted system for inspection. Exiting the shell will unmount the system." 1>&2 )
fi
LC_ALL=C PATH=$PATH:@{native.util-linux}/bin @{native.nixos-install-tools}/bin/nixos-enter --root $mnt -- /nix/var/nix/profiles/system/sw/bin/bash -c 'source /etc/set-environment ; exec bash --login' || exit # +o monitor
LC_ALL=C PATH=$PATH:@{native.util-linux}/bin @{native.nixos-install-tools}/bin/nixos-enter --root $mnt -- /nix/var/nix/profiles/system/sw/bin/bash -c 'source /etc/set-environment ; CHROOT_DIR="'"$mnt"'" mnt=/ exec "'"$self"'" bash' || exit # +o monitor
fi
mkdir -p $mnt/var/lib/systemd/timesync && touch $mnt/var/lib/systemd/timesync/clock || true # save current time

View File

@ -161,7 +161,7 @@ function run-qemu {
done ; fi
if [[ ${args[install]:-} == always ]] ; then
local verbosity=--quiet ; if [[ ${args[trace]:-} ]] ; then verbosity=--trace ; fi ; if [[ ${args[debug]:-} ]] ; then verbosity=--debug ; fi
hostPath=${hostPath:-} ${args[dry-run]:+echo} $0 install-system "$diskImages" $verbosity --no-inspect || return
hostPath=${hostPath:-} ${args[dry-run]:+echo} "$self" install-system "$diskImages" $verbosity --no-inspect || return
fi
qemu+=( "${argv[@]}" )
@ -198,6 +198,18 @@ function mount-keystore-luks {
@{native.util-linux}/bin/mount -o nodev,umask=0077,fmask=0077,dmask=0077,ro /dev/mapper/$keystore /run/$keystore && prepend_trap "@{native.util-linux}/bin/umount /run/$keystore" EXIT || return
}
## Opens the keystore with the primary unlock method, which may not be convenient to use, but should always be defined.
function mount-keystore-luks-primary {
local usage=luks/keystore-@{config.networking.hostName!hashString.sha256:0:8}/0
local method=@{config.setup.keystore.keys[$usage]%%=*}
local options=@{config.setup.keystore.keys[$usage]:$(( ${#method} + 1 ))}
local attempt ; for attempt in 2 3 x ; do
if mount-keystore-luks --key-file=<( gen-key-"$method" "$usage" "$options" ) ; then break ; fi
if [[ $attempt == x ]] ; then \return 1 ; fi ; echo "Retrying ($attempt/3):"
done
}
declare-command open-system diskImages << 'EOD'
Performs any steps necessary to mount the target system at »/tmp/nixos-install-@{config.networking.hostName}« on the current host.
For any steps taken, it also adds the steps to undo them on exit from the calling shell (so this is not useful as a standalone »COMMAND«, use the »bash« or »--command« options, and don't call this from a sub-shell that exits too early).
@ -220,27 +232,19 @@ function open-system {
@{native.systemd}/bin/udevadm settle -t 15 || true # sometimes partitions aren't quite made available yet
if [[ @{config.setup.keystore.enable} && ! -e /dev/mapper/keystore-@{config.networking.hostName!hashString.sha256:0:8} ]] ; then # Try a bunch of approaches for opening the keystore:
mount-keystore-luks --key-file=<( printf %s "@{config.networking.hostName}" ) || return
mount-keystore-luks --key-file=/dev/disk/by-partlabel/bootkey-@{config.networking.hostName!hashString.sha256:0:8} || return
mount-keystore-luks --key-file=<( read -s -p PIN: pin && echo ' touch!' >&2 && @{native.yubikey-personalization}/bin/ykchalresp -2 "$pin" ) || return
# TODO: try static yubikey challenge
mount-keystore-luks || return
mount-keystore-luks --key-file=<( printf %s "@{config.networking.hostName}" ) || # costs nothing to try
mount-keystore-luks --key-file=/dev/disk/by-partlabel/bootkey-@{config.networking.hostName!hashString.sha256:0:8} || # costs nothing to try
mount-keystore-luks-primary || # should always be applicable
mount-keystore-luks --key-file=<( read -s -p PIN: pin && echo ' touch!' >&2 && @{native.yubikey-personalization}/bin/ykchalresp -2 "$pin" ) ||
mount-keystore-luks || # (getting desperate)
return # oh well
fi
mnt=/tmp/nixos-install-@{config.networking.hostName} # allow this to leak into the calling scope
export mnt=/tmp/nixos-install-@{config.networking.hostName} # allow this to leak into the calling scope
if [[ ! -e $mnt ]] ; then mkdir -p "$mnt" && prepend_trap "rmdir '$mnt'" EXIT || return ; fi
open-luks-layers || return # Load crypt layers and zfs pools:
if [[ $( LC_ALL=C type -t ensure-datasets ) == 'function' ]] ; then
local poolName ; for poolName in "@{!config.setup.zfs.pools[@]}" ; do
if [[ ! @{config.setup.zfs.pools!catAttrSets.createDuringInstallation[$poolName]} ]] ; then continue ; fi
if ! @{native.zfs}/bin/zfs get -o value -H name "$poolName" &>/dev/null ; then
@{native.zfs}/bin/zpool import -f -N -R "$mnt" "$poolName" && prepend_trap "@{native.zfs}/bin/zpool export '$poolName'" EXIT || return
fi
: | @{native.zfs}/bin/zfs load-key -r "$poolName" || true
ensure-datasets "$mnt" '^'"$poolName"'($|[/])' || return
done
fi
if [[ $( LC_ALL=C type -t import-zpools ) == 'function' ]] ; then import-zpools "$mnt" skipImported ; fi
prepend_trap "unmount-system '$mnt'" EXIT && mount-system "$mnt" '' 1 || return
df -h | grep $mnt | cat

View File

@ -79,7 +79,7 @@ function copy-function { # 1: existingName, 2: newName
function mkdir-sticky { # 1: path, 2?: fallbackOwner, 3?: fallbackGroup, 4?: fallbackMode
local path ; path=$1 ; shift
if [[ -d $path ]] ; then return ; fi # existing (symlink to existing) dir
if [[ -L $path || -e $path ]] ; then echo "Can't create (child of) existing file (or broken symlink) '$path'" 1>&2 ; return 1 ; fi
if [[ -L $path || -e $path ]] ; then echo "Can't create (child of) existing file (or broken symlink) '$path'" 1>&2 ; \return 1 ; fi
local parent ; parent=$( dirname "$path" ) || return
mkdir-sticky "$parent" "$@" || return
parent=$( realpath "$parent" ) || return

View File

@ -7,11 +7,23 @@ function create-zpools { # 1: mnt
done
}
## Imports all of the system's ZFS pools that are »createDuringInstallation« and not imported yet.
function import-zpools { # 1: mnt, 2: skipImported
local mnt=$1 ; local skipImported=${2:-}
local poolName ; for poolName in "@{!config.setup.zfs.pools[@]}" ; do
if [[ ! @{config.setup.zfs.pools!catAttrSets.createDuringInstallation[$poolName]} ]] ; then continue ; fi
if @{native.zfs}/bin/zfs get -o value -H name "$poolName" &>/dev/null && [[ $skipImported ]] ; then continue ; fi
@{native.zfs}/bin/zpool import -f -N -R "$mnt" "$poolName" && prepend_trap "@{native.zfs}/bin/zpool export '$poolName'" EXIT || return
: | @{native.zfs}/bin/zfs load-key -r "$poolName" || true
ensure-datasets "$mnt" '^'"$poolName"'($|[/])' || return
done
}
declare-command create-zpool mnt poolName << 'EOD'
Creates a single of the system's ZFS pools, and its datasets. Can be called manually to create pools that were added to the configuration, or to create those declared with »createDuringInstallation = false«. Expects the backing device(-partition)s to exist as declared for the pool.
EOD
declare-flag install-system zpool-force "" "(create-zpool) When creating ZFS storage pools, pass the »-f« (force) option. This may be required when installing to disks that are currently part of a pool, or ZFS refuses do reuse them."
declare-flag install-system,create-zpool zpool-force "" "When creating ZFS storage pools, pass the »-f« (force) option. This may be required when installing to disks that are currently part of a pool, or ZFS refuses do reuse them."
function create-zpool {
local mnt=$1 ; local poolName=$2
eval 'local -A pool='"@{config.setup.zfs.pools[$poolName]}"
@ -67,7 +79,8 @@ function ensure-datasets {
if $zfs get -o value -H name "${dataset[name]}" &>/dev/null ; then # dataset exists: check its properties
if [[ ${props[mountpoint]:-} ]] ; then # don't set the current mount point again (no-op), cuz that fails if the dataset is mounted
local current=$($zfs get -o value -H mountpoint "${dataset[name]}") ; current=${current/$mnt/}
# (The behavior inside a (nixos-enter) chroot is quite odd: ZFS ignores the chroot when printing the mountpoint, but heeds it when setting it. So this test fails (if the CHROOT_DIR is not set), but the set operation still sets the correct value.)
local current=$($zfs get -o value -H mountpoint "${dataset[name]}") ; current=${current/${CHROOT_DIR:-}$mnt/}
if [[ ${props[mountpoint]} == "${current:-/}" ]] ; then unset props[mountpoint] ; fi
fi
if [[ ${props[keyformat]:-} == ephemeral ]] ; then

View File

@ -71,7 +71,7 @@ in {
system.boot.loader.id = "extlinux";
system.build.installBootLoader = "${pkgs.writeShellScript "install-extlinux.sh" ''
if [[ ! ''${1:-} || $1 != /nix/store/* ]] ; then echo "Usage: $0 TOPLEVEL_PATH" 1>&2 ; exit 1 ; fi
if [[ ! ''${1:-} || $1 != /nix/store/* ]] ; then echo "Usage: ${builtins.placeholder "out"} TOPLEVEL_PATH" 1>&2 ; exit 1 ; fi
export PATH=$PATH:${pkgs.stdenv}/bin
${extlinux-conf-builder} "$1" -d ${esc cfg.targetDir}

View File

@ -12,9 +12,9 @@ The default functions in [`lib/setup-scripts`](../../lib/setup-scripts/README.md
What keys are used for is derived from the attribute name in the `.keys` specification, which (plus a `.key` suffix) also becomes their storage path in the keystore:
* Keys in `luks/` are used for LUKS devices, where the second path label is both the target device name and source device GPT partition label, and the third and final label is the LUKS key slot (`0` is required to be specified, `1` to `7` are optional).
* Keys in `zfs/` are used for ZFS datasets, where the further path is that of the dataset. Datasets implicitly inherit their parent's encryption by default. An empty key (created by method `unencrypted`) explicitly disables encryption on a dataset. Other keys are by default used with `keyformat=hex` and must thus be exactly 64 (lowercase) hex digits.
* Keys in `home/` are meant to be used as composites for home directory encryption, where the second and only other path label us the user name.
* Keys in `home/` are meant to be used as composites for home directory encryption, where the second and only other path label is the user name.
The attribute value in the `.keys` keys specification dictates how the key is acquired, primarily initially during installation, but (depending on the keys usage) also during boot unlocking, etc.
The attribute value in the `.keys` keys specification dictates how the key is acquired, primarily initially during installation, but (depending on the key's usage) also during boot unlocking, etc.
The format of the key specification is `method[=args]`, where `method` is the suffix of a bash function call `gen-key-<method>` (the default functions are in [`add-key.sh`](../../lib/setup-scripts/add-key.sh), but others could be added to the installer), and `args` is the second argument to the respective function (often a `:` separated list of arguments, but some methods don't need any arguments at all).
Most key generation methods only make sense in some key usage contexts. A `random` key is impossible to provide to unlock the keystore (which it is stored in), but is well suited to unlock other devices (if the keystore has backups); conversely a USB-partition can be used to headlessly unlock the keystore, but would be redundant for any further devices, as it would also be copied into the keystore.

View File

@ -10,7 +10,7 @@ Such state generally falls into one of four categories:
3) It may or may not be secret and can be re-generated quickly.
Category 1 data can not be included in the nix store and thus can not (directly) be derived from the system configuration.
It needs to be stored in a way that the host (and only the host) can access it an that it can't be lost.
It needs to be stored in a way that the host (and only the host) can access it and that it can't be lost.
It is therefore referred to as `remote` data (even though it would usually also be stored locally).
Category 2 data is required for the system to boot (in reasonable time), and should thus, as `local` data, be stored persistently on the host.
@ -204,7 +204,15 @@ in {
}) (lib.mkIf cfg.persistenceFixes { # Cope with the consequences of having »/« (including »/{etc,var,root,...}«) cleared on every reboot.
environment.etc.nixos.source = "/local/etc/nixos";
environment.etc = {
nixos.source = "/local/etc/nixos";
# SSHd host keys:
"ssh/ssh_host_ed25519_key".source = "/${keep}/etc/ssh/ssh_host_ed25519_key";
"ssh/ssh_host_ed25519_key.pub".source = "/${keep}/etc/ssh/ssh_host_ed25519_key.pub";
"ssh/ssh_host_rsa_key".source = "/${keep}/etc/ssh/ssh_host_rsa_key";
"ssh/ssh_host_rsa_key.pub".source = "/${keep}/etc/ssh/ssh_host_rsa_key.pub";
};
systemd.tmpfiles.rules = [ # keep in mind: this does not get applied super early ...
# »/root/.nix-channels« is already being restored.
@ -216,12 +224,7 @@ in {
"f /${keep}/root/.local/share/nix/repl-history 0600 root root -"
"L+ /root/.local/share/nix/repl-history - - - - ../../../../${keep}/root/.local/share/nix/repl-history"
# SSHd host keys:
"d /${keep}/etc/ssh/ 0755 root root - -"
"L /etc/ssh/ssh_host_ed25519_key - - - - /${keep}/etc/ssh/ssh_host_ed25519_key"
"L /etc/ssh/ssh_host_ed25519_key.pub - - - - /${keep}/etc/ssh/ssh_host_ed25519_key.pub"
"L /etc/ssh/ssh_host_rsa_key - - - - /${keep}/etc/ssh/ssh_host_rsa_key"
"L /etc/ssh/ssh_host_rsa_key.pub - - - - /${keep}/etc/ssh/ssh_host_rsa_key.pub"
];
fileSystems = { # this does get applied early

View File

@ -3,7 +3,7 @@
# ZFS Pools and Datasets
This module primarily allows the specification of ZFS pools and datasets. The declared pools and datasets are complemented with some default and are then used by [`lib/setup-scripts/zfs.sh`](../../lib/setup-scripts/zfs.sh) to create them during system installation, and can optionally later be kept up to date (with the config) at config activation time or during reboot.
Additionally, this module sets some defaults for ZFS (but only in a "always better than nothing" style, so `lib.mkForce null` should never be necessary).
Additionally, this module sets some defaults for ZFS (but only in an "always better than nothing" style, so `lib.mkForce null` should never be necessary).
## Implementation