summary refs log tree commit diff
path: root/nixos/modules/config
diff options
context:
space:
mode:
authorRickard Nilsson <rickynils@gmail.com>2014-04-06 12:39:51 +0200
committerRickard Nilsson <rickynils@gmail.com>2014-04-06 12:42:55 +0200
commitbf129a2c238e5ffe99e7778851042c16ba467496 (patch)
treea005e9ce09dda93b74c2aec8ae2b909f07f056ca /nixos/modules/config
parent71ab7472d3bdb85e36316bd0f8a7b31985b8dd83 (diff)
downloadnixlib-bf129a2c238e5ffe99e7778851042c16ba467496.tar
nixlib-bf129a2c238e5ffe99e7778851042c16ba467496.tar.gz
nixlib-bf129a2c238e5ffe99e7778851042c16ba467496.tar.bz2
nixlib-bf129a2c238e5ffe99e7778851042c16ba467496.tar.lz
nixlib-bf129a2c238e5ffe99e7778851042c16ba467496.tar.xz
nixlib-bf129a2c238e5ffe99e7778851042c16ba467496.tar.zst
nixlib-bf129a2c238e5ffe99e7778851042c16ba467496.zip
Allow undefined uids and gids when mutableUsers = true
Groups and users without gid/uid are created with
useradd/groupadd after the passwd/group merge phase
if mutableUsers = true.

This should fix #2114.
Diffstat (limited to 'nixos/modules/config')
-rw-r--r--nixos/modules/config/users-groups.nix96
1 files changed, 73 insertions, 23 deletions
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index 2d9b941a2cae..c5d44223ecf8 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -7,6 +7,9 @@ let
   ids = config.ids;
   cfg = config.users;
 
+  nonUidUsers = filterAttrs (n: u: u.uid == null) cfg.extraUsers;
+  nonGidGroups = filterAttrs (n: g: g.gid == null) cfg.extraGroups;
+
   passwordDescription = ''
     The options <literal>hashedPassword</literal>,
     <literal>password</literal> and <literal>passwordFile</literal>
@@ -31,7 +34,10 @@ let
 
       name = mkOption {
         type = types.str;
-        description = "The name of the user account. If undefined, the name of the attribute set will be used.";
+        description = ''
+          The name of the user account. If undefined, the name of the
+          attribute set will be used.
+        '';
       };
 
       description = mkOption {
@@ -46,8 +52,14 @@ let
       };
 
       uid = mkOption {
-        type = with types; uniq int;
-        description = "The account UID.";
+        type = with types; nullOr int;
+        default = null;
+        description = ''
+          The account UID. If the <literal>mutableUsers</literal> option
+          is false, the UID cannot be null. Otherwise, the UID might be
+          null, in which case a free UID is picked on activation (by the
+          useradd command).
+        '';
       };
 
       group = mkOption {
@@ -151,12 +163,21 @@ let
 
       name = mkOption {
         type = types.str;
-        description = "The name of the group. If undefined, the name of the attribute set will be used.";
+        description = ''
+          The name of the group. If undefined, the name of the attribute set
+          will be used.
+        '';
       };
 
       gid = mkOption {
-        type = with types; uniq int;
-        description = "The GID of the group.";
+        type = with types; nullOr int;
+        default = null;
+        description = ''
+          The group GID. If the <literal>mutableUsers</literal> option
+          is false, the GID cannot be null. Otherwise, the GID might be
+          null, in which case a free GID is picked on activation (by the
+          groupadd command).
+        '';
       };
 
       members = mkOption {
@@ -218,13 +239,15 @@ let
 
   groupFile = pkgs.writeText "group" (
     concatStringsSep "\n" (map (g: mkGroupEntry g.name) (
-      sortOn "gid" (attrValues cfg.extraGroups)
+      let f = g: g.gid != null; in
+        sortOn "gid" (filter f (attrValues cfg.extraGroups))
     ))
   );
 
   passwdFile = pkgs.writeText "passwd" (
     concatStringsSep "\n" (map (u: mkPasswdEntry u.name) (
-      sortOn "uid" (filter (u: u.createUser) (attrValues cfg.extraUsers))
+      let f = u: u.createUser && (u.uid != null); in
+        sortOn "uid" (filter f (attrValues cfg.extraUsers))
     ))
   );
 
@@ -261,11 +284,11 @@ let
       then builtins.trace "Duplicate ${idAttr} ${id}" { dup = true; acc = null; }
       else { dup = false; acc = newAcc; }
     ) { dup = false; acc = {}; } (builtins.attrNames set)).dup;
