about summary refs log tree commit diff
path: root/nixos/modules/system/boot/systemd
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/system/boot/systemd')
-rw-r--r--nixos/modules/system/boot/systemd/initrd.nix28
-rw-r--r--nixos/modules/system/boot/systemd/journald.nix3
-rw-r--r--nixos/modules/system/boot/systemd/nspawn.nix3
-rw-r--r--nixos/modules/system/boot/systemd/shutdown.nix10
-rw-r--r--nixos/modules/system/boot/systemd/sysusers.nix215
-rw-r--r--nixos/modules/system/boot/systemd/tmpfiles.nix6
6 files changed, 138 insertions, 127 deletions
diff --git a/nixos/modules/system/boot/systemd/initrd.nix b/nixos/modules/system/boot/systemd/initrd.nix
index 6107a2594baf8..0caea104b1b52 100644
--- a/nixos/modules/system/boot/systemd/initrd.nix
+++ b/nixos/modules/system/boot/systemd/initrd.nix
@@ -70,6 +70,7 @@ let
     "systemd-tmpfiles-setup-dev.service"
     "systemd-tmpfiles-setup.service"
     "timers.target"
+    "tpm2.target"
     "umount.target"
     "systemd-bsod.service"
   ] ++ cfg.additionalUpstreamUnits;
@@ -102,7 +103,7 @@ let
   initrdBinEnv = pkgs.buildEnv {
     name = "initrd-bin-env";
     paths = map getBin cfg.initrdBin;
-    pathsToLink = ["/bin" "/sbin"];
+    pathsToLink = ["/bin"];
     postBuild = concatStringsSep "\n" (mapAttrsToList (n: v: "ln -sf '${v}' $out/bin/'${n}'") cfg.extraBin);
   };
 
@@ -111,8 +112,7 @@ let
     inherit (config.boot.initrd) compressor compressorArgs prepend;
     inherit (cfg) strip;
 
