summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--nixos/doc/manual/development/settings-options.xml2
-rw-r--r--nixos/doc/manual/release-notes/rl-2105.xml7
-rw-r--r--nixos/modules/config/pulseaudio.nix1
-rw-r--r--nixos/modules/config/users-groups.nix34
-rw-r--r--nixos/modules/services/backup/borgbackup.nix1
-rw-r--r--nixos/modules/services/databases/pgmanage.nix1
-rw-r--r--nixos/modules/services/misc/bazarr.nix1
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix1
-rw-r--r--nixos/modules/services/monitoring/tuptime.nix5
-rw-r--r--nixos/modules/services/networking/bird.nix1
-rw-r--r--nixos/modules/services/networking/ncdns.nix6
-rw-r--r--nixos/modules/services/networking/pixiecore.nix1
-rw-r--r--nixos/modules/services/networking/pleroma.nix1
-rw-r--r--nixos/modules/services/security/privacyidea.nix2
-rw-r--r--nixos/modules/services/web-apps/nextcloud.nix1
-rw-r--r--nixos/tests/mysql/mariadb-galera-mariabackup.nix6
-rw-r--r--nixos/tests/mysql/mariadb-galera-rsync.nix6
-rw-r--r--nixos/tests/mysql/mysql.nix12
-rw-r--r--nixos/tests/redis.nix3
-rw-r--r--nixos/tests/rspamd.nix5
-rw-r--r--nixos/tests/shadow.nix3
-rw-r--r--nixos/tests/systemd-confinement.nix1
-rw-r--r--nixos/tests/unbound.nix11
23 files changed, 80 insertions, 32 deletions
diff --git a/nixos/doc/manual/development/settings-options.xml b/nixos/doc/manual/development/settings-options.xml
index c99c3af92f8..7795d7c8044 100644
--- a/nixos/doc/manual/development/settings-options.xml
+++ b/nixos/doc/manual/development/settings-options.xml
@@ -167,7 +167,7 @@ in {
 
     # We know that the `user` attribute exists because we set a default value
     # for it above, allowing us to use it without worries here
-    users.users.${cfg.settings.user} = {};
+    users.users.${cfg.settings.user} = { isSystemUser = true; };
 
     # ...
   };
diff --git a/nixos/doc/manual/release-notes/rl-2105.xml b/nixos/doc/manual/release-notes/rl-2105.xml
index cf9e0609723..723f928ec35 100644
--- a/nixos/doc/manual/release-notes/rl-2105.xml
+++ b/nixos/doc/manual/release-notes/rl-2105.xml
@@ -840,6 +840,13 @@ environment.systemPackages = [
     </para>
    </listitem>
    <listitem>
+    <para>
+      When defining a new user, one of <xref linkend="opt-users.users._name_.isNormalUser" /> and <xref linkend="opt-users.users._name_.isSystemUser" /> is now required.
+      This is to prevent accidentally giving a UID above 1000 to system users, which could have unexpected consequences, like running user activation scripts for system users.
+      Note that users defined with an explicit UID below 500 are exempted from this check, as <xref linkend="opt-users.users._name_.isSystemUser" /> has no effect for those.
+    </para>
+   </listitem>
+   <listitem>
      <para>
        The GNOME desktop manager once again installs <package>gnome3.epiphany</package> by default.
      </para>
diff --git a/nixos/modules/config/pulseaudio.nix b/nixos/modules/config/pulseaudio.nix
index c0e90a8c26e..0266bbfb121 100644
--- a/nixos/modules/config/pulseaudio.nix
+++ b/nixos/modules/config/pulseaudio.nix
@@ -306,6 +306,7 @@ in {
         description = "PulseAudio system service user";
         home = stateDir;
         createHome = true;
+        isSystemUser = true;
       };
 
       users.groups.pulse.gid = gid;
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index 4a2647339c5..2b6a61e9a80 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -92,6 +92,8 @@ let
           the user's UID is allocated in the range for system users
           (below 500) or in the range for normal users (starting at
           1000).
+          Exactly one of <literal>isNormalUser</literal> and
+          <literal>isSystemUser</literal> must be true.
         '';
       };
 
