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.
This commit is contained in:
parent
71ab7472d3
commit
bf129a2c23
@ -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,15 +447,30 @@ 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
|
||||
@ -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";
|
||||
}
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user