-    contents = map (path: { object = path; symlink = ""; }) (subtractLists cfg.suppressedStorePaths cfg.storePaths)
-      ++ mapAttrsToList (_: v: { object = v.source; symlink = v.target; }) (filterAttrs (_: v: v.enable) cfg.contents);
+    contents = lib.filter ({ source, ... }: !lib.elem source cfg.suppressedStorePaths) cfg.storePaths;
   };
 
 in {
@@ -160,7 +160,7 @@ in {
       description = "Set of files that have to be linked into the initrd";
       example = literalExpression ''
         {
-          "/etc/hostname".text = "mymachine";
+          "/etc/machine-id".source = /etc/machine-id;
         }
       '';
       default = {};
@@ -171,7 +171,7 @@ in {
       description = ''
         Store paths to copy into the initrd as well.
       '';
-      type = with types; listOf (oneOf [ singleLineStr package ]);
+      type = utils.systemdUtils.types.initrdStorePath;
       default = [];
     };
 
@@ -344,7 +344,8 @@ in {
     };
 
     enableTpm2 = mkOption {
-      default = true;
+      default = cfg.package.withTpm2Tss;
+      defaultText = "boot.initrd.systemd.package.withTpm2Tss";
       type = types.bool;
       description = ''
         Whether to enable TPM2 support in the initrd.
@@ -407,7 +408,7 @@ in {
         fsck = "${cfg.package.util-linux}/bin/fsck";
       };
 
-      managerEnvironment.PATH = "/bin:/sbin";
+      managerEnvironment.PATH = "/bin";
 
       contents = {
         "/tmp/.keep".text = "systemd requires the /tmp mount point in the initrd cpio archive";
@@ -416,7 +417,7 @@ in {
 
         "/etc/systemd/system.conf".text = ''
           [Manager]
-          DefaultEnvironment=PATH=/bin:/sbin
+          DefaultEnvironment=PATH=/bin
           ${cfg.extraConfig}
           ManagerEnvironment=${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "${n}=${lib.escapeShellArg v}") cfg.managerEnvironment)}
         '';
@@ -431,9 +432,9 @@ in {
         "/etc/shadow".text = "root:${if isBool cfg.emergencyAccess then optionalString (!cfg.emergencyAccess) "*" else cfg.emergencyAccess}:::::::";
 
         "/bin".source = "${initrdBinEnv}/bin";
-        "/sbin".source = "${initrdBinEnv}/sbin";
+        "/sbin".source = "${initrdBinEnv}/bin";
 
-        "/etc/sysctl.d/nixos.conf".text = "kernel.modprobe = /sbin/modprobe";
+        "/etc/sysctl.d/nixos.conf".text = "kernel.modprobe = /bin/modprobe";
         "/etc/modprobe.d/systemd.conf".source = "${cfg.package}/lib/modprobe.d/systemd.conf";
         "/etc/modprobe.d/ubuntu.conf".source = pkgs.runCommand "initrd-kmod-blacklist-ubuntu" { } ''
           ${pkgs.buildPackages.perl}/bin/perl -0pe 's/## file: iwlwifi.conf(.+?)##/##/s;' $src > $out
@@ -443,6 +444,9 @@ in {
         "/etc/os-release".source = config.boot.initrd.osRelease;
         "/etc/initrd-release".source = config.boot.initrd.osRelease;
 
+        # For systemd-journald's _HOSTNAME field; needs to be set early, cannot be backfilled.
+        "/etc/hostname".text = config.networking.hostName;
+
       } // optionalAttrs (config.environment.etc ? "modprobe.d/nixos.conf") {
         "/etc/modprobe.d/nixos.conf".source = config.environment.etc."modprobe.d/nixos.conf".source;
       };
@@ -460,6 +464,7 @@ in {
         "${cfg.package}/lib/systemd/systemd-sulogin-shell"
         "${cfg.package}/lib/systemd/systemd-sysctl"
         "${cfg.package}/lib/systemd/systemd-bsod"
+        "${cfg.package}/lib/systemd/systemd-sysroot-fstab-check"
 
         # generators
         "${cfg.package}/lib/systemd/system-generators/systemd-debug-generator"
@@ -486,7 +491,8 @@ in {
         # fido2 support
         "${cfg.package}/lib/cryptsetup/libcryptsetup-token-systemd-fido2.so"
         "${pkgs.libfido2}/lib/libfido2.so.1"
-      ] ++ jobScripts;
+      ] ++ jobScripts
+      ++ map (c: builtins.removeAttrs c ["text"]) (builtins.attrValues cfg.contents);
 
       targets.initrd.aliases = ["default.target"];
       units =
diff --git a/nixos/modules/system/boot/systemd/journald.nix b/nixos/modules/system/boot/systemd/journald.nix
index f9f05d2b08f41..180a5cf6c396b 100644
--- a/nixos/modules/system/boot/systemd/journald.nix
+++ b/nixos/modules/system/boot/systemd/journald.nix
@@ -72,7 +72,7 @@ in {
       type = types.lines;
       example = "Storage=volatile";
       description = ''
-        Extra config options for systemd-journald. See man journald.conf
+        Extra config options for systemd-journald. See {manpage}`journald.conf(5)`
         for available options.
       '';
     };
@@ -96,6 +96,7 @@ in {
       "systemd-journald@.service"
       "systemd-journal-flush.service"
       "systemd-journal-catalog-update.service"
+      "systemd-journald-sync@.service"
       ] ++ (optional (!config.boot.isContainer) "systemd-journald-audit.socket") ++ [
       "systemd-journald-dev-log.socket"
       "syslog.socket"
diff --git a/nixos/modules/system/boot/systemd/nspawn.nix b/nixos/modules/system/boot/systemd/nspawn.nix
index 11fbb88838e10..e9bf82c462a95 100644
--- a/nixos/modules/system/boot/systemd/nspawn.nix
+++ b/nixos/modules/system/boot/systemd/nspawn.nix
@@ -127,6 +127,9 @@ in {
         })
         {
           systemd.targets.multi-user.wants = [ "machines.target" ];
+          systemd.services."systemd-nspawn@".environment = {
+            SYSTEMD_NSPAWN_UNIFIED_HIERARCHY = mkDefault "1";
+          };
         }
       ];
 }
diff --git a/nixos/modules/system/boot/systemd/shutdown.nix b/nixos/modules/system/boot/systemd/shutdown.nix
index 5c2525a57b4be..48477954e20c7 100644
--- a/nixos/modules/system/boot/systemd/shutdown.nix
+++ b/nixos/modules/system/boot/systemd/shutdown.nix
@@ -2,10 +2,7 @@
 
   cfg = config.systemd.shutdownRamfs;
 
-  ramfsContents = let
-    storePaths = map (p: "${p}\n") cfg.storePaths;
-    contents = lib.mapAttrsToList (_: v: "${v.source}\n${v.target}") (lib.filterAttrs (_: v: v.enable) cfg.contents);
-  in pkgs.writeText "shutdown-ramfs-contents" (lib.concatStringsSep "\n" (storePaths ++ contents));
+  ramfsContents = pkgs.writeText "shutdown-ramfs-contents.json" (builtins.toJSON cfg.storePaths);
 
 in {
   options.systemd.shutdownRamfs = {
@@ -24,7 +21,7 @@ in {
       description = ''
         Store paths to copy into the shutdown ramfs as well.
       '';
-      type = lib.types.listOf lib.types.singleLineStr;
+      type = utils.systemdUtils.types.initrdStorePath;
       default = [];
     };
   };
@@ -35,7 +32,8 @@ in {
       "/etc/initrd-release".source = config.environment.etc.os-release.source;
       "/etc/os-release".source = config.environment.etc.os-release.source;
     };
-    systemd.shutdownRamfs.storePaths = [pkgs.runtimeShell "${pkgs.coreutils}/bin"];
+    systemd.shutdownRamfs.storePaths = [pkgs.runtimeShell "${pkgs.coreutils}/bin"]
+      ++ map (c: builtins.removeAttrs c ["text"]) (builtins.attrValues cfg.contents);
 
     systemd.mounts = [{
       what = "tmpfs";
diff --git a/nixos/modules/system/boot/systemd/sysusers.nix b/nixos/modules/system/boot/systemd/sysusers.nix
index 476251e140456..8d401436daa17 100644
--- a/nixos/modules/system/boot/systemd/sysusers.nix
+++ b/nixos/modules/system/boot/systemd/sysusers.nix
@@ -5,6 +5,8 @@ let
   cfg = config.systemd.sysusers;
   userCfg = config.users;
 
+  systemUsers = lib.filterAttrs (_username: opts: !opts.isNormalUser) userCfg.users;
+
   sysusersConfig = pkgs.writeTextDir "00-nixos.conf" ''
     # Type Name ID GECOS Home directory Shell
 
@@ -16,7 +18,7 @@ let
         in
           ''u ${username} ${uid}:${opts.group} "${opts.description}" ${opts.home} ${utils.toShellPath opts.shell}''
       )
-      userCfg.users)
+      systemUsers)
     }
 
     # Groups
