diff options
Diffstat (limited to 'nixos/modules/system')
17 files changed, 337 insertions, 174 deletions
diff --git a/nixos/modules/system/activation/bootspec.nix b/nixos/modules/system/activation/bootspec.nix index 9e1fa309d5d..98c234bc340 100644 --- a/nixos/modules/system/activation/bootspec.nix +++ b/nixos/modules/system/activation/bootspec.nix @@ -79,7 +79,7 @@ in // { default = true; internal = true; }; enableValidation = lib.mkEnableOption (lib.mdDoc ''the validation of bootspec documents for each build. This will introduce Go in the build-time closure as we are relying on [Cuelang](https://cuelang.org/) for schema validation. - Enable this option if you want to ascertain that your documents are correct. + Enable this option if you want to ascertain that your documents are correct '' ); diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl index 8bd450d7343..b3ff3ac0abf 100755 --- a/nixos/modules/system/activation/switch-to-configuration.pl +++ b/nixos/modules/system/activation/switch-to-configuration.pl @@ -74,7 +74,7 @@ if ("@localeArchive@" ne "") { if (!defined($action) || ($action ne "switch" && $action ne "boot" && $action ne "test" && $action ne "dry-activate")) { print STDERR <<"EOF"; -Usage: $0 [switch|boot|test] +Usage: $0 [switch|boot|test|dry-activate] switch: make the configuration the boot default and activate now boot: make the configuration the boot default @@ -599,7 +599,9 @@ while (my ($unit, $state) = each(%{$active_cur})) { $units_to_start{$unit} = 1; record_unit($start_list_file, $unit); # Don't spam the user with target units that always get started. - $units_to_filter{$unit} = 1; + if (($ENV{"STC_DISPLAY_ALL_UNITS"} // "") ne "1") { + $units_to_filter{$unit} = 1; + } } } @@ -661,10 +663,20 @@ foreach my $mount_point (keys(%{$cur_fss})) { # Filesystem entry disappeared, so unmount it. $units_to_stop{$unit} = 1; } elsif ($cur->{fsType} ne $new->{fsType} || $cur->{device} ne $new->{device}) { - # Filesystem type or device changed, so unmount and mount it. - $units_to_stop{$unit} = 1; - $units_to_start{$unit} = 1; - record_unit($start_list_file, $unit); + if ($mount_point eq '/' or $mount_point eq '/nix') { + if ($cur->{options} ne $new->{options}) { + # Mount options changed, so remount it. + $units_to_reload{$unit} = 1; + record_unit($reload_list_file, $unit); + } else { + # Don't unmount / or /nix if the device changed + $units_to_skip{$unit} = 1; + } + } else { + # Filesystem type or device changed, so unmount and mount it. + $units_to_restart{$unit} = 1; + record_unit($restart_list_file, $unit); + } } elsif ($cur->{options} ne $new->{options}) { # Mount options changes, so remount it. $units_to_reload{$unit} = 1; diff --git a/nixos/modules/system/boot/grow-partition.nix b/nixos/modules/system/boot/grow-partition.nix index a2764187a53..897602f9826 100644 --- a/nixos/modules/system/boot/grow-partition.nix +++ b/nixos/modules/system/boot/grow-partition.nix @@ -12,33 +12,32 @@ with lib; ]; options = { - boot.growPartition = mkEnableOption (lib.mdDoc "grow the root partition on boot"); + boot.growPartition = mkEnableOption (lib.mdDoc "growing the root partition on boot"); }; config = mkIf config.boot.growPartition { - - assertions = [{ - assertion = !config.boot.initrd.systemd.enable; - message = "systemd stage 1 does not support 'boot.growPartition' yet."; - }]; - - boot.initrd.extraUtilsCommands = '' - copy_bin_and_libs ${pkgs.gawk}/bin/gawk - copy_bin_and_libs ${pkgs.gnused}/bin/sed - copy_bin_and_libs ${pkgs.util-linux}/sbin/sfdisk - copy_bin_and_libs ${pkgs.util-linux}/sbin/lsblk - - substitute "${pkgs.cloud-utils.guest}/bin/.growpart-wrapped" "$out/bin/growpart" \ - --replace "${pkgs.bash}/bin/sh" "/bin/sh" \ - --replace "awk" "gawk" \ - --replace "sed" "gnused" - - ln -s sed $out/bin/gnused - ''; - - boot.initrd.postDeviceCommands = '' - rootDevice="${config.fileSystems."/".device}" - if waitDevice "$rootDevice"; then + assertions = [ + { + assertion = !config.boot.initrd.systemd.repart.enable && !config.systemd.repart.enable; + message = "systemd-repart already grows the root partition and thus you should not use boot.growPartition"; + } + ]; + systemd.services.growpart = { + wantedBy = [ "-.mount" ]; + after = [ "-.mount" ]; + before = [ "systemd-growfs-root.service" ]; + conflicts = [ "shutdown.target" ]; + unitConfig.DefaultDependencies = false; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + TimeoutSec = "infinity"; + # growpart returns 1 if the partition is already grown + SuccessExitStatus = "0 1"; + }; + + script = '' + rootDevice="${config.fileSystems."/".device}" rootDevice="$(readlink -f "$rootDevice")" parentDevice="$rootDevice" while [ "''${parentDevice%[0-9]}" != "''${parentDevice}" ]; do @@ -48,11 +47,8 @@ with lib; if [ "''${parentDevice%[0-9]p}" != "''${parentDevice}" ] && [ -b "''${parentDevice%p}" ]; then parentDevice="''${parentDevice%p}" fi - TMPDIR=/run sh $(type -P growpart) "$parentDevice" "$partNum" - udevadm settle - fi - ''; - + "${pkgs.cloud-utils.guest}/bin/growpart" "$parentDevice" "$partNum" + ''; + }; }; - } diff --git a/nixos/modules/system/boot/initrd-network.nix b/nixos/modules/system/boot/initrd-network.nix index 1d95742face..5bf38b6fa20 100644 --- a/nixos/modules/system/boot/initrd-network.nix +++ b/nixos/modules/system/boot/initrd-network.nix @@ -80,7 +80,7 @@ in }; boot.initrd.network.udhcpc.enable = mkOption { - default = config.networking.useDHCP; + default = config.networking.useDHCP && !config.boot.initrd.systemd.enable; defaultText = "networking.useDHCP"; type = types.bool; description = lib.mdDoc '' diff --git a/nixos/modules/system/boot/loader/external/external.nix b/nixos/modules/system/boot/loader/external/external.nix index 926cbd2b4b3..78982356a9e 100644 --- a/nixos/modules/system/boot/loader/external/external.nix +++ b/nixos/modules/system/boot/loader/external/external.nix @@ -12,7 +12,7 @@ in }; options.boot.loader.external = { - enable = mkEnableOption (lib.mdDoc "use an external tool to install your bootloader"); + enable = mkEnableOption (lib.mdDoc "using an external tool to install your bootloader"); installHook = mkOption { type = with types; path; diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py index a040518a5a5..310584e398b 100755..100644 --- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py +++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py @@ -1,27 +1,25 @@ #! @python3@/bin/python3 -B import argparse -import shutil -import os -import sys -import errno -import subprocess -import glob -import tempfile -import errno -import warnings import ctypes -libc = ctypes.CDLL("libc.so.6") -import re import datetime +import errno import glob +import os import os.path -from typing import NamedTuple, List, Optional -from packaging import version +import re +import shutil +import subprocess +import sys +import warnings +from typing import NamedTuple + + +libc = ctypes.CDLL("libc.so.6") class SystemIdentifier(NamedTuple): - profile: Optional[str] + profile: str | None generation: int - specialisation: Optional[str] + specialisation: str | None def copy_if_not_exists(source: str, dest: str) -> None: @@ -29,13 +27,13 @@ def copy_if_not_exists(source: str, dest: str) -> None: shutil.copyfile(source, dest) -def generation_dir(profile: Optional[str], generation: int) -> str: +def generation_dir(profile: str | None, generation: int) -> str: if profile: return "/nix/var/nix/profiles/system-profiles/%s-%d-link" % (profile, generation) else: return "/nix/var/nix/profiles/system-%d-link" % (generation) -def system_dir(profile: Optional[str], generation: int, specialisation: Optional[str]) -> str: +def system_dir(profile: str | None, generation: int, specialisation: str | None) -> str: d = generation_dir(profile, generation) if specialisation: return os.path.join(d, "specialisation", specialisation) @@ -49,7 +47,7 @@ initrd {initrd} options {kernel_params} """ -def generation_conf_filename(profile: Optional[str], generation: int, specialisation: Optional[str]) -> str: +def generation_conf_filename(profile: str | None, generation: int, specialisation: str | None) -> str: pieces = [ "nixos", profile or None, @@ -60,22 +58,24 @@ def generation_conf_filename(profile: Optional[str], generation: int, specialisa return "-".join(p for p in pieces if p) + ".conf" -def write_loader_conf(profile: Optional[str], generation: int, specialisation: Optional[str]) -> None: +def write_loader_conf(profile: str | None, generation: int, specialisation: str | None) -> None: with open("@efiSysMountPoint@/loader/loader.conf.tmp", 'w') as f: if "@timeout@" != "": f.write("timeout @timeout@\n") f.write("default %s\n" % generation_conf_filename(profile, generation, specialisation)) if not @editor@: - f.write("editor 0\n"); - f.write("console-mode @consoleMode@\n"); + f.write("editor 0\n") + f.write("console-mode @consoleMode@\n") + f.flush() + os.fsync(f.fileno()) os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf") -def profile_path(profile: Optional[str], generation: int, specialisation: Optional[str], name: str) -> str: +def profile_path(profile: str | None, generation: int, specialisation: str | None, name: str) -> str: return os.path.realpath("%s/%s" % (system_dir(profile, generation, specialisation), name)) -def copy_from_profile(profile: Optional[str], generation: int, specialisation: Optional[str], name: str, dry_run: bool = False) -> str: +def copy_from_profile(profile: str | None, generation: int, specialisation: str | None, name: str, dry_run: bool = False) -> str: store_file_path = profile_path(profile, generation, specialisation, name) suffix = os.path.basename(store_file_path) store_dir = os.path.basename(os.path.dirname(store_file_path)) @@ -85,7 +85,7 @@ def copy_from_profile(profile: Optional[str], generation: int, specialisation: O return efi_file_path -def describe_generation(profile: Optional[str], generation: int, specialisation: Optional[str]) -> str: +def describe_generation(profile: str | None, generation: int, specialisation: str | None) -> str: try: with open(profile_path(profile, generation, specialisation, "nixos-version")) as f: nixos_version = f.read() @@ -106,7 +106,7 @@ def describe_generation(profile: Optional[str], generation: int, specialisation: return description -def write_entry(profile: Optional[str], generation: int, specialisation: Optional[str], +def write_entry(profile: str | None, generation: int, specialisation: str | None, machine_id: str, current: bool) -> None: kernel = copy_from_profile(profile, generation, specialisation, "kernel") initrd = copy_from_profile(profile, generation, specialisation, "initrd") @@ -145,18 +145,12 @@ def write_entry(profile: Optional[str], generation: int, specialisation: Optiona description=describe_generation(profile, generation, specialisation))) if machine_id is not None: f.write("machine-id %s\n" % machine_id) + f.flush() + os.fsync(f.fileno()) os.rename(tmp_path, entry_file) -def mkdir_p(path: str) -> None: - try: - os.makedirs(path) - except OSError as e: - if e.errno != errno.EEXIST or not os.path.isdir(path): - raise - - -def get_generations(profile: Optional[str] = None) -> List[SystemIdentifier]: +def get_generations(profile: str | None = None) -> list[SystemIdentifier]: gen_list = subprocess.check_output([ "@nix@/bin/nix-env", "--list-generations", @@ -179,7 +173,7 @@ def get_generations(profile: Optional[str] = None) -> List[SystemIdentifier]: return configurations[-configurationLimit:] -def get_specialisations(profile: Optional[str], generation: int, _: Optional[str]) -> List[SystemIdentifier]: +def get_specialisations(profile: str | None, generation: int, _: str | None) -> list[SystemIdentifier]: specialisations_dir = os.path.join( system_dir(profile, generation, None), "specialisation") if not os.path.exists(specialisations_dir): @@ -187,9 +181,9 @@ def get_specialisations(profile: Optional[str], generation: int, _: Optional[str return [SystemIdentifier(profile, generation, spec) for spec in os.listdir(specialisations_dir)] -def remove_old_entries(gens: List[SystemIdentifier]) -> None: - rex_profile = re.compile("^@efiSysMountPoint@/loader/entries/nixos-(.*)-generation-.*\.conf$") - rex_generation = re.compile("^@efiSysMountPoint@/loader/entries/nixos.*-generation-([0-9]+)(-specialisation-.*)?\.conf$") +def remove_old_entries(gens: list[SystemIdentifier]) -> None: + rex_profile = re.compile(r"^@efiSysMountPoint@/loader/entries/nixos-(.*)-generation-.*\.conf$") + rex_generation = re.compile(r"^@efiSysMountPoint@/loader/entries/nixos.*-generation-([0-9]+)(-specialisation-.*)?\.conf$") known_paths = [] for gen in gens: known_paths.append(copy_from_profile(*gen, "kernel", True)) @@ -210,7 +204,7 @@ def remove_old_entries(gens: List[SystemIdentifier]) -> None: os.unlink(path) -def get_profiles() -> List[str]: +def get_profiles() -> list[str]: if os.path.isdir("/nix/var/nix/profiles/system-profiles/"): return [x for x in os.listdir("/nix/var/nix/profiles/system-profiles/") @@ -218,11 +212,7 @@ def get_profiles() -> List[str]: else: return [] -def main() -> None: - parser = argparse.ArgumentParser(description='Update @distroName@-related systemd-boot files') - parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help='The default @distroName@ config to boot') - args = parser.parse_args() - +def install_bootloader(args: argparse.Namespace) -> None: try: with open("/etc/machine-id") as machine_file: machine_id = machine_file.readlines()[0] @@ -273,21 +263,15 @@ def main() -> None: if available_match is None: raise Exception("could not determine systemd-boot version") - installed_version = version.parse(installed_match.group(1)) - available_version = version.parse(available_match.group(1)) + installed_version = installed_match.group(1) + available_version = available_match.group(1) - # systemd 252 has a regression that leaves some machines unbootable, so we skip that update. - # The fix is in 252.2 - # See https://github.com/systemd/systemd/issues/25363 and https://github.com/NixOS/nixpkgs/pull/201558#issuecomment-1348603263 if installed_version < available_version: - if version.parse('252') <= available_version < version.parse('252.2'): - print("skipping systemd-boot update to %s because of known regression" % available_version) - else: - print("updating systemd-boot from %s to %s" % (installed_version, available_version)) - subprocess.check_call(["@systemd@/bin/bootctl", "--esp-path=@efiSysMountPoint@"] + bootctl_flags + ["update"]) + print("updating systemd-boot from %s to %s" % (installed_version, available_version)) + subprocess.check_call(["@systemd@/bin/bootctl", "--esp-path=@efiSysMountPoint@", "update"]) - mkdir_p("@efiSysMountPoint@/efi/nixos") - mkdir_p("@efiSysMountPoint@/loader/entries") + os.makedirs("@efiSysMountPoint@/efi/nixos", exist_ok=True) + os.makedirs("@efiSysMountPoint@/loader/entries", exist_ok=True) gens = get_generations() for profile in get_profiles(): @@ -324,17 +308,26 @@ def main() -> None: os.rmdir(actual_root) os.rmdir(root) - mkdir_p("@efiSysMountPoint@/efi/nixos/.extra-files") + os.makedirs("@efiSysMountPoint@/efi/nixos/.extra-files", exist_ok=True) subprocess.check_call("@copyExtraFiles@") - # Since fat32 provides little recovery facilities after a crash, - # it can leave the system in an unbootable state, when a crash/outage - # happens shortly after an update. To decrease the likelihood of this - # event sync the efi filesystem after each update. - rc = libc.syncfs(os.open("@efiSysMountPoint@", os.O_RDONLY)) - if rc != 0: - print("could not sync @efiSysMountPoint@: {}".format(os.strerror(rc)), file=sys.stderr) + +def main() -> None: + parser = argparse.ArgumentParser(description='Update @distroName@-related systemd-boot files') + parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help='The default @distroName@ config to boot') + args = parser.parse_args() + + try: + install_bootloader(args) + finally: + # Since fat32 provides little recovery facilities after a crash, + # it can leave the system in an unbootable state, when a crash/outage + # happens shortly after an update. To decrease the likelihood of this + # event sync the efi filesystem after each update. + rc = libc.syncfs(os.open("@efiSysMountPoint@", os.O_RDONLY)) + if rc != 0: + print("could not sync @efiSysMountPoint@: {}".format(os.strerror(rc)), file=sys.stderr) if __name__ == '__main__': diff --git a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix index 1770f075943..6f0a62d0ea8 100644 --- a/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix +++ b/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix @@ -7,14 +7,12 @@ let efi = config.boot.loader.efi; - python3 = pkgs.python3.withPackages (ps: [ ps.packaging ]); - systemdBootBuilder = pkgs.substituteAll { src = ./systemd-boot-builder.py; isExecutable = true; - inherit python3; + inherit (pkgs) python3; systemd = config.systemd.package; @@ -52,7 +50,7 @@ let }; checkedSystemdBootBuilder = pkgs.runCommand "systemd-boot" { - nativeBuildInputs = [ pkgs.mypy python3 ]; + nativeBuildInputs = [ pkgs.mypy ]; } '' install -m755 ${systemdBootBuilder} $out mypy \ @@ -147,7 +145,7 @@ in { default = false; type = types.bool; description = lib.mdDoc '' - Make MemTest86+ available from the systemd-boot menu. MemTest86+ is a + Make Memtest86+ available from the systemd-boot menu. Memtest86+ is a program for testing memory. ''; }; @@ -191,7 +189,7 @@ in { default = {}; example = literalExpression '' { "memtest86.conf" = ''' - title MemTest86+ + title Memtest86+ efi /efi/memtest86/memtest.efi '''; } ''; @@ -285,7 +283,7 @@ in { boot.loader.systemd-boot.extraEntries = mkMerge [ (mkIf cfg.memtest86.enable { "${cfg.memtest86.entryFilename}" = '' - title MemTest86 + title Memtest86+ efi /efi/memtest86/memtest.efi ''; }) diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix index dc3fe163116..06c329e006b 100644 --- a/nixos/modules/system/boot/luksroot.nix +++ b/nixos/modules/system/boot/luksroot.nix @@ -351,6 +351,12 @@ let new_response="$(ykchalresp -${toString dev.yubikey.slot} -x $new_challenge 2>/dev/null)" + if [ -z "$new_response" ]; then + echo "Warning: Unable to generate new challenge response, current challenge persists!" + umount /crypt-storage + return + fi + if [ ! -z "$k_user" ]; then new_k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $new_iterations $new_response | rbtohex)" else diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index 87afc502325..b7ced5b0d34 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -83,7 +83,7 @@ let (assertByteFormat "BitsPerSecond") (assertValueOneOf "Duplex" ["half" "full"]) (assertValueOneOf "AutoNegotiation" boolValues) - (assertValueOneOf "WakeOnLan" ["phy" "unicast" "multicast" "broadcast" "arp" "magic" "secureon" "off"]) + (assertValuesSomeOfOr "WakeOnLan" ["phy" "unicast" "multicast" "broadcast" "arp" "magic" "secureon"] "off") (assertValueOneOf "Port" ["tp" "aui" "bnc" "mii" "fibre"]) (assertValueOneOf "ReceiveChecksumOffload" boolValues) (assertValueOneOf "TransmitChecksumOffload" boolValues) @@ -159,6 +159,7 @@ let "geneve" "l2tp" "macsec" + "wlan" "vrf" "vcan" "vxcan" @@ -468,6 +469,30 @@ let (assertMinimum "Table" 0) ]; + sectionWLAN = checkUnitConfig "WLAN" [ + (assertOnlyFields [ + "PhysicalDevice" # systemd supports both strings ("phy0") and indexes (0) here. + "Type" + "WDS" + ]) + # See https://github.com/systemd/systemd/blob/main/src/basic/linux/nl80211.h#L3382 + (assertValueOneOf "Type" [ + "ad-hoc" + "station" + "ap" + "ap-vlan" + "wds" + "monitor" + "mesh-point" + "p2p-client" + "p2p-go" + "p2p-device" + "ocb" + "nan" + ]) + (assertValueOneOf "WDS" boolValues) + ]; + sectionBatmanAdvanced = checkUnitConfig "BatmanAdvanced" [ (assertOnlyFields [ "GatewayMode" @@ -517,17 +542,24 @@ let (assertValueOneOf "Unmanaged" boolValues) (assertInt "Group") (assertRange "Group" 0 2147483647) - (assertValueOneOf "RequiredForOnline" (boolValues ++ [ - "missing" - "off" - "no-carrier" - "dormant" - "degraded-carrier" - "carrier" - "degraded" - "enslaved" - "routable" - ])) + (assertValueOneOf "RequiredForOnline" (boolValues ++ ( + let + # https://freedesktop.org/software/systemd/man/networkctl.html#missing + operationalStates = [ + "missing" + "off" + "no-carrier" + "dormant" + "degraded-carrier" + "carrier" + "degraded" + "enslaved" + "routable" + ]; + operationalStateRanges = concatLists (imap0 (i: min: map (max: "${min}:${max}") (drop i operationalStates)) operationalStates); + in + operationalStates ++ operationalStateRanges + ))) (assertValueOneOf "RequiredFamilyForOnline" [ "ipv4" "ipv6" @@ -799,6 +831,8 @@ let "UseAddress" "UseDNS" "UseNTP" + "UseHostname" + "UseDomains" "RouteMetric" "RapidCommit" "MUDURL" @@ -813,16 +847,20 @@ let "DUIDRawData" "IAID" "UseDelegatedPrefix" + "SendRelease" ]) (assertValueOneOf "UseAddress" boolValues) (assertValueOneOf "UseDNS" boolValues) (assertValueOneOf "UseNTP" boolValues) + (assertValueOneOf "UseHostname" boolValues) + (assertValueOneOf "UseDomains" (boolValues ++ ["route"])) (assertInt "RouteMetric") (assertValueOneOf "RapidCommit" boolValues) (assertValueOneOf "WithoutRA" ["no" "solicit" "information-request"]) (assertRange "SendOption" 1 65536) (assertInt "IAID") (assertValueOneOf "UseDelegatedPrefix" boolValues) + (assertValueOneOf "SendRelease" boolValues) ]; sectionDHCPPrefixDelegation = checkUnitConfig "DHCPPrefixDelegation" [ @@ -902,6 +940,9 @@ let "RelayTarget" "RelayAgentCircuitId" "RelayAgentRemoteId" + "BootServerAddress" + "BootServerName" + "BootFilename" ]) (assertInt "PoolOffset") (assertMinimum "PoolOffset" 0) @@ -945,10 +986,12 @@ let "Prefix" "PreferredLifetimeSec" "ValidLifetimeSec" + "Assign" "Token" ]) (assertValueOneOf "AddressAutoconfiguration" boolValues) (assertValueOneOf "OnLink" boolValues) + (assertValueOneOf "Assign" boolValues) ]; sectionIPv6RoutePrefix = checkUnitConfig "IPv6RoutePrefix" [ @@ -977,7 +1020,7 @@ let "MulticastToUnicast" "NeighborSuppression" "Learning" - "Hairpin" + "HairPin" "Isolated" "UseBPDU" "FastLeave" @@ -993,7 +1036,7 @@ let (assertValueOneOf "MulticastToUnicast" boolValues) (assertValueOneOf "NeighborSuppression" boolValues) (assertValueOneOf "Learning" boolValues) - (assertValueOneOf "Hairpin" boolValues) + (assertValueOneOf "HairPin" boolValues) (assertValueOneOf "Isolated" boolValues) (assertValueOneOf "UseBPDU" boolValues) (assertValueOneOf "FastLeave" boolValues) @@ -1761,6 +1804,16 @@ let ''; }; + wlanConfig = mkOption { + default = {}; + example = { PhysicalDevice = 0; Type = "station"; }; + type = types.addCheck (types.attrsOf unitOption) check.netdev.sectionWLAN; + description = lib.mdDoc '' + Each attribute in this set specifies an option in the `[WLAN]` section of the unit. + See {manpage}`systemd.netdev(5)` for details. + ''; + }; + batmanAdvancedConfig = mkOption { default = {}; example = { @@ -2706,9 +2759,12 @@ let description = lib.mdDoc '' Whether to consider the network online when any interface is online, as opposed to all of them. This is useful on portable machines with a wired and a wireless interface, for example. + + This is on by default if {option}`networking.useDHCP` is enabled. ''; type = types.bool; - default = false; + defaultText = "config.networking.useDHCP"; + default = config.networking.useDHCP; }; ignoredInterfaces = mkOption { @@ -2840,6 +2896,17 @@ let }) ]; + stage1Options = { + options.boot.initrd.systemd.network.networks = mkOption { + type = with types; attrsOf (submodule { + # Default in initrd is dhcp-on-stop, which is correct if flushBeforeStage2 = false + config = mkIf config.boot.initrd.network.flushBeforeStage2 { + networkConfig.KeepConfiguration = mkDefault false; + }; + }); + }; + }; + stage1Config = let cfg = config.boot.initrd.systemd.network; in mkMerge [ @@ -2858,8 +2925,6 @@ let (mkIf cfg.enable { - systemd.package = mkDefault pkgs.systemdStage1Network; - # For networkctl systemd.dbus.enable = mkDefault true; @@ -2903,45 +2968,14 @@ let ]; kernelModules = [ "af_packet" ]; - systemd.services.nixos-flush-networkd = mkIf config.boot.initrd.network.flushBeforeStage2 { - description = "Flush Network Configuration"; - wantedBy = ["initrd.target"]; - after = ["systemd-networkd.service" "dbus.socket" "dbus.service"]; - before = ["shutdown.target" "initrd-switch-root.target"]; - conflicts = ["shutdown.target" "initrd-switch-root.target"]; - unitConfig.DefaultDependencies = false; - serviceConfig = { - # This service does nothing when starting, but brings down - # interfaces when switching root. This is the easiest way to - # ensure proper ordering while stopping. See systemd.unit(5) - # section on Before= and After=. The important part is that - # we are stopped before units we need, like dbus.service, - # and that we are stopped before starting units like - # initrd-switch-root.target - Type = "oneshot"; - RemainAfterExit = true; - ExecStart = "/bin/true"; - }; - # systemd-networkd doesn't bring down interfaces on its own - # when it exits (see: systemd-networkd(8)), so we have to do - # it ourselves. The networkctl command doesn't have a way to - # bring all interfaces down, so we have to iterate over the - # list and filter out unmanaged interfaces to bring them down - # individually. - preStop = '' - networkctl list --full --no-legend | while read _idx link _type _operational setup _; do - [ "$setup" = unmanaged ] && continue - networkctl down "$link" - done - ''; - }; - }) ]; in { + imports = [ stage1Options ]; + options = { systemd.network = commonOptions true; boot.initrd.systemd.network = commonOptions "shallow"; @@ -2951,10 +2985,10 @@ in stage2Config (mkIf config.boot.initrd.systemd.enable { assertions = [{ - assertion = config.boot.initrd.network.udhcpc.extraArgs == []; + assertion = !config.boot.initrd.network.udhcpc.enable && config.boot.initrd.network.udhcpc.extraArgs == []; message = '' - boot.initrd.network.udhcpc.extraArgs is not supported when - boot.initrd.systemd.enable is enabled + systemd stage 1 networking does not support 'boot.initrd.network.udhcpc'. Configure + DHCP with 'networking.*' options or with 'boot.initrd.systemd.network' options. ''; }]; diff --git a/nixos/modules/system/boot/resolved.nix b/nixos/modules/system/boot/resolved.nix index 4e7201833db..b898a631796 100644 --- a/nixos/modules/system/boot/resolved.nix +++ b/nixos/modules/system/boot/resolved.nix @@ -66,7 +66,7 @@ in }; services.resolved.dnssec = mkOption { - default = "allow-downgrade"; + default = "false"; example = "true"; type = types.enum [ "true" "allow-downgrade" "false" ]; description = lib.mdDoc '' @@ -85,6 +85,12 @@ in synthesizing a DNS response that suggests DNSSEC was not supported. - `"false"`: DNS lookups are not DNSSEC validated. + + At the time of September 2023, systemd upstream advise + to disable DNSSEC by default as the current code + is not robust enough to deal with "in the wild" non-compliant + servers, which will usually give you a broken bad experience + in addition of insecure. ''; }; diff --git a/nixos/modules/system/boot/stage-1.nix b/nixos/modules/system/boot/stage-1.nix index 7aaa3f85bfe..a3551f68dbe 100644 --- a/nixos/modules/system/boot/stage-1.nix +++ b/nixos/modules/system/boot/stage-1.nix @@ -123,7 +123,7 @@ let # ZFS properties such as `setuid=off` and `exec=off` (unless manually # duplicated in `fileSystems.*.options`, defeating "zfsutil"'s purpose). copy_bin_and_libs ${lib.getOutput "mount" pkgs.util-linux}/bin/mount - copy_bin_and_libs ${pkgs.zfs}/bin/mount.zfs + copy_bin_and_libs ${config.boot.zfs.package}/bin/mount.zfs ''} # Copy some util-linux stuff. diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index ac55461107f..68a8c1f37ed 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -48,6 +48,7 @@ let "rescue.service" # Udev. + "systemd-tmpfiles-setup-dev-early.service" "systemd-udevd-control.socket" "systemd-udevd-kernel.socket" "systemd-udevd.service" @@ -395,7 +396,9 @@ in description = lib.mdDoc '' The amount of time which can elapse after a reboot has been triggered before a watchdog hardware device will automatically reboot the system. - Valid time units include "ms", "s", "min", "h", "d", and "w". + Valid time units include "ms", "s", "min", "h", "d", and "w". If left + `null`, systemd will use its default of `10min`; see also {command}`man + 5 systemd-system.conf`. ''; }; @@ -572,7 +575,7 @@ in system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled [ "DEVTMPFS" "CGROUPS" "INOTIFY_USER" "SIGNALFD" "TIMERFD" "EPOLL" "NET" "SYSFS" "PROC_FS" "FHANDLE" "CRYPTO_USER_API_HASH" "CRYPTO_HMAC" - "CRYPTO_SHA256" "DMIID" "AUTOFS4_FS" "TMPFS_POSIX_ACL" + "CRYPTO_SHA256" "DMIID" "AUTOFS_FS" "TMPFS_POSIX_ACL" "TMPFS_XATTR" "SECCOMP" ]; diff --git a/nixos/modules/system/boot/systemd/homed.nix b/nixos/modules/system/boot/systemd/homed.nix index 403d1690124..b216820c0c0 100644 --- a/nixos/modules/system/boot/systemd/homed.nix +++ b/nixos/modules/system/boot/systemd/homed.nix @@ -5,7 +5,7 @@ let in { options.services.homed.enable = lib.mkEnableOption (lib.mdDoc '' - Enable systemd home area/user account manager + systemd home area/user account manager ''); config = lib.mkIf cfg.enable { diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix index 5d9fca7a605..175e757cbbb 100644 --- a/nixos/modules/system/boot/systemd/initrd.nix +++ b/nixos/modules/system/boot/systemd/initrd.nix @@ -57,7 +57,6 @@ let "systemd-ask-password-console.service" "systemd-fsck@.service" "systemd-halt.service" - "systemd-hibernate-resume@.service" "systemd-journald-audit.socket" "systemd-journald-dev-log.socket" "systemd-journald.service" @@ -136,8 +135,13 @@ in { ''; }; - package = mkPackageOptionMD pkgs "systemd" { - default = "systemdStage1"; + package = lib.mkOption { + type = lib.types.package; + default = config.systemd.package; + defaultText = lib.literalExpression "config.systemd.package"; + description = '' + The systemd package to use. + ''; }; extraConfig = mkOption { @@ -354,7 +358,7 @@ in { ++ lib.optional (cfg.enableTpm2 && !(pkgs.stdenv.hostPlatform.isRiscV64 || pkgs.stdenv.hostPlatform.isArmv7)) "tpm-crb"; boot.initrd.systemd = { - initrdBin = [pkgs.bash pkgs.coreutils cfg.package.kmod cfg.package] ++ config.system.fsPackages; + initrdBin = [pkgs.bash pkgs.coreutils cfg.package.kmod cfg.package]; extraBin = { less = "${pkgs.less}/bin/less"; mount = "${cfg.package.util-linux}/bin/mount"; diff --git a/nixos/modules/system/boot/systemd/repart.nix b/nixos/modules/system/boot/systemd/repart.nix index 2431c68ea17..5ac2ace56ba 100644 --- a/nixos/modules/system/boot/systemd/repart.nix +++ b/nixos/modules/system/boot/systemd/repart.nix @@ -74,6 +74,15 @@ in }; config = lib.mkIf (cfg.enable || initrdCfg.enable) { + assertions = [ + { + assertion = initrdCfg.enable -> config.boot.initrd.systemd.enable; + message = '' + 'boot.initrd.systemd.repart.enable' requires 'boot.initrd.systemd.enable' to be enabled. + ''; + } + ]; + boot.initrd.systemd = lib.mkIf initrdCfg.enable { additionalUpstreamUnits = [ "systemd-repart.service" diff --git a/nixos/modules/system/boot/systemd/tmpfiles.nix b/nixos/modules/system/boot/systemd/tmpfiles.nix index 32b9b275d35..183e2033ecb 100644 --- a/nixos/modules/system/boot/systemd/tmpfiles.nix +++ b/nixos/modules/system/boot/systemd/tmpfiles.nix @@ -20,6 +20,102 @@ in ''; }; + systemd.tmpfiles.settings = mkOption { + description = lib.mdDoc '' + Declare systemd-tmpfiles rules to create, delete, and clean up volatile + and temporary files and directories. + + Even though the service is called `*tmp*files` you can also create + persistent files. + ''; + example = { + "10-mypackage" = { + "/var/lib/my-service/statefolder".d = { + mode = "0755"; + user = "root"; + group = "root"; + }; + }; + }; + default = {}; + type = types.attrsOf (types.attrsOf (types.attrsOf (types.submodule ({ name, config, ... }: { + options.type = mkOption { + type = types.str; + default = name; + example = "d"; + description = lib.mdDoc '' + The type of operation to perform on the file. + + The type consists of a single letter and optionally one or more + modifier characters. + + Please see the upstream documentation for the available types and + more details: + <https://www.freedesktop.org/software/systemd/man/tmpfiles.d> + ''; + }; + options.mode = mkOption { + type = types.str; + default = "-"; + example = "0755"; + description = lib.mdDoc '' + The file access mode to use when creating this file or directory. + ''; + }; + options.user = mkOption { + type = types.str; + default = "-"; + example = "root"; + description = lib.mdDoc '' + The user of the file. + + This may either be a numeric ID or a user/group name. + + If omitted or when set to `"-"`, the user and group of the user who + invokes systemd-tmpfiles is used. + ''; + }; + options.group = mkOption { + type = types.str; + default = "-"; + example = "root"; + description = lib.mdDoc '' + The group of the file. + + This may either be a numeric ID or a user/group name. + + If omitted or when set to `"-"`, the user and group of the user who + invokes systemd-tmpfiles is used. + ''; + }; + options.age = mkOption { + type = types.str; + default = "-"; + example = "10d"; + description = lib.mdDoc '' + Delete a file when it reaches a certain age. + + If a file or directory is older than the current time minus the age + field, it is deleted. + + If set to `"-"` no automatic clean-up is done. + ''; + }; + options.argument = mkOption { + type = types.str; + default = ""; + example = ""; + description = lib.mdDoc '' + An argument whose meaning depends on the type of operation. + + Please see the upstream documentation for the meaning of this + parameter in different situations: + <https://www.freedesktop.org/software/systemd/man/tmpfiles.d> + ''; + }; + })))); + }; + systemd.tmpfiles.packages = mkOption { type = types.listOf types.package; default = []; @@ -100,7 +196,13 @@ in ${concatStringsSep "\n" cfg.rules} ''; }) - ]; + ] ++ (mapAttrsToList (name: paths: + pkgs.writeTextDir "lib/tmpfiles.d/${name}.conf" (concatStrings (mapAttrsToList (path: types: + concatStrings (mapAttrsToList (_type: entry: '' + '${entry.type}' '${path}' '${entry.mode}' '${entry.user}' '${entry.group}' '${entry.age}' ${entry.argument} + '') types) + ) paths )) + ) cfg.settings); systemd.tmpfiles.rules = [ "d /nix/var 0755 root root - -" diff --git a/nixos/modules/system/boot/systemd/userdbd.nix b/nixos/modules/system/boot/systemd/userdbd.nix index 994aa3ca3b8..e7f6d42341c 100644 --- a/nixos/modules/system/boot/systemd/userdbd.nix +++ b/nixos/modules/system/boot/systemd/userdbd.nix @@ -5,7 +5,7 @@ let in { options.services.userdbd.enable = lib.mkEnableOption (lib.mdDoc '' - Enables the systemd JSON user/group record lookup service + the systemd JSON user/group record lookup service ''); config = lib.mkIf cfg.enable { systemd.additionalUpstreamSystemUnits = [ |