@@ -107,6 +109,8 @@ let
           <option>useDefaultShell</option> to <literal>true</literal>,
           and <option>isSystemUser</option> to
           <literal>false</literal>.
+          Exactly one of <literal>isNormalUser</literal> and
+          <literal>isSystemUser</literal> must be true.
         '';
       };
 
@@ -521,6 +525,7 @@ in {
       };
       nobody = {
         uid = ids.uids.nobody;
+        isSystemUser = true;
         description = "Unprivileged account (don't use!)";
         group = "nogroup";
       };
@@ -608,17 +613,28 @@ in {
           Neither the root account nor any wheel user has a password or SSH authorized key.
           You must set one to prevent being locked out of your system.'';
       }
-    ] ++ flip mapAttrsToList cfg.users (name: user:
-      {
+    ] ++ flatten (flip mapAttrsToList cfg.users (name: user:
+      [
+        {
         assertion = (user.hashedPassword != null)
-                    -> (builtins.match ".*:.*" user.hashedPassword == null);
+        -> (builtins.match ".*:.*" user.hashedPassword == null);
         message = ''
-          The password hash of user "${user.name}" contains a ":" character.
-          This is invalid and would break the login system because the fields
-          of /etc/shadow (file where hashes are stored) are colon-separated.
-          Please check the value of option `users.users."${user.name}".hashedPassword`.'';
-      }
-    );
+            The password hash of user "${user.name}" contains a ":" character.
+            This is invalid and would break the login system because the fields
+            of /etc/shadow (file where hashes are stored) are colon-separated.
+            Please check the value of option `users.users."${user.name}".hashedPassword`.'';
+          }
+          {
+            assertion = let
+              xor = a: b: a && !b || b && !a;
+              isEffectivelySystemUser = user.isSystemUser || (user.uid != null && user.uid < 500);
+            in xor isEffectivelySystemUser user.isNormalUser;
+            message = ''
+              Exactly one of users.users.${user.name}.isSystemUser and users.users.${user.name}.isNormalUser must be set.
+            '';
+          }
+        ]
+    ));
 
     warnings =
       builtins.filter (x: x != null) (
diff --git a/nixos/modules/services/backup/borgbackup.nix b/nixos/modules/services/backup/borgbackup.nix
index be661b201f0..18fb29fd72a 100644
--- a/nixos/modules/services/backup/borgbackup.nix
+++ b/nixos/modules/services/backup/borgbackup.nix
@@ -169,6 +169,7 @@ let
         (map (mkAuthorizedKey cfg false) cfg.authorizedKeys
         ++ map (mkAuthorizedKey cfg true) cfg.authorizedKeysAppendOnly);
       useDefaultShell = true;
+      isSystemUser = true;
     };
     groups.${cfg.group} = { };
   };