@@ -30,32 +32,12 @@ let
     }
   '';
 
-  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
-  '';
+  immutableEtc = config.system.etc.overlay.enable && !config.system.etc.overlay.mutable;
+  # The location of the password files when using an immutable /etc.
+  immutablePasswordFilesLocation = "/var/lib/nixos/etc";
+  passwordFilesLocation = if immutableEtc then immutablePasswordFilesLocation else "/etc";
+  # The filenames created by systemd-sysusers.
+  passwordFiles = [ "passwd" "group" "shadow" "gshadow" ];
 
 in
 
@@ -90,95 +72,114 @@ in
         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);
-
-        # Create uid/gid marker files for those without an explicit id
-        tmpfiles.settings.nixos-uid = lib.mapAttrs'
-          (username: opts: lib.nameValuePair "/var/lib/nixos/uid/${username}" {
-            f = {
-              user = username;
-            };
-          })
-          (lib.filterAttrs (_username: opts: opts.uid == null) userCfg.users);
-
-        tmpfiles.settings.nixos-gid = lib.mapAttrs'
-          (groupname: opts: lib.nameValuePair "/var/lib/nixos/gid/${groupname}" {
-            f = {
-              group = groupname;
-            };
-          })
-          (lib.filterAttrs (_groupname: opts: opts.gid == null) userCfg.groups);
+    ] ++ (lib.mapAttrsToList
+      (_username: opts: {
+        assertion = !opts.isNormalUser;
+        message = "systemd-sysusers doesn't create normal users. You can currently only use it to create system users.";
       })
+      userCfg.users)
+    ++ lib.mapAttrsToList
+      (username: opts: {
+        assertion = (opts.password == opts.initialPassword || opts.password == null) &&
+          (opts.hashedPassword == opts.initialHashedPassword || opts.hashedPassword == null);
+        message = "${username} uses password or hashedPassword. systemd-sysupdate only supports initial passwords. It'll never update your passwords.";
+      })
+      systemUsers;
+
+    systemd = {
+
+      # 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") systemUsers);
+
+      # Create uid/gid marker files for those without an explicit id
+      tmpfiles.settings.nixos-uid = lib.mapAttrs'
+        (username: opts: lib.nameValuePair "/var/lib/nixos/uid/${username}" {
+          f = {
+            user = username;
+          };
+        })
+        (lib.filterAttrs (_username: opts: opts.uid == null) systemUsers);
 
-      (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))
-            ;
+      tmpfiles.settings.nixos-gid = lib.mapAttrs'
+        (groupname: opts: lib.nameValuePair "/var/lib/nixos/gid/${groupname}" {
+          f = {
+            group = groupname;
           };
+        })
+        (lib.filterAttrs (_groupname: opts: opts.gid == null) userCfg.groups);
+
+      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 = {
+          # When we have an immutable /etc we cannot write the files directly
+          # to /etc so we write it to a different directory and symlink them
+          # into /etc.
+          #
+          # We need to explicitly list the config file, otherwise
+          # systemd-sysusers cannot find it when we also pass another flag.
+          ExecStart = lib.mkIf immutableEtc
+            [ "" "${config.systemd.package}/bin/systemd-sysusers --root ${builtins.dirOf immutablePasswordFilesLocation} /etc/sysusers.d/00-nixos.conf" ];
+
+          # Make the source files writable before executing sysusers.
+          ExecStartPre = lib.mkIf (!userCfg.mutableUsers)
+            (lib.map
+              (file: "-${pkgs.util-linux}/bin/umount ${passwordFilesLocation}/${file}")
+              passwordFiles);
+          # Make the source files read-only after sysusers has finished.
+          ExecStartPost = lib.mkIf (!userCfg.mutableUsers)
+            (lib.map
+              (file: "${pkgs.util-linux}/bin/mount --bind -o ro ${passwordFilesLocation}/${file} ${passwordFilesLocation}/${file}")
+              passwordFiles);
+
+          LoadCredential = lib.mapAttrsToList
+            (username: opts: "passwd.hashed-password.${username}:${opts.hashedPasswordFile}")
+            (lib.filterAttrs (_username: opts: opts.hashedPasswordFile != null) systemUsers);
+          SetCredential = (lib.mapAttrsToList
+            (username: opts: "passwd.hashed-password.${username}:${opts.initialHashedPassword}")
+            (lib.filterAttrs (_username: opts: opts.initialHashedPassword != null) systemUsers))
+          ++
+          (lib.mapAttrsToList
+            (username: opts: "passwd.plaintext-password.${username}:${opts.initialPassword}")
+            (lib.filterAttrs (_username: opts: opts.initialPassword != null) systemUsers))
+          ;
         };