-  uidsAreUnique = idsAreUnique cfg.extraUsers "uid";
-  gidsAreUnique = idsAreUnique cfg.extraGroups "gid";
-in
 
-{
+  uidsAreUnique = idsAreUnique (filterAttrs (n: u: u.uid != null) cfg.extraUsers) "uid";
+  gidsAreUnique = idsAreUnique (filterAttrs (n: g: g.gid != null) cfg.extraGroups) "gid";
+
+in {
 
   ###### interface
 
@@ -424,16 +447,31 @@ in
             }
           fi
         '';
-        mkhome = n: u:
-         let
-            uid = toString u.uid;
-            gid = toString ((getGroup u.group).gid);
-            h = u.home;
-          in ''
-            test -a "${h}" || mkdir -p "${h}" || true
-            test "$(stat -c %u "${h}")" = ${uid} || chown ${uid} "${h}" || true
-            test "$(stat -c %g "${h}")" = ${gid} || chgrp ${gid} "${h}" || true
-          '';
+        mkhome = n: u: ''
+          uid="$(id -u ${u.name})"
+          gid="$(id -g ${u.name})"
+          h="${u.home}"
+          test -a "$h" || mkdir -p "$h" || true
+          test "$(stat -c %u "$h")" = $uid || chown $uid "$h" || true
+          test "$(stat -c %g "$h")" = $gid || chgrp $gid "$h" || true
+        '';
+        groupadd = n: g: ''
+          if [ -z "$(getent group "${g.name}")" ]; then
+            echo "Adding group ${g.name}"
+            ${pkgs.shadow}/sbin/groupadd "${g.name}"
+          fi
+        '';
+        useradd = n: u: ''
+          if ! id "${u.name}" &>/dev/null; then
+            echo "Adding user ${u.name}"
+            ${pkgs.shadow}/sbin/useradd \
+              -g "${u.group}" \
+              -s "${u.shell}" \
+              -d "${u.home}" \
+              "${u.name}"
+            echo "${u.name}:x" | ${pkgs.shadow}/sbin/chpasswd -e
+          fi
+        '';
       in stringAfter [ "etc" ] ''
         touch /etc/group
         touch /etc/passwd
@@ -441,6 +479,8 @@ in
         VISUAL=${merger passwdFile} ${pkgs.shadow}/sbin/vipw &>/dev/null
         ${pkgs.shadow}/sbin/grpconv
         ${pkgs.shadow}/sbin/pwconv
+        ${concatStrings (mapAttrsToList groupadd nonGidGroups)}
+        ${concatStrings (mapAttrsToList useradd nonUidUsers)}
         ${concatStrings (mapAttrsToList mkhome mkhomeUsers)}
         ${concatStrings (mapAttrsToList setpw setpwUsers)}
       '';
@@ -448,7 +488,17 @@ in
     # for backwards compatibility
     system.activationScripts.groups = stringAfter [ "users" ] "";
 
-    assertions = [ { assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique); message = "uids and gids must be unique!"; } ];
+    assertions = [
+      { assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique);
+        message = "uids and gids must be unique!";
+      }
+      { assertion = cfg.mutableUsers || (nonUidUsers == {});
+        message = "When mutableUsers is false, no uid can be null";
+      }
+      { assertion = cfg.mutableUsers || (nonGidGroups == {});
+        message = "When mutableUsers is false, no gid can be null";
+      }
+    ];
 
   };