diff --git a/.vscode/settings.json b/.vscode/settings.json index 265045e..495c080 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -147,6 +147,7 @@ "partlabel", // linux "partprobe", // program / function "partuuid", // linux + "pbkdf", // option "pflash", // cli arg "pipefail", // bash "pkgs", // nix diff --git a/hosts/example.nix.md b/example/hosts/example.nix.md similarity index 98% rename from hosts/example.nix.md rename to example/hosts/example.nix.md index 0b31cc9..a7cf538 100644 --- a/hosts/example.nix.md +++ b/example/hosts/example.nix.md @@ -36,7 +36,7 @@ in { preface = { # (any »preface« options have to be defined here) boot.loader.grub.enable = false; # Example of adding and/or overwriting setup/maintenance functions: - #installer.scripts.install-overwrite = { path = ../example/install.sh.md; order = 1500; }; + #installer.scripts.install-overwrite.path = ../lib/install.sh.md; boot.initrd.systemd.enable = true; diff --git a/example/install.sh.md b/example/lib/install.sh.md similarity index 81% rename from example/install.sh.md rename to example/lib/install.sh.md index 52f6d08..11ea2d7 100644 --- a/example/install.sh.md +++ b/example/lib/install.sh.md @@ -4,7 +4,7 @@ # Installer Script Overrides (Example) This is an example on how to customize the default installation process. -The [`config.installer.commands.*`](../modules/installer.nix.md) can be used for some per-host customization, but for further reaching changes that are supposed to affect all hosts in a configuration, it may be necessary or more appropriate to extend/override the [default installer functions](../lib/setup-scripts/). +The [`config.installer.commands.*`](../../modules/installer.nix.md) can be used for some per-host customization, but for further reaching changes that are supposed to affect all hosts in a configuration, it may be necessary or more appropriate to extend/override the [default installer functions](../../lib/setup-scripts/). This file would need to be included in the configuration like this: ```nix @@ -32,7 +32,7 @@ declare-command my-thing withThese coolArguments << 'EOD' This does my thing with these cool arguments -- duh! EOD declare-flag my-thing laser-color COLOR "Color of the laser used for my thing." -function run-qemu { # 1: withThese, 2: coolArguments +function my-thing { # 1: withThese, 2: coolArguments local withThese=$1 local coolThings=$2 echo "I am playing $withThese to shoot $coolArguments with ${args[laser-color]:-red} lasers!" diff --git a/flake.lock b/flake.lock index 0f7cd08..bcd64cb 100644 Binary files a/flake.lock and b/flake.lock differ diff --git a/flake.nix b/flake.nix index 618acd7..4f7238d 100644 --- a/flake.nix +++ b/flake.nix @@ -6,17 +6,18 @@ functions = { url = "github:NiklasGollenstede/nix-functions"; inputs.nixpkgs.follows = "nixpkgs"; }; config.url = "github:NiklasGollenstede/nixos-installer?dir=example/defaultConfig"; # "path:./example/defaultConfig"; # (The latter only works on each host after using this flake directly (not as dependency or another flake). The former effectively points to the last commit, i.e. it takes two commits to apply changes to the default config.) -}; outputs = inputs@{ self, ... }: inputs.functions.lib.importRepo inputs ./. (repo@{ overlays, ... }: let +}; outputs = inputs@{ self, ... }: inputs.functions.lib.importRepo inputs ./. (repo': let + repo = repo'.override { applyToPackages = pkgs: packages: builtins.removeAttrs packages [ "libblockdev" ]; }; lib = repo.lib.__internal__; in [ # Run »nix flake show --allow-import-from-derivation« to see what this merges to: ## Exports (things to reuse in other flakes): - (repo // { packages = lib.fun.packagesFromOverlay { inherit inputs; exclude = [ "libblockdev" ]; }; }) # lib.* nixosModules.* overlays.* packages.* + repo # lib.* nixosModules.* overlays.* packages.* ## Examples: # The example host definitions from ./hosts/, plus their installers (apps): - (lib.self.mkSystemsFlake { inherit inputs; asDefaultPackage = true; }) # nixosConfigurations.* apps.*-linux.* devShells.*-linux.* packages.*-linux.all-systems/default + (lib.self.mkSystemsFlake { inherit inputs; hosts.dir = "${self}/example/hosts"; asDefaultPackage = true; }) # nixosConfigurations.* apps.*-linux.* devShells.*-linux.* packages.*-linux.all-systems/default # The same cross-compiled from aarch64 (just to show how that works): - (lib.self.mkSystemsFlake { inherit inputs; buildPlatform = "aarch64-linux"; renameOutputs = name: "arm:${name}"; }) # nixosConfigurations.arm:* apps.*-linux.arm:* devShells.*-linux.arm:* packages.*-linux.arm:all-systems + (lib.self.mkSystemsFlake { inherit inputs; hosts.dir = "${self}/example/hosts"; buildPlatform = "aarch64-linux"; renameOutputs = name: "arm:${name}"; }) # nixosConfigurations.arm:* apps.*-linux.arm:* devShells.*-linux.arm:* packages.*-linux.arm:all-systems ]); } diff --git a/lib/nixos.nix b/lib/nixos.nix index de4c686..5097908 100644 --- a/lib/nixos.nix +++ b/lib/nixos.nix @@ -28,20 +28,26 @@ in rec { extraModules ? [ ], moduleArgs ? { }, nixosArgs ? { }, nixosSystem ? inputs.nixpkgs.lib.nixosSystem, buildPlatform ? null, - }: nixosSystem (nixosArgs // { + }: nixosSystem (nixosArgs // (let args = { #system = null; # (This actually does nothing more than setting »config.nixpkgs.system« (which is the same as »config.nixpkgs.buildPlatform.system«) and can be null/unset here.) modules = (nixosArgs.modules or [ ]) ++ [ { imports = [ # Anything specific to only this evaluation of the module tree should go here. - (let + (let # mainModule module = if (builtins.isPath mainModule) || (builtins.isString mainModule) then (importWrapped inputs mainModule).module else mainModule; - bindName = func: lib.setFunctionArgs (args: func (args // { inherit name; })) (builtins.removeAttrs (lib.functionArgs func) [ "name" ]); - bindToModule = module: if lib.isFunction module then bindName module else if module?imports then module // { imports = map bindToModule module.imports; } else module; - in bindToModule module) # ensure that in the main module, the "name" parameter is available during the import stage already + # ensure that in the main module, the "name" parameter is available during the import stage already: + bindName2function = func: lib.setFunctionArgs (args: func (args // { inherit name; })) (builtins.removeAttrs (lib.functionArgs func) [ "name" ]); + bindName2module = module: if lib.isFunction module then bindName2function module else if module?imports then module // { imports = map bindName2module module.imports; } else module; + in bindName2module module) { _module.args.name = lib.mkOverride 0 name; } # (specialisations can somehow end up with the name »configuration«, which is very incorrect) { networking.hostName = name; } + # containers may or may not want to inherit extraModules (but it is easy to add), but there is no reason not to inherit specialArgs: + { options.containers = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule (_: { config = { + specialArgs = builtins.mapAttrs (_: lib.mkDefault) args.specialArgs; # imports = args.extraModules; + }; })); }; } + # `specializations` use `noUserModules`, which inherit specialArgs and extraModules, or `extendModules` inherits all modules and arguments. VM-variants also use `extendModules`. ]; _file = "${dirname}/nixos.nix#modules"; } ]; - extraModules = (nixosArgs.extraModules or [ ]) ++ modules ++ extraModules ++ [ { imports = [ (args: { + extraModules = (nixosArgs.extraModules or [ ]) ++ modules ++ extraModules ++ [ { imports = [ (_: { # 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; }; }); }«.) @@ -57,7 +63,7 @@ in rec { 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.) - }); + }; in args)); # Given either a list (or attr set) of »files« (paths to ».nix« or ».nix.md« files for dirs with »default.nix« files in them) or a »dir« path (and optionally a list of base names to »exclude« from it), this builds the NixOS configuration for each host (per file) in the context of all configs provided. # If »files« is an attr set, exactly one host with the attribute's name as hostname is built for each attribute. Otherwise the default is to build for one host per configuration file, named as the file name without extension or the sub-directory name. Setting »${preface'}.instances« can override this to build the same configuration for those multiple names instead (the specific »name« is passed as additional »moduleArgs« to the modules and can thus be used to adjust the config per instance). @@ -77,11 +83,14 @@ in rec { in (mapMergeUnique (name: { "${name}" = let preface = getPreface inputs (moduleArgs // { inherit preface; }) mainModule name; # (call again, with name) in { inherit preface; } // (mkNixosConfiguration (let systemArgs = ( - builtins.removeAttrs args [ "files" "dir" "exclude" ] + builtins.removeAttrs args [ "files" "dir" "exclude" "prefix" ] ) // { 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) + nixosArgs = (args.nixosArgs or { }) // { + specialArgs = (args.nixosArgs.specialArgs or { }) // { inherit preface; }; # make this available early, and only for the main evaluation (+specialisations +containers) + prefix = (args.nixosArgs.prefix or [ ]) ++ ((args.prefix or (_: [ ])) name); + }; 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; }); @@ -123,6 +132,8 @@ in rec { moduleArgs ? { }, # The »nixosSystem« function defined in »/flake.nix«, or equivalent. nixosSystem ? inputs.nixpkgs.lib.nixosSystem, + # Attribute path labels to prepend to option names/paths. Useful for debugging when building multiple systems at once. + prefix ? (name: [ "[${if renameOutputs == false then name else renameOutputs name}]" ]), # If provided, this will be set as »config.nixpkgs.buildPlatform« for all hosts, which in turn enables cross-compilation for all hosts whose »config.nixpkgs.hostPlatform« (the architecture they will run on) does not expand to the same value. Without this, building for other platforms may still work (slowly) if »boot.binfmt.emulatedSystems« on the building system is configured for the respective target(s). buildPlatform ? null, ## The platforms for which the setup scripts (installation & maintenance/debugging) will be defined. Should include the ».buildPlatform« and/or the target system's »config.nixpkgs.hostPlatform«. @@ -134,7 +145,7 @@ in rec { ... }: let getName = if renameOutputs == false then (name: name) else renameOutputs; otherArgs = (builtins.removeAttrs args [ "hosts" "moduleInputs" "overlayInputs" "renameOutputs" "asDefaultPackage" ]) // { - inherit inputs modules overlays moduleArgs nixosSystem buildPlatform extraModules; + inherit inputs modules overlays moduleArgs nixosSystem buildPlatform extraModules prefix; nixosArgs = (args.nixosArgs or { }) // { modules = (args.nixosArgs.modules or [ ]) ++ [ { imports = [ (args: { ${installer}.outputName = getName args.config._module.args.name; }) ]; _file = "${dirname}/nixos.nix#mkSystemsFlake-extraModule"; } ]; }; diff --git a/lib/setup-scripts/add-key.sh b/lib/setup-scripts/add-key.sh index c124d94..2ef3a29 100644 --- a/lib/setup-scripts/add-key.sh +++ b/lib/setup-scripts/add-key.sh @@ -52,8 +52,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 "for the user account »$user« (as component for key »(@{config.networking.hostName}:)$usage«)") - if [[ ! $password ]] ; then \exit 1 ; fi + password=$( prompt-new-password "for the user account »$user« (as component for key »(@{config.networking.hostName}:)$usage«)" ) + if [[ ! $password ]] ; then \exit 1 ; fi ; userPasswords[$user]=$password # TODO: won't propagate fi { cat "$keystore"/home/"$user".key && cat <<<"$password" ; } | sha256sum | head -c 64 )} @@ -66,8 +66,8 @@ 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 "for the user account »$user« (as YubiKey challenge for key »:$usage«)") - if [[ ! $password ]] ; then \exit 1 ; fi + password=$( prompt-new-password "for the user account »$user« (as YubiKey challenge for key »:$usage«)" ) + if [[ ! $password ]] ; then \exit 1 ; fi ; userPasswords[$user]=$password # TODO: won't propagate fi gen-key-yubikey-challenge "$usage" "$serial:$slot:home-$user=$password" true "»${user}«'s password (to create key »:${usage}«)" )} diff --git a/lib/setup-scripts/disk.sh b/lib/setup-scripts/disk.sh index 49cb1e6..2da0095 100644 --- a/lib/setup-scripts/disk.sh +++ b/lib/setup-scripts/disk.sh @@ -193,8 +193,8 @@ function format-partitions { 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[@]}" 1>&2 ; \return 1 ; fi else continue ; fi - eval 'declare -a formatArgs='"${fs[formatArgs]}" - ( PATH=@{native.e2fsprogs}/bin:@{native.f2fs-tools}/bin:@{native.xfsprogs}/bin:@{native.dosfstools}/bin:$PATH ; ${_set_x:-:} ; mkfs."${fs[fsType]}" "${formatArgs[@]}" "${fs[device]}" >$beLoud 2>$beSilent ) || [[ $options == *,nofail,* ]] || return + eval 'declare -a formatArgs='"${fs[formatArgs]}" ; eval 'declare -a options='"${fs[options]}" + ( PATH=@{native.e2fsprogs}/bin:@{native.f2fs-tools}/bin:@{native.xfsprogs}/bin:@{native.dosfstools}/bin:$PATH ; ${_set_x:-:} ; mkfs."${fs[fsType]}" "${formatArgs[@]}" "${fs[device]}" >$beLoud 2>$beSilent ) || [[ ' '${options[@]}' ' == *' 'nofail' '* ]] || return @{native.parted}/bin/partprobe "${fs[device]}" || true done for swapDev in "@{config.swapDevices!catAttrs.device[@]}" ; do diff --git a/lib/setup-scripts/install.sh b/lib/setup-scripts/install.sh index 4867aec..07bea8e 100644 --- a/lib/setup-scripts/install.sh +++ b/lib/setup-scripts/install.sh @@ -42,6 +42,8 @@ declare-flag install-system no-vm "" "Never perform the installation in a VM. Fa ## Does some argument validation, performs some sanity checks, includes a hack to make installation work when nix isn't installed for root, and runs the installation in qemu (if requested). function prepare-installer { # 1: diskPaths + run-hook-script 'Prepare Installer' @{config.installer.commands.prepareInstaller!writeText.prepareInstallerCommands} || exit + if [[ ! ${args[disks]:-} ]] ; then args[disks]=${1:?"The disks flag or the first positional argument must specify the path(s) to the disk(s) and/or image file(s) to install to"} ; shift ; fi umask g-w,o-w # Ensure that files created without explicit permissions are not writable for group and other. diff --git a/lib/setup-scripts/keys.sh b/lib/setup-scripts/keys.sh index c5963ec..d494baa 100644 --- a/lib/setup-scripts/keys.sh +++ b/lib/setup-scripts/keys.sh @@ -7,10 +7,7 @@ function prompt-for-user-passwords { # (void) userPasswords[$user]=@{config.users.users!catAttrSets.password[$user]} done local user ; for user in "@{!config.users.users!catAttrSets.hashedPasswordFile[@]}" "@{!config.users.users!catAttrSets.passwordFile[@]}" ; do - for attempt in 2 3 x ; do - if userPasswords[$user]=$(prompt-new-password "for the user account »$user«") ; then break ; fi - if [[ $attempt == x ]] ; then \return 1 ; fi ; echo "Retrying ($attempt/3):" - done + prompt-new-password-thrice "for the user account »$user«" done } diff --git a/lib/setup-scripts/maintenance.sh b/lib/setup-scripts/maintenance.sh index 0bdf07b..dcf37fa 100644 --- a/lib/setup-scripts/maintenance.sh +++ b/lib/setup-scripts/maintenance.sh @@ -68,7 +68,7 @@ Example 2 (connect many VMs, unprivileged): $ nix shell nixpkgs#vde2 --command vde_switch -sock /tmp/vm-net $ ... --nic=vde,sock=/tmp/vm-net # multiple times" declare-flag run-qemu no-serial "" "Do not connect the calling terminal to a serial adapter the guest can log to and open a terminal on the guests serial, as would be the default if the guests logs to ttyS0." -declare-flag run-qemu share "decls" "Host dirs to make available as network shares for the guest, as space separated list of »name:host-path,options. E.g. »--share='foo:/home/user/foo,readonly=on bar:/tmp/bar«. In the VM hte share can be mounted with: »$ mount -t 9p -o trans=virtio -o version=9p2000.L -o msize=4194304 -o ro foo /foo«." +declare-flag run-qemu share "decls" "Host dirs to make available as network shares for the guest, as space separated list of »name:host-path,options«. E.g. »--share='foo:/home/user/foo,readonly=on bar:/tmp/bar«. In the VM the share can be mounted with: »$ mount -t 9p -o trans=virtio -o version=9p2000.L -o msize=4194304 -o ro foo /foo«." declare-flag run-qemu virtio-blk "" "Pass the system's disks/images as virtio disks, instead of using AHCI+IDE. Default iff »boot.initrd.availableKernelModules« includes »virtio_blk« (because it requires that driver)." function run-qemu { if [[ ${args[install]:-} && ! ${argv[0]:-} ]] ; then argv[0]=/tmp/nixos-vm/@{config.installer.outputName:-@{config.system.name}}/ ; fi @@ -158,7 +158,7 @@ function run-qemu { if [[ ! -e $disk ]] ; then args[install]=always ; fi done ; fi if [[ ${args[install]:-} == always ]] && [[ ! ${args[dry-run]:-} ]] ; then ( - if [[ ! ${args[trace]:-} ]] && [[! ${args[debug]:-} ]] ; then args[quiet]=1 ; fi + if [[ ! ${args[trace]:-} ]] && [[ ! ${args[debug]:-} ]] ; then args[quiet]=1 ; fi args[no-inspect]=1 ; install-system "$diskImages" || exit ) || return ; fi @@ -176,7 +176,7 @@ declare-flag run-qemu,install-system,'*' vm-mem "num" "VM RAM in MiB (» declare-flag run-qemu,install-system,'*' vm-smp "num" "Number of guest CPU cores." declare-flag run-qemu,install-system,'*' vm-usb-port "path" "A physical USB port (or hub) to pass to the guest (e.g. a YubiKey for unlocking). Specified as »-«, where bus and port refer to the physical USB port »/sys/bus/usb/devices/-« (see »lsusb -tvv«). E.g.: »--vm-usb-port=3-1.1.1.4«." function apply-vm-args { - qemu+=( -m ${args[vm-mem]:-2048} ) + qemu+=( -m ${args[vm-mem]:-4096} ) if [[ ${args[vm-smp]:-} ]] ; then qemu+=( -smp ${args[vm-smp]} ) ; fi if [[ ${args[vm-usb-port]:-} ]] ; then local decl ; for decl in ${args[vm-usb-port]//:/ } ; do diff --git a/lib/setup-scripts/utils.sh b/lib/setup-scripts/utils.sh index 5ca2ffb..c397a0f 100644 --- a/lib/setup-scripts/utils.sh +++ b/lib/setup-scripts/utils.sh @@ -45,6 +45,12 @@ function prompt-new-password {( set -u # 1: usage if [[ "$password1" != "$password2" ]] ; then printf 'Passwords mismatch.\n' 1>&2 ; \exit 1 ; fi printf %s "$password1" || exit )} +function prompt-new-password-thrice { + local attempt ; for attempt in 2 3 x ; do + if userPasswords[$user]=$( prompt-new-password "$@" ) ; then break ; fi + if [[ $attempt == x ]] ; then \return 1 ; fi ; echo "Retrying ($attempt/3):" + done +} ## If »secretFile« does not exist, interactively prompts up to three times for the secret to be stored in that file. declare-flag '*' no-optional-prompts "" "Skip prompting for (and thus saving) secret marked as optional." @@ -67,16 +73,15 @@ function prompt-secret-as {( set -u # 1: what, 2: secretFile, 3?: owner[:[group] declare-flag install-system inspectScripts "" "When running installation hooks (»...*Commands« composed as Nix strings) print out and pause before each command. This works ... semi-well." ## Runs an installer hook script, optionally stepping through the script. -function run-hook-script {( # 1: title, 2: scriptPath - trap - EXIT # start with empty traps for sub-shell +function run-hook-script { # 1: title, 2: scriptPath if [[ ${args[inspectScripts]:-} && "$(cat "$2")" != $'' ]] ; then echo "Running $1 commands. For each command printed, press Enter to continue or Ctrl+C to abort the installation:" 1>&2 # (this does not help against intentionally malicious scripts, it's quite easy to trick this) BASH_PREV_COMMAND= ; set -o functrace ; trap 'if [[ $BASH_COMMAND != "$BASH_PREV_COMMAND" ]] ; then echo -n "> $BASH_COMMAND" >&2 ; read ; fi ; BASH_PREV_COMMAND=$BASH_COMMAND' debug fi - set -e # The called script snippets should not rely on this, but neither should this function rely on the scripts correctly exiting on errors. source "$2" -)} + set +o functrace ; trap - debug +} ## Lazily builds a nix derivation at run time, instead of when building the script. # When maybe-using packages that take long to build, instead of »at{some.package.out}«, use: »$( build-lazy at{some.package.drvPath!unsafeDiscardStringContext} out )« diff --git a/modules/installer.nix.md b/modules/installer.nix.md index 8d24647..2e8ce6c 100644 --- a/modules/installer.nix.md +++ b/modules/installer.nix.md @@ -36,6 +36,7 @@ in { Note that these commands are executed without any further sandboxing (i.e. when not using the VM installation mode, as root on the host). Partitions may be used via `/dev/disk/by-partlabel/`.${lib.optionalString mounted '' The target system is mounted at `$mnt`.''} ''; type = lib.types.lines; default = ""; }; in { + prepareInstaller = cmdOpt "early during preparation of the installer (right after CLI parsing)" false; postPartition = cmdOpt "after partitioning the disks" false; postFormat = cmdOpt "after formatting the partitions with filesystems" false; postMount = cmdOpt "after mounting all filesystems" true; @@ -47,7 +48,7 @@ in { type = lib.types.nullOr lib.types.str; default = null; }; build.scripts = lib.mkOption { - type = lib.types.functionTo lib.types.str; internal = true; readOnly = true; + type = lib.types.functionTo lib.types.str; internal = true; readOnly = true; default = context: "${lib.fun.substituteImplicit { # This replaces the `@{}` references in the scripts with normal bash variables that hold serializations of the Nix values they refer to. inherit pkgs; scripts = lib.sort (a: b: a.order < b.order) (lib.attrValues cfg.scripts); context = { inherit config options pkgs; inherit (moduleArgs) inputs; } // context; @@ -58,7 +59,7 @@ in { config = { ${installer} = { - scripts = lib.mapAttrs (name: path: lib.mkOptionDefault { inherit path; }) (lib.self.setup-scripts); + scripts = lib.mapAttrs (name: path: lib.mkOptionDefault { inherit path; order = 750; }) (lib.self.setup-scripts); }; }; diff --git a/modules/setup/temproot.nix.md b/modules/setup/temproot.nix.md index aa50546..073f611 100644 --- a/modules/setup/temproot.nix.md +++ b/modules/setup/temproot.nix.md @@ -206,13 +206,11 @@ in { }) (lib.mkIf cfg.persistenceFixes { # Cope with the consequences of having »/« (including »/{etc,var,root,...}«) cleared on every reboot. - environment.etc = { - # 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"; - }; + # SSHd host keys: + environment.etc = lib.fun.mapMerge ({ path, ... }: if lib.hasPrefix "/etc/" path then let rel = lib.removePrefix "/etc/" path; in { + "${rel}".source = lib.mkDefault "/${keep}/etc/${rel}"; + "${rel}.pub".source = lib.mkDefault "/${keep}/etc/${rel}.pub"; + } else { }) (config.services.openssh.hostKeys); systemd.tmpfiles.rules = [ # keep in mind: this does not get applied super early ... # »/root/.nix-channels« is already being restored. diff --git a/modules/vm-exec.nix.md b/modules/vm-exec.nix.md index 7eddbfb..6e0e52a 100644 --- a/modules/vm-exec.nix.md +++ b/modules/vm-exec.nix.md @@ -156,7 +156,7 @@ in let hostModule = { fileSystems = lib.mkVMOverride { "/nix/var/nix/db.lower" = { fsType = "9p"; device = "nix-var-nix-db"; neededForBoot = true; - options = [ "trans=virtio" "version=9p2000.L" "msize=4194304" "ro" ]; + options = [ "trans=virtio" "version=9p2000.L" "msize=4194304" "ro" ]; }; "/nix/store".options = lib.mkAfter [ "ro" "msize=4194304" ]; "/nix/store".mountPoint = lib.mkForce "/nix/store.lower";