-      })
-    ];
+      };
 
-    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 {
+    environment.etc = lib.mkMerge [
+      ({
         "sysusers.d".source = sysusersConfig;
       })
-    ];
 
+      # Statically create the symlinks to immutablePasswordFilesLocation when
+      # using an immutable /etc because we will not be able to do it at
+      # runtime!
+      (lib.mkIf immutableEtc (lib.listToAttrs (lib.map
+        (file: lib.nameValuePair file {
+          source = "${immutablePasswordFilesLocation}/${file}";
+          mode = "direct-symlink";
+        })
+        passwordFiles)))
+    ];
   };
 
   meta.maintainers = with lib.maintainers; [ nikstur ];
diff --git a/nixos/modules/system/boot/systemd/tmpfiles.nix b/nixos/modules/system/boot/systemd/tmpfiles.nix
index ded13728017d1..af37fb07d29bc 100644
--- a/nixos/modules/system/boot/systemd/tmpfiles.nix
+++ b/nixos/modules/system/boot/systemd/tmpfiles.nix
@@ -200,6 +200,10 @@ in
           rm -f $out/${removePrefix "tmpfiles.d/" name}
         '') config.system.build.etc.passthru.targets;
       }) + "/*";
+      "mtab" = {
+        mode = "direct-symlink";
+        source = "/proc/mounts";
+      };
     };
 
     systemd.tmpfiles.packages = [
@@ -244,13 +248,11 @@ in
       "L+ /nix/var/nix/gcroots/booted-system 0755 root root - /run/booted-system"
       "d  /run/lock                          0755 root root - -"
       "d  /var/db                            0755 root root - -"
-      "L  /etc/mtab                          -    -    -    - ../proc/mounts"
       "L  /var/lock                          -    -    -    - ../run/lock"
       # Boot-time cleanup
       "R! /etc/group.lock                    -    -    -    - -"
       "R! /etc/passwd.lock                   -    -    -    - -"
       "R! /etc/shadow.lock                   -    -    -    - -"
-      "R! /etc/mtab*                         -    -    -    - -"
       "R! /nix/var/nix/gcroots/tmp           -    -    -    - -"
       "R! /nix/var/nix/temproots             -    -    -    - -"
     ];