diff --git a/nixos/modules/services/databases/pgmanage.nix b/nixos/modules/services/databases/pgmanage.nix
index 0f8634dab31..8508e76b5cd 100644
--- a/nixos/modules/services/databases/pgmanage.nix
+++ b/nixos/modules/services/databases/pgmanage.nix
@@ -197,6 +197,7 @@ in {
         group = pgmanage;
         home  = cfg.sqlRoot;
         createHome = true;
+        isSystemUser = true;
       };
       groups.${pgmanage} = {
         name = pgmanage;
diff --git a/nixos/modules/services/misc/bazarr.nix b/nixos/modules/services/misc/bazarr.nix
index d3fd5b08cc8..99343a146a7 100644
--- a/nixos/modules/services/misc/bazarr.nix
+++ b/nixos/modules/services/misc/bazarr.nix
@@ -64,6 +64,7 @@ in
 
     users.users = mkIf (cfg.user == "bazarr") {
       bazarr = {
+        isSystemUser = true;
         group = cfg.group;
         home = "/var/lib/${config.systemd.services.bazarr.serviceConfig.StateDirectory}";
       };
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index 64bdbf159d5..133e96da0ec 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -21,6 +21,7 @@ let
          calls in `libstore/build.cc', don't add any supplementary group
          here except "nixbld".  */
       uid = builtins.add config.ids.uids.nixbld nr;
+      isSystemUser = true;
       group = "nixbld";
       extraGroups = [ "nixbld" ];
     };
diff --git a/nixos/modules/services/monitoring/tuptime.nix b/nixos/modules/services/monitoring/tuptime.nix
index 8f79d916599..17c5c1f56ea 100644
--- a/nixos/modules/services/monitoring/tuptime.nix
+++ b/nixos/modules/services/monitoring/tuptime.nix
@@ -34,7 +34,10 @@ in {
 
     users = {
       groups._tuptime.members = [ "_tuptime" ];
-      users._tuptime.description = "tuptime database owner";
+      users._tuptime = {
+        isSystemUser = true;
+        description = "tuptime database owner";
+      };
     };
 
     systemd = {
diff --git a/nixos/modules/services/networking/bird.nix b/nixos/modules/services/networking/bird.nix
index 6d7e7760d94..1923afdf83f 100644
--- a/nixos/modules/services/networking/bird.nix
+++ b/nixos/modules/services/networking/bird.nix
@@ -73,6 +73,7 @@ let
           users.${variant} = {
             description = "BIRD Internet Routing Daemon user";
             group = variant;
+            isSystemUser = true;
           };
           groups.${variant} = {};
         };
diff --git a/nixos/modules/services/networking/ncdns.nix b/nixos/modules/services/networking/ncdns.nix
index c1832ad1752..d30fe0f6f6d 100644
--- a/nixos/modules/services/networking/ncdns.nix
+++ b/nixos/modules/services/networking/ncdns.nix
@@ -243,8 +243,10 @@ in
         xlog.journal = true;
     };
 
-    users.users.ncdns =
-      { description = "ncdns daemon user"; };
+    users.users.ncdns = {
+      isSystemUser = true;
+      description = "ncdns daemon user";
+    };
 
     systemd.services.ncdns = {
       description = "ncdns daemon";
diff --git a/nixos/modules/services/networking/pixiecore.nix b/nixos/modules/services/networking/pixiecore.nix
index 85aa40784af..d2642c82c2d 100644
--- a/nixos/modules/services/networking/pixiecore.nix
+++ b/nixos/modules/services/networking/pixiecore.nix
@@ -93,6 +93,7 @@ in
     users.users.pixiecore = {
       description = "Pixiecore daemon user";
       group = "pixiecore";
+      isSystemUser = true;
     };
 
     networking.firewall = mkIf cfg.openFirewall {
diff --git a/nixos/modules/services/networking/pleroma.nix b/nixos/modules/services/networking/pleroma.nix
index 9b2bf9f6124..2687230a158 100644
--- a/nixos/modules/services/networking/pleroma.nix
+++ b/nixos/modules/services/networking/pleroma.nix
@@ -75,6 +75,7 @@ in {
         description = "Pleroma user";
         home = cfg.stateDir;
         extraGroups = [ cfg.group ];
+        isSystemUser = true;
       };
       groups."${cfg.group}" = {};
     };
diff --git a/nixos/modules/services/security/privacyidea.nix b/nixos/modules/services/security/privacyidea.nix
index f7b40089a93..2696dca4c76 100644
--- a/nixos/modules/services/security/privacyidea.nix
+++ b/nixos/modules/services/security/privacyidea.nix
@@ -264,6 +264,7 @@ in
 
       users.users.privacyidea = mkIf (cfg.user == "privacyidea") {
         group = cfg.group;
+        isSystemUser = true;
       };
 
       users.groups.privacyidea = mkIf (cfg.group == "privacyidea") {};
@@ -294,6 +295,7 @@ in
 
       users.users.pi-ldap-proxy = mkIf (cfg.ldap-proxy.user == "pi-ldap-proxy") {
         group = cfg.ldap-proxy.group;
+        isSystemUser = true;
       };
 
       users.groups.pi-ldap-proxy = mkIf (cfg.ldap-proxy.group == "pi-ldap-proxy") {};
diff --git a/nixos/modules/services/web-apps/nextcloud.nix b/nixos/modules/services/web-apps/nextcloud.nix
index 9a541aba6e4..58e8e5a0a8b 100644
--- a/nixos/modules/services/web-apps/nextcloud.nix
+++ b/nixos/modules/services/web-apps/nextcloud.nix
@@ -607,6 +607,7 @@ in {
         home = "${cfg.home}";
         group = "nextcloud";
         createHome = true;
+        isSystemUser = true;
       };
       users.groups.nextcloud.members = [ "nextcloud" config.services.nginx.user ];
 
diff --git a/nixos/tests/mysql/mariadb-galera-mariabackup.nix b/nixos/tests/mysql/mariadb-galera-mariabackup.nix
index 0a40c010a47..1c73bc854a5 100644
--- a/nixos/tests/mysql/mariadb-galera-mariabackup.nix
+++ b/nixos/tests/mysql/mariadb-galera-mariabackup.nix
@@ -31,7 +31,7 @@ in {
         firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
         firewall.allowedUDPPorts = [ 4567 ];
       };
-      users.users.testuser = { };
+      users.users.testuser = { isSystemUser = true; };
       systemd.services.mysql = with pkgs; {
         path = [ mysqlenv-common mysqlenv-mariabackup ];
       };
@@ -89,7 +89,7 @@ in {
         firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
         firewall.allowedUDPPorts = [ 4567 ];
       };
-      users.users.testuser = { };
+      users.users.testuser = { isSystemUser = true; };
       systemd.services.mysql = with pkgs; {
         path = [ mysqlenv-common mysqlenv-mariabackup ];
       };
@@ -136,7 +136,7 @@ in {
         firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
         firewall.allowedUDPPorts = [ 4567 ];
       };
-      users.users.testuser = { };
+      users.users.testuser = { isSystemUser = true; };
       systemd.services.mysql = with pkgs; {
         path = [ mysqlenv-common mysqlenv-mariabackup ];
       };
diff --git a/nixos/tests/mysql/mariadb-galera-rsync.nix b/nixos/tests/mysql/mariadb-galera-rsync.nix
index 6fb3cfef8d7..709a8b5085c 100644
--- a/nixos/tests/mysql/mariadb-galera-rsync.nix
+++ b/nixos/tests/mysql/mariadb-galera-rsync.nix
@@ -31,7 +31,7 @@ in {
         firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
         firewall.allowedUDPPorts = [ 4567 ];
       };
-      users.users.testuser = { };
+      users.users.testuser = { isSystemUser = true; };
       systemd.services.mysql = with pkgs; {
         path = [ mysqlenv-common mysqlenv-rsync ];
       };
@@ -84,7 +84,7 @@ in {
         firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
         firewall.allowedUDPPorts = [ 4567 ];
       };
-      users.users.testuser = { };
+      users.users.testuser = { isSystemUser = true; };
       systemd.services.mysql = with pkgs; {
         path = [ mysqlenv-common mysqlenv-rsync ];
       };
@@ -130,7 +130,7 @@ in {
         firewall.allowedTCPPorts = [ 3306 4444 4567 4568 ];
         firewall.allowedUDPPorts = [ 4567 ];
       };
-      users.users.testuser = { };
+      users.users.testuser = { isSystemUser = true; };
       systemd.services.mysql = with pkgs; {
         path = [ mysqlenv-common mysqlenv-rsync ];
       };
diff --git a/nixos/tests/mysql/mysql.nix b/nixos/tests/mysql/mysql.nix
index 50ad5c68aef..c21136416d4 100644
--- a/nixos/tests/mysql/mysql.nix
+++ b/nixos/tests/mysql/mysql.nix
@@ -9,8 +9,8 @@ import ./../make-test-python.nix ({ pkgs, ...} : {
       { pkgs, ... }:
 
       {
-        users.users.testuser = { };
-        users.users.testuser2 = { };
+        users.users.testuser = { isSystemUser = true; };
+        users.users.testuser2 = { isSystemUser = true; };
         services.mysql.enable = true;
         services.mysql.initialDatabases = [
           { name = "testdb3"; schema = ./testdb.sql; }
@@ -44,8 +44,8 @@ import ./../make-test-python.nix ({ pkgs, ...} : {
         # Kernel panic - not syncing: Out of memory: compulsory panic_on_oom is enabled
         virtualisation.memorySize = 1024;
 
-        users.users.testuser = { };
-        users.users.testuser2 = { };
+        users.users.testuser = { isSystemUser = true; };
+        users.users.testuser2 = { isSystemUser = true; };
         services.mysql.enable = true;
         services.mysql.initialDatabases = [
           { name = "testdb3"; schema = ./testdb.sql; }
@@ -75,8 +75,8 @@ import ./../make-test-python.nix ({ pkgs, ...} : {
       { pkgs, ... }:
 
       {
-        users.users.testuser = { };
-        users.users.testuser2 = { };
+        users.users.testuser = { isSystemUser = true; };
+        users.users.testuser2 = { isSystemUser = true; };
         services.mysql.enable = true;
         services.mysql.initialScript = pkgs.writeText "mariadb-init.sql" ''
           ALTER USER root@localhost IDENTIFIED WITH unix_socket;
diff --git a/nixos/tests/redis.nix b/nixos/tests/redis.nix
index 79a7847414a..28b6058c2c0 100644
--- a/nixos/tests/redis.nix
+++ b/nixos/tests/redis.nix
@@ -22,11 +22,10 @@ in
         users.users."member" = {
           createHome = false;
           description = "A member of the redis group";
+          isNormalUser = true;
           extraGroups = [
             "redis"
           ];
-          group = "users";
-          shell = "/bin/sh";
         };
       };
   };
diff --git a/nixos/tests/rspamd.nix b/nixos/tests/rspamd.nix
index 7f41e1a7956..f0ccfe7ea0e 100644
--- a/nixos/tests/rspamd.nix
+++ b/nixos/tests/rspamd.nix
@@ -274,7 +274,10 @@ in
 
         I find cows to be evil don't you?
       '';
-      users.users.tester.password = "test";
+      users.users.tester = {
+        isNormalUser = true;
+        password = "test";
+      };
       services.postfix = {
         enable = true;
         destination = ["example.com"];
diff --git a/nixos/tests/shadow.nix b/nixos/tests/shadow.nix
index e5755e8e087..c51961e1fc6 100644
--- a/nixos/tests/shadow.nix
+++ b/nixos/tests/shadow.nix
@@ -13,14 +13,17 @@ in import ./make-test-python.nix ({ pkgs, ... }: {
     users = {
       mutableUsers = true;
       users.emma = {
+        isNormalUser = true;
         password = password1;
         shell = pkgs.bash;
       };
       users.layla = {
+        isNormalUser = true;
         password = password2;
         shell = pkgs.shadow;
       };
       users.ash = {
+        isNormalUser = true;
         password = password4;
         shell = pkgs.bash;
       };
diff --git a/nixos/tests/systemd-confinement.nix b/nixos/tests/systemd-confinement.nix
index ebf6d218fd6..d04e4a3f867 100644
--- a/nixos/tests/systemd-confinement.nix
+++ b/nixos/tests/systemd-confinement.nix
@@ -150,6 +150,7 @@ import ./make-test-python.nix {
 
     config.users.groups.chroot-testgroup = {};
     config.users.users.chroot-testuser = {
+      isSystemUser = true;
       description = "Chroot Test User";
       group = "chroot-testgroup";
     };
diff --git a/nixos/tests/unbound.nix b/nixos/tests/unbound.nix
index d4b8bb15ced..ca9718ac633 100644
--- a/nixos/tests/unbound.nix
+++ b/nixos/tests/unbound.nix
@@ -132,12 +132,15 @@ import ./make-test-python.nix ({ pkgs, lib, ... }:
 
         users.users = {
           # user that is permitted to access the unix socket
-          someuser.extraGroups = [
-            config.users.users.unbound.group
-          ];
+          someuser = {
+            isSystemUser = true;
+            extraGroups = [
+              config.users.users.unbound.group
+            ];
+          };
 
           # user that is not permitted to access the unix socket
-          unauthorizeduser = {};
+          unauthorizeduser = { isSystemUser = true; };
         };
 
         environment.etc = {