about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/system/boot
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/system/boot')
-rw-r--r--nixpkgs/nixos/modules/system/boot/clevis.md6
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl6
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py89
-rw-r--r--nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix17
-rw-r--r--nixpkgs/nixos/modules/system/boot/luksroot.nix4
-rw-r--r--nixpkgs/nixos/modules/system/boot/networkd.nix6
-rw-r--r--nixpkgs/nixos/modules/system/boot/resolved.nix24
-rw-r--r--nixpkgs/nixos/modules/system/boot/stage-1-init.sh11
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd.nix18
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/initrd.nix4
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/journald.nix4
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/sysupdate.nix4
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/sysusers.nix169
-rw-r--r--nixpkgs/nixos/modules/system/boot/uki.nix85
14 files changed, 382 insertions, 65 deletions
diff --git a/nixpkgs/nixos/modules/system/boot/clevis.md b/nixpkgs/nixos/modules/system/boot/clevis.md
index 91eb728a919e..dcbf55de60a8 100644
--- a/nixpkgs/nixos/modules/system/boot/clevis.md
+++ b/nixpkgs/nixos/modules/system/boot/clevis.md
@@ -14,20 +14,20 @@ JWE files have to be created through the clevis command line. 3 types of policie
 
 Secrets are pinned against the presence of a TPM2 device, for example:
 ```
-echo hi | clevis encrypt tpm2 '{}' > hi.jwe
+echo -n hi | clevis encrypt tpm2 '{}' > hi.jwe
 ```
 2) Tang policies
 
 Secrets are pinned against the presence of a Tang server, for example:
 ```
-echo hi | clevis encrypt tang '{"url": "http://tang.local"}' > hi.jwe
+echo -n hi | clevis encrypt tang '{"url": "http://tang.local"}' > hi.jwe
 ```
 
 3) Shamir Secret Sharing
 
 Using Shamir's Secret Sharing ([sss](https://en.wikipedia.org/wiki/Shamir%27s_secret_sharing)), secrets are pinned using a combination of the two preceding policies. For example:
 ```
-echo hi | clevis encrypt sss \
+echo -n hi | clevis encrypt sss \
 '{"t": 2, "pins": {"tpm2": {"pcr_ids": "0"}, "tang": {"url": "http://tang.local"}}}' \
 > hi.jwe
 ```
diff --git a/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl b/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl
index d1e7a0cb8178..6f0f62546a01 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl
+++ b/nixpkgs/nixos/modules/system/boot/loader/grub/install-grub.pl
@@ -136,7 +136,6 @@ sub GetFs {
         chomp $fs;
         my @fields = split / /, $fs;
         my $mountPoint = $fields[4];
-        next unless -d $mountPoint;
         my @mountOptions = split /,/, $fields[5];
 
         # Skip the optional fields.
@@ -155,6 +154,11 @@ sub GetFs {
 
         # Is it better than our current match?
         if (length($mountPoint) > length($bestFs->mount)) {
+
+            # -d performs a stat, which can hang forever on network file systems,
+            # so we only make this call last, when it's likely that this is the mount point we need.
+            next unless -d $mountPoint;
+
             $bestFs = Fs->new(device => $device, type => $fsType, mount => $mountPoint);
         }
     }
diff --git a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
index 6cd46f30373b..a9978d7adf80 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
+++ b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot-builder.py
@@ -15,6 +15,19 @@ import json
 from typing import NamedTuple, Dict, List
 from dataclasses import dataclass
 
+# These values will be replaced with actual values during the package build
+EFI_SYS_MOUNT_POINT = "@efiSysMountPoint@"
+TIMEOUT = "@timeout@"
+EDITOR = "@editor@" == "1"
+CONSOLE_MODE = "@consoleMode@"
+BOOTSPEC_TOOLS = "@bootspecTools@"
+DISTRO_NAME = "@distroName@"
+NIX = "@nix@"
+SYSTEMD = "@systemd@"
+CONFIGURATION_LIMIT = int("@configurationLimit@")
+CAN_TOUCH_EFI_VARIABLES = "@canTouchEfiVariables@"
+GRACEFUL = "@graceful@"
+COPY_EXTRA_FILES = "@copyExtraFiles@"
 
 @dataclass
 class BootSpec:
@@ -29,7 +42,6 @@ class BootSpec:
     initrdSecrets: str | None = None
 
 
-
 libc = ctypes.CDLL("libc.so.6")
 
 class SystemIdentifier(NamedTuple):
@@ -75,16 +87,16 @@ def generation_conf_filename(profile: str | None, generation: int, specialisatio
 
 
 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")
+    with open(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf.tmp", 'w') as f:
+        if TIMEOUT != "":
+            f.write(f"timeout {TIMEOUT}\n")
         f.write("default %s\n" % generation_conf_filename(profile, generation, specialisation))
-        if not @editor@:
+        if not EDITOR:
             f.write("editor 0\n")
-        f.write("console-mode @consoleMode@\n")
+        f.write(f"console-mode {CONSOLE_MODE}\n")
         f.flush()
         os.fsync(f.fileno())
-    os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf")
+    os.rename(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf.tmp", f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf")
 
 
 def get_bootspec(profile: str | None, generation: int) -> BootSpec:
@@ -95,7 +107,7 @@ def get_bootspec(profile: str | None, generation: int) -> BootSpec:
         bootspec_json = json.load(boot_json_f)
     else:
         boot_json_str = subprocess.check_output([
-        "@bootspecTools@/bin/synthesize",
+        f"{BOOTSPEC_TOOLS}/bin/synthesize",
         "--version",
         "1",
         system_directory,
@@ -116,7 +128,7 @@ def copy_from_file(file: str, dry_run: bool = False) -> str:
     store_dir = os.path.basename(os.path.dirname(store_file_path))
     efi_file_path = "/efi/nixos/%s-%s.efi" % (store_dir, suffix)
     if not dry_run:
-        copy_if_not_exists(store_file_path, "@efiSysMountPoint@%s" % (efi_file_path))
+        copy_if_not_exists(store_file_path, f"{EFI_SYS_MOUNT_POINT}%s" % (efi_file_path))
     return efi_file_path
 
 def write_entry(profile: str | None, generation: int, specialisation: str | None,
@@ -126,13 +138,14 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None
     kernel = copy_from_file(bootspec.kernel)
     initrd = copy_from_file(bootspec.initrd)
 
-    title = "@distroName@{profile}{specialisation}".format(
+    title = "{name}{profile}{specialisation}".format(
+        name=DISTRO_NAME,
         profile=" [" + profile + "]" if profile else "",
         specialisation=" (%s)" % specialisation if specialisation else "")
 
     try:
         if bootspec.initrdSecrets is not None:
-            subprocess.check_call([bootspec.initrdSecrets, "@efiSysMountPoint@%s" % (initrd)])
+            subprocess.check_call([bootspec.initrdSecrets, f"{EFI_SYS_MOUNT_POINT}%s" % (initrd)])
     except subprocess.CalledProcessError:
         if current:
             print("failed to create initrd secrets!", file=sys.stderr)
@@ -142,7 +155,7 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None
                   f'for "{title} - Configuration {generation}", an older generation', file=sys.stderr)
             print("note: this is normal after having removed "
                   "or renamed a file in `boot.initrd.secrets`", file=sys.stderr)
-    entry_file = "@efiSysMountPoint@/loader/entries/%s" % (
+    entry_file = f"{EFI_SYS_MOUNT_POINT}/loader/entries/%s" % (
         generation_conf_filename(profile, generation, specialisation))
     tmp_path = "%s.tmp" % (entry_file)
     kernel_params = "init=%s " % bootspec.init
@@ -167,7 +180,7 @@ def write_entry(profile: str | None, generation: int, specialisation: str | None
 
 def get_generations(profile: str | None = None) -> list[SystemIdentifier]:
     gen_list = subprocess.check_output([
-        "@nix@/bin/nix-env",
+        f"{NIX}/bin/nix-env",
         "--list-generations",
         "-p",
         "/nix/var/nix/profiles/%s" % ("system-profiles/" + profile if profile else "system"),
@@ -176,7 +189,7 @@ def get_generations(profile: str | None = None) -> list[SystemIdentifier]:
     gen_lines = gen_list.split('\n')
     gen_lines.pop()
 
-    configurationLimit = @configurationLimit@
+    configurationLimit = CONFIGURATION_LIMIT
     configurations = [
         SystemIdentifier(
             profile=profile,
@@ -189,14 +202,14 @@ def get_generations(profile: str | None = None) -> list[SystemIdentifier]:
 
 
 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$")
+    rex_profile = re.compile(r"^" + re.escape(EFI_SYS_MOUNT_POINT) + "/loader/entries/nixos-(.*)-generation-.*\.conf$")
+    rex_generation = re.compile(r"^" + re.escape(EFI_SYS_MOUNT_POINT) + "/loader/entries/nixos.*-generation-([0-9]+)(-specialisation-.*)?\.conf$")
     known_paths = []
     for gen in gens:
         bootspec = get_bootspec(gen.profile, gen.generation)
         known_paths.append(copy_from_file(bootspec.kernel, True))
         known_paths.append(copy_from_file(bootspec.initrd, True))
-    for path in glob.iglob("@efiSysMountPoint@/loader/entries/nixos*-generation-[1-9]*.conf"):
+    for path in glob.iglob(f"{EFI_SYS_MOUNT_POINT}/loader/entries/nixos*-generation-[1-9]*.conf"):
         if rex_profile.match(path):
             prof = rex_profile.sub(r"\1", path)
         else:
@@ -207,7 +220,7 @@ def remove_old_entries(gens: list[SystemIdentifier]) -> None:
             continue
         if not (prof, gen_number, None) in gens:
             os.unlink(path)
-    for path in glob.iglob("@efiSysMountPoint@/efi/nixos/*"):
+    for path in glob.iglob(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/*"):
         if not path in known_paths and not os.path.isdir(path):
             os.unlink(path)
 
@@ -230,7 +243,7 @@ def install_bootloader(args: argparse.Namespace) -> None:
         # Since systemd version 232 a machine ID is required and it might not
         # be there on newly installed systems, so let's generate one so that
         # bootctl can find it and we can also pass it to write_entry() later.
-        cmd = ["@systemd@/bin/systemd-machine-id-setup", "--print"]
+        cmd = [f"{SYSTEMD}/bin/systemd-machine-id-setup", "--print"]
         machine_id = subprocess.run(
           cmd, text=True, check=True, stdout=subprocess.PIPE
         ).stdout.rstrip()
@@ -242,22 +255,22 @@ def install_bootloader(args: argparse.Namespace) -> None:
     # flags to pass to bootctl install/update
     bootctl_flags = []
 
-    if "@canTouchEfiVariables@" != "1":
+    if CAN_TOUCH_EFI_VARIABLES != "1":
         bootctl_flags.append("--no-variables")
 
-    if "@graceful@" == "1":
+    if GRACEFUL == "1":
         bootctl_flags.append("--graceful")
 
     if os.getenv("NIXOS_INSTALL_BOOTLOADER") == "1":
         # bootctl uses fopen() with modes "wxe" and fails if the file exists.
-        if os.path.exists("@efiSysMountPoint@/loader/loader.conf"):
-            os.unlink("@efiSysMountPoint@/loader/loader.conf")
+        if os.path.exists(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf"):
+            os.unlink(f"{EFI_SYS_MOUNT_POINT}/loader/loader.conf")
 
-        subprocess.check_call(["@systemd@/bin/bootctl", "--esp-path=@efiSysMountPoint@"] + bootctl_flags + ["install"])
+        subprocess.check_call([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"] + bootctl_flags + ["install"])
     else:
         # Update bootloader to latest if needed
-        available_out = subprocess.check_output(["@systemd@/bin/bootctl", "--version"], universal_newlines=True).split()[2]
-        installed_out = subprocess.check_output(["@systemd@/bin/bootctl", "--esp-path=@efiSysMountPoint@", "status"], universal_newlines=True)
+        available_out = subprocess.check_output([f"{SYSTEMD}/bin/bootctl", "--version"], universal_newlines=True).split()[2]
+        installed_out = subprocess.check_output([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}", "status"], universal_newlines=True)
 
         # See status_binaries() in systemd bootctl.c for code which generates this
         installed_match = re.search(r"^\W+File:.*/EFI/(?:BOOT|systemd)/.*\.efi \(systemd-boot ([\d.]+[^)]*)\)$",
@@ -276,10 +289,10 @@ def install_bootloader(args: argparse.Namespace) -> None:
 
         if installed_version < available_version:
             print("updating systemd-boot from %s to %s" % (installed_version, available_version))
-            subprocess.check_call(["@systemd@/bin/bootctl", "--esp-path=@efiSysMountPoint@"] + bootctl_flags + ["update"])
+            subprocess.check_call([f"{SYSTEMD}/bin/bootctl", f"--esp-path={EFI_SYS_MOUNT_POINT}"] + bootctl_flags + ["update"])
 
-    os.makedirs("@efiSysMountPoint@/efi/nixos", exist_ok=True)
-    os.makedirs("@efiSysMountPoint@/loader/entries", exist_ok=True)
+    os.makedirs(f"{EFI_SYS_MOUNT_POINT}/efi/nixos", exist_ok=True)
+    os.makedirs(f"{EFI_SYS_MOUNT_POINT}/loader/entries", exist_ok=True)
 
     gens = get_generations()
     for profile in get_profiles():
@@ -302,9 +315,9 @@ def install_bootloader(args: argparse.Namespace) -> None:
             else:
                 raise e
 
-    for root, _, files in os.walk('@efiSysMountPoint@/efi/nixos/.extra-files', topdown=False):
-        relative_root = root.removeprefix("@efiSysMountPoint@/efi/nixos/.extra-files").removeprefix("/")
-        actual_root = os.path.join("@efiSysMountPoint@", relative_root)
+    for root, _, files in os.walk(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/.extra-files", topdown=False):
+        relative_root = root.removeprefix(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/.extra-files").removeprefix("/")
+        actual_root = os.path.join(f"{EFI_SYS_MOUNT_POINT}", relative_root)
 
         for file in files:
             actual_file = os.path.join(actual_root, file)
@@ -317,14 +330,14 @@ def install_bootloader(args: argparse.Namespace) -> None:
             os.rmdir(actual_root)
         os.rmdir(root)
 
-    os.makedirs("@efiSysMountPoint@/efi/nixos/.extra-files", exist_ok=True)
+    os.makedirs(f"{EFI_SYS_MOUNT_POINT}/efi/nixos/.extra-files", exist_ok=True)
 
-    subprocess.check_call("@copyExtraFiles@")
+    subprocess.check_call(COPY_EXTRA_FILES)
 
 
 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')
+    parser = argparse.ArgumentParser(description=f"Update {DISTRO_NAME}-related systemd-boot files")
+    parser.add_argument('default_config', metavar='DEFAULT-CONFIG', help=f"The default {DISTRO_NAME} config to boot")
     args = parser.parse_args()
 
     try:
@@ -334,9 +347,9 @@ def main() -> None:
         # 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))
+        rc = libc.syncfs(os.open(f"{EFI_SYS_MOUNT_POINT}", os.O_RDONLY))
         if rc != 0:
-            print("could not sync @efiSysMountPoint@: {}".format(os.strerror(rc)), file=sys.stderr)
+            print(f"could not sync {EFI_SYS_MOUNT_POINT}: {os.strerror(rc)}", file=sys.stderr)
 
 
 if __name__ == '__main__':
diff --git a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
index 9d55c21077d1..ea4553b8208f 100644
--- a/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
+++ b/nixpkgs/nixos/modules/system/boot/loader/systemd-boot/systemd-boot.nix
@@ -22,11 +22,9 @@ let
 
     timeout = optionalString (config.boot.loader.timeout != null) config.boot.loader.timeout;
 
-    editor = if cfg.editor then "True" else "False";
-
     configurationLimit = if cfg.configurationLimit == null then 0 else cfg.configurationLimit;
 
-    inherit (cfg) consoleMode graceful;
+    inherit (cfg) consoleMode graceful editor;
 
     inherit (efi) efiSysMountPoint canTouchEfiVariables;
 
@@ -54,17 +52,18 @@ let
   checkedSystemdBootBuilder = pkgs.runCommand "systemd-boot" {
     nativeBuildInputs = [ pkgs.mypy ];
   } ''
-    install -m755 ${systemdBootBuilder} $out
+    mkdir -p $out/bin
+    install -m755 ${systemdBootBuilder} $out/bin/systemd-boot-builder
     mypy \
       --no-implicit-optional \
       --disallow-untyped-calls \
       --disallow-untyped-defs \
-      $out
+      $out/bin/systemd-boot-builder
   '';
 
   finalSystemdBootBuilder = pkgs.writeScript "install-systemd-boot.sh" ''
     #!${pkgs.runtimeShell}
-    ${checkedSystemdBootBuilder} "$@"
+    ${checkedSystemdBootBuilder}/bin/systemd-boot-builder "$@"
     ${cfg.extraInstallCommands}
   '';
 in {
@@ -81,7 +80,11 @@ in {
 
       type = types.bool;
 
-      description = lib.mdDoc "Whether to enable the systemd-boot (formerly gummiboot) EFI boot manager";
+      description = lib.mdDoc ''
+        Whether to enable the systemd-boot (formerly gummiboot) EFI boot manager.
+        For more information about systemd-boot:
+        https://www.freedesktop.org/wiki/Software/systemd/systemd-boot/
+      '';
     };
 
     editor = mkOption {
diff --git a/nixpkgs/nixos/modules/system/boot/luksroot.nix b/nixpkgs/nixos/modules/system/boot/luksroot.nix
index 221e90b6f38f..86a3875e2c67 100644
--- a/nixpkgs/nixos/modules/system/boot/luksroot.nix
+++ b/nixpkgs/nixos/modules/system/boot/luksroot.nix
@@ -1076,7 +1076,7 @@ in
     boot.initrd.systemd = {
       contents."/etc/crypttab".source = stage1Crypttab;
 
-      extraBin.systemd-cryptsetup = "${config.boot.initrd.systemd.package}/lib/systemd/systemd-cryptsetup";
+      extraBin.systemd-cryptsetup = "${config.boot.initrd.systemd.package}/bin/systemd-cryptsetup";
 
       additionalUpstreamUnits = [
         "cryptsetup-pre.target"
@@ -1084,7 +1084,7 @@ in
         "remote-cryptsetup.target"
       ];
       storePaths = [
-        "${config.boot.initrd.systemd.package}/lib/systemd/systemd-cryptsetup"
+        "${config.boot.initrd.systemd.package}/bin/systemd-cryptsetup"
         "${config.boot.initrd.systemd.package}/lib/systemd/system-generators/systemd-cryptsetup-generator"
       ];
 
diff --git a/nixpkgs/nixos/modules/system/boot/networkd.nix b/nixpkgs/nixos/modules/system/boot/networkd.nix
index f236a4c005ad..a7399bd55e77 100644
--- a/nixpkgs/nixos/modules/system/boot/networkd.nix
+++ b/nixpkgs/nixos/modules/system/boot/networkd.nix
@@ -2989,15 +2989,9 @@ let
 
       systemd.services.systemd-networkd = {
         wantedBy = [ "initrd.target" ];
-        # These before and conflicts lines can be removed when this PR makes it into a release:
-        # https://github.com/systemd/systemd/pull/27791
-        before = ["initrd-switch-root.target"];
-        conflicts = ["initrd-switch-root.target"];
       };
       systemd.sockets.systemd-networkd = {
         wantedBy = [ "initrd.target" ];
-        before = ["initrd-switch-root.target"];
-        conflicts = ["initrd-switch-root.target"];
       };
 
       systemd.services.systemd-network-generator.wantedBy = [ "sysinit.target" ];
diff --git a/nixpkgs/nixos/modules/system/boot/resolved.nix b/nixpkgs/nixos/modules/system/boot/resolved.nix
index 538f71cc0b9a..c42c88163c56 100644
--- a/nixpkgs/nixos/modules/system/boot/resolved.nix
+++ b/nixpkgs/nixos/modules/system/boot/resolved.nix
@@ -95,6 +95,29 @@ in
       '';
     };
 
+    services.resolved.dnsovertls = mkOption {
+      default = "false";
+      example = "true";
+      type = types.enum [ "true" "opportunistic" "false" ];
+      description = lib.mdDoc ''
+        If set to
+        - `"true"`:
+            all DNS lookups will be encrypted. This requires
+            that the DNS server supports DNS-over-TLS and
+            has a valid certificate. If the hostname was specified
+            via the `address#hostname` format in {option}`services.resolved.domains`
+            then the specified hostname is used to validate its certificate.
+        - `"opportunistic"`:
+            all DNS lookups will attempt to be encrypted, but will fallback
+            to unecrypted requests if the server does not support DNS-over-TLS.
+            Note that this mode does allow for a malicious party to conduct a
+            downgrade attack by immitating the DNS server and pretending to not
+            support encryption.
+        - `"false"`:
+            all DNS lookups are done unencrypted.
+      '';
+    };
+
     services.resolved.extraConfig = mkOption {
       default = "";
       type = types.lines;
@@ -141,6 +164,7 @@ in
           "Domains=${concatStringsSep " " cfg.domains}"}
         LLMNR=${cfg.llmnr}
         DNSSEC=${cfg.dnssec}
+        DNSOverTLS=${cfg.dnsovertls}
         ${config.services.resolved.extraConfig}
       '';
 
diff --git a/nixpkgs/nixos/modules/system/boot/stage-1-init.sh b/nixpkgs/nixos/modules/system/boot/stage-1-init.sh
index 086e5d65da2f..59cf1a47fb7f 100644
--- a/nixpkgs/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixpkgs/nixos/modules/system/boot/stage-1-init.sh
@@ -86,9 +86,14 @@ touch /etc/initrd-release
 # Function for waiting for device(s) to appear.
 waitDevice() {
     local device="$1"
-    # Split device string using ':' as a delimiter as bcachefs
-    # uses this for multi-device filesystems, i.e. /dev/sda1:/dev/sda2:/dev/sda3
-    local IFS=':'
+    # Split device string using ':' as a delimiter, bcachefs uses
+    # this for multi-device filesystems, i.e. /dev/sda1:/dev/sda2:/dev/sda3
+    local IFS
+
+    # bcachefs is the only known use for this at the moment
+    # Preferably, the 'UUID=' syntax should be enforced, but
+    # this is kept for compatibility reasons
+    if [ "$fsType" = bcachefs ]; then IFS=':'; fi
 
     # USB storage devices tend to appear with some delay.  It would be
     # great if we had a way to synchronously wait for them, but
diff --git a/nixpkgs/nixos/modules/system/boot/systemd.nix b/nixpkgs/nixos/modules/system/boot/systemd.nix
index 46c3f66f02dc..e29fa49ea23b 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd.nix
@@ -428,7 +428,13 @@ in
 
   config = {
 
-    warnings = concatLists (
+    warnings = let
+      mkOneNetOnlineWarn = typeStr: name: def: lib.optional
+        (lib.elem "network-online.target" def.after && !(lib.elem "network-online.target" (def.wants ++ def.requires ++ def.bindsTo)))
+        "${name}.${typeStr} is ordered after 'network-online.target' but doesn't depend on it";
+      mkNetOnlineWarns = typeStr: defs: lib.concatLists (lib.mapAttrsToList (mkOneNetOnlineWarn typeStr) defs);
+      mkMountNetOnlineWarns = typeStr: defs: lib.concatLists (map (m: mkOneNetOnlineWarn typeStr m.what m) defs);
+    in concatLists (
       mapAttrsToList
         (name: service:
           let
@@ -449,7 +455,15 @@ in
             ]
         )
         cfg.services
-    );
+    )
+    ++ (mkNetOnlineWarns "target" cfg.targets)
+    ++ (mkNetOnlineWarns "service" cfg.services)
+    ++ (mkNetOnlineWarns "socket" cfg.sockets)
+    ++ (mkNetOnlineWarns "timer" cfg.timers)
+    ++ (mkNetOnlineWarns "path" cfg.paths)
+    ++ (mkMountNetOnlineWarns "mount" cfg.mounts)
+    ++ (mkMountNetOnlineWarns "automount" cfg.automounts)
+    ++ (mkNetOnlineWarns "slice" cfg.slices);
 
     assertions = concatLists (
       mapAttrsToList
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/initrd.nix b/nixpkgs/nixos/modules/system/boot/systemd/initrd.nix
index 4ae07944afc3..9641921fc795 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd/initrd.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd/initrd.nix
@@ -57,6 +57,7 @@ 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"
@@ -70,6 +71,7 @@ let
     "systemd-tmpfiles-setup.service"
     "timers.target"
     "umount.target"
+    "systemd-bsod.service"
   ] ++ cfg.additionalUpstreamUnits;
 
   upstreamWants = [
@@ -424,6 +426,7 @@ in {
 
       storePaths = [
         # systemd tooling
+        "${cfg.package}/lib/systemd/systemd-executor"
         "${cfg.package}/lib/systemd/systemd-fsck"
         "${cfg.package}/lib/systemd/systemd-hibernate-resume"
         "${cfg.package}/lib/systemd/systemd-journald"
@@ -433,6 +436,7 @@ in {
         "${cfg.package}/lib/systemd/systemd-shutdown"
         "${cfg.package}/lib/systemd/systemd-sulogin-shell"
         "${cfg.package}/lib/systemd/systemd-sysctl"
+        "${cfg.package}/lib/systemd/systemd-bsod"
 
         # generators
         "${cfg.package}/lib/systemd/system-generators/systemd-debug-generator"
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/journald.nix b/nixpkgs/nixos/modules/system/boot/systemd/journald.nix
index 9a8e7d592603..62e24a305dde 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd/journald.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd/journald.nix
@@ -56,7 +56,9 @@ in {
         journald.conf(5)](https://www.freedesktop.org/software/systemd/man/journald.conf.html).
 
         Note that the total amount of logs stored is limited by journald settings
-        such as `SystemMaxUse`, which defaults to a 4 GB cap.
+        such as `SystemMaxUse`, which defaults to 10% the file system size
+        (capped at max 4GB), and `SystemKeepFree`, which defaults to 15% of the
+        file system size.
 
         It is thus recommended to compute what period of time that you will be
         able to store logs for when an application logs at full burst rate.
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/sysupdate.nix b/nixpkgs/nixos/modules/system/boot/systemd/sysupdate.nix
index cab35ddf270c..1f4088ddf825 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd/sysupdate.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd/sysupdate.nix
@@ -3,7 +3,7 @@
 let
   cfg = config.systemd.sysupdate;
 
-  format = pkgs.formats.ini { };
+  format = pkgs.formats.ini { listToValue = toString; };
 
   definitionsDirectory = utils.systemdUtils.lib.definitions
     "sysupdate.d"
@@ -79,7 +79,7 @@ in
           Source = {
             Type = "url-file";
             Path = "https://download.example.com/";
-            MatchPattern = "nixos_@v.efi.xz";
+            MatchPattern = [ "nixos_@v+@l-@d.efi" "nixos_@v+@l.efi" "nixos_@v.efi" ];
           };
 
           Target = {
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/sysusers.nix b/nixpkgs/nixos/modules/system/boot/systemd/sysusers.nix
new file mode 100644
index 000000000000..c619c2d91eb0
--- /dev/null
+++ b/nixpkgs/nixos/modules/system/boot/systemd/sysusers.nix
@@ -0,0 +1,169 @@
+{ config, lib, pkgs, utils, ... }:
+
+let
+
+  cfg = config.systemd.sysusers;
+  userCfg = config.users;
+
+  sysusersConfig = pkgs.writeTextDir "00-nixos.conf" ''
+    # Type Name ID GECOS Home directory Shell
+
+    # Users
+    ${lib.concatLines (lib.mapAttrsToList
+      (username: opts:
+        let
+          uid = if opts.uid == null then "-" else toString opts.uid;
+        in
+          ''u ${username} ${uid}:${opts.group} "${opts.description}" ${opts.home} ${utils.toShellPath opts.shell}''
+      )
+      userCfg.users)
+    }
+
+    # Groups
+    ${lib.concatLines (lib.mapAttrsToList
+      (groupname: opts: ''g ${groupname} ${if opts.gid == null then "-" else toString opts.gid}'') userCfg.groups)
+    }
+
+    # Group membership
+    ${lib.concatStrings (lib.mapAttrsToList
+      (groupname: opts: (lib.concatMapStrings (username: "m ${username} ${groupname}\n")) opts.members ) userCfg.groups)
+    }
+  '';
+
+  staticSysusersCredentials = pkgs.runCommand "static-sysusers-credentials" { } ''
+    mkdir $out; cd $out
+    ${lib.concatLines (
+      (lib.mapAttrsToList
+        (username: opts: "echo -n '${opts.initialHashedPassword}' > 'passwd.hashed-password.${username}'")
+        (lib.filterAttrs (_username: opts: opts.initialHashedPassword != null) userCfg.users))
+        ++
+      (lib.mapAttrsToList
+        (username: opts: "echo -n '${opts.initialPassword}' > 'passwd.plaintext-password.${username}'")
+        (lib.filterAttrs (_username: opts: opts.initialPassword != null) userCfg.users))
+        ++
+      (lib.mapAttrsToList
+        (username: opts: "cat '${opts.hashedPasswordFile}' > 'passwd.hashed-password.${username}'")
+        (lib.filterAttrs (_username: opts: opts.hashedPasswordFile != null) userCfg.users))
+      )
+    }
+  '';
+
+  staticSysusers = pkgs.runCommand "static-sysusers"
+    {
+      nativeBuildInputs = [ pkgs.systemd ];
+    } ''
+    mkdir $out
+    export CREDENTIALS_DIRECTORY=${staticSysusersCredentials}
+    systemd-sysusers --root $out ${sysusersConfig}/00-nixos.conf
+  '';
+
+in
+
+{
+
+  options = {
+
+    # This module doesn't set it's own user options but reuses the ones from
+    # users-groups.nix
+
+    systemd.sysusers = {
+      enable = lib.mkEnableOption (lib.mdDoc "systemd-sysusers") // {
+        description = lib.mdDoc ''
+          If enabled, users are created with systemd-sysusers instead of with
+          the custom `update-users-groups.pl` script.
+
+          Note: This is experimental.
+        '';
+      };
+    };
+
+  };
+
+  config = lib.mkIf cfg.enable {
+
+    assertions = [
+      {
+        assertion = config.system.activationScripts.users == "";
+        message = "system.activationScripts.users has to be empty to use systemd-sysusers";
+      }
+      {
+        assertion = config.users.mutableUsers -> config.system.etc.overlay.enable;
+        message = "config.users.mutableUsers requires config.system.etc.overlay.enable.";
+      }
+    ];
+
+    systemd = lib.mkMerge [
+      ({
+
+        # Create home directories, do not create /var/empty even if that's a user's
+        # home.
+        tmpfiles.settings.home-directories = lib.mapAttrs'
+          (username: opts: lib.nameValuePair opts.home {
+            d = {
+              mode = opts.homeMode;
+              user = username;
+              group = opts.group;
+            };
+          })
+          (lib.filterAttrs (_username: opts: opts.home != "/var/empty") userCfg.users);
+      })
+
+      (lib.mkIf config.users.mutableUsers {
+        additionalUpstreamSystemUnits = [
+          "systemd-sysusers.service"
+        ];
+
+        services.systemd-sysusers = {
+          # Enable switch-to-configuration to restart the service.
+          unitConfig.ConditionNeedsUpdate = [ "" ];
+          requiredBy = [ "sysinit-reactivation.target" ];
+          before = [ "sysinit-reactivation.target" ];
+          restartTriggers = [ "${config.environment.etc."sysusers.d".source}" ];
+
+          serviceConfig = {
+            LoadCredential = lib.mapAttrsToList
+              (username: opts: "passwd.hashed-password.${username}:${opts.hashedPasswordFile}")
+              (lib.filterAttrs (_username: opts: opts.hashedPasswordFile != null) userCfg.users);
+            SetCredential = (lib.mapAttrsToList
+              (username: opts: "passwd.hashed-password.${username}:${opts.initialHashedPassword}")
+              (lib.filterAttrs (_username: opts: opts.initialHashedPassword != null) userCfg.users))
+            ++
+            (lib.mapAttrsToList
+              (username: opts: "passwd.plaintext-password.${username}:${opts.initialPassword}")
+              (lib.filterAttrs (_username: opts: opts.initialPassword != null) userCfg.users))
+            ;
+          };
+        };
+      })
+    ];
+
+    environment.etc = lib.mkMerge [
+      (lib.mkIf (!userCfg.mutableUsers) {
+        "passwd" = {
+          source = "${staticSysusers}/etc/passwd";
+          mode = "0644";
+        };
+        "group" = {
+          source = "${staticSysusers}/etc/group";
+          mode = "0644";
+        };
+        "shadow" = {
+          source = "${staticSysusers}/etc/shadow";
+          mode = "0000";
+        };
+        "gshadow" = {
+          source = "${staticSysusers}/etc/gshadow";
+          mode = "0000";
+        };
+      })
+
+      (lib.mkIf userCfg.mutableUsers {
+        "sysusers.d".source = sysusersConfig;
+      })
+    ];
+
+  };
+
+  meta.maintainers = with lib.maintainers; [ nikstur ];
+
+}
diff --git a/nixpkgs/nixos/modules/system/boot/uki.nix b/nixpkgs/nixos/modules/system/boot/uki.nix
new file mode 100644
index 000000000000..63a7cbc5967b
--- /dev/null
+++ b/nixpkgs/nixos/modules/system/boot/uki.nix
@@ -0,0 +1,85 @@
+{ config, lib, pkgs, ... }:
+
+let
+
+  cfg = config.boot.uki;
+
+  inherit (pkgs.stdenv.hostPlatform) efiArch;
+
+  format = pkgs.formats.ini { };
+  ukifyConfig = format.generate "ukify.conf" cfg.settings;
+
+in
+
+{
+  options = {
+
+    boot.uki = {
+      name = lib.mkOption {
+        type = lib.types.str;
+        description = lib.mdDoc "Name of the UKI";
+      };
+
+      version = lib.mkOption {
+        type = lib.types.nullOr lib.types.str;
+        default = config.system.image.version;
+        defaultText = lib.literalExpression "config.system.image.version";
+        description = lib.mdDoc "Version of the image or generation the UKI belongs to";
+      };
+
+      settings = lib.mkOption {
+        type = format.type;
+        description = lib.mdDoc ''
+          The configuration settings for ukify. These control what the UKI
+          contains and how it is built.
+        '';
+      };
+    };
+
+    system.boot.loader.ukiFile = lib.mkOption {
+      type = lib.types.str;
+      internal = true;
+      description = lib.mdDoc "Name of the UKI file";
+    };
+
+  };
+
+  config = {
+
+    boot.uki.name = lib.mkOptionDefault (if config.system.image.id != null then
+      config.system.image.id
+    else
+      "nixos");
+
+    boot.uki.settings = {
+      UKI = {
+        Linux = lib.mkOptionDefault "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}";
+        Initrd = lib.mkOptionDefault "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
+        Cmdline = lib.mkOptionDefault "init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}";
+        Stub = lib.mkOptionDefault "${pkgs.systemd}/lib/systemd/boot/efi/linux${efiArch}.efi.stub";
+        Uname = lib.mkOptionDefault "${config.boot.kernelPackages.kernel.modDirVersion}";
+        OSRelease = lib.mkOptionDefault "@${config.system.build.etc}/etc/os-release";
+        # This is needed for cross compiling.
+        EFIArch = lib.mkOptionDefault efiArch;
+      };
+    };
+
+    system.boot.loader.ukiFile =
+      let
+        name = config.boot.uki.name;
+        version = config.boot.uki.version;
+        versionInfix = if version != null then "_${version}" else "";
+      in
+      name + versionInfix + ".efi";
+
+    system.build.uki = pkgs.runCommand config.system.boot.loader.ukiFile { } ''
+      mkdir -p $out
+      ${pkgs.buildPackages.systemdUkify}/lib/systemd/ukify build \
+        --config=${ukifyConfig} \
+        --output="$out/${config.system.boot.loader.ukiFile}"
+    '';
+
+    meta.maintainers = with lib.maintainers; [ nikstur ];
+
+  };
+}