2009-01-02 16:07:01 +00:00
|
|
|
|
{pkgs, config, ...}:
|
2007-06-08 16:41:12 +01:00
|
|
|
|
|
2009-09-02 18:35:24 +01:00
|
|
|
|
with pkgs.lib;
|
2009-01-02 16:07:01 +00:00
|
|
|
|
|
|
|
|
|
let
|
2009-09-02 18:35:24 +01:00
|
|
|
|
|
2009-05-29 15:25:56 +01:00
|
|
|
|
ids = config.ids;
|
2011-11-29 06:08:55 +00:00
|
|
|
|
users = config.users;
|
2007-06-08 16:41:12 +01:00
|
|
|
|
|
2012-04-20 13:55:09 +01:00
|
|
|
|
userOpts = { name, config, ... }: {
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
options = {
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
name = mkOption {
|
2013-10-30 10:02:04 +00:00
|
|
|
|
type = types.str;
|
2011-11-29 06:08:55 +00:00
|
|
|
|
description = "The name of the user account. If undefined, the name of the attribute set will be used.";
|
|
|
|
|
};
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
description = mkOption {
|
2013-10-30 10:02:04 +00:00
|
|
|
|
type = types.str;
|
2011-11-29 06:08:55 +00:00
|
|
|
|
default = "";
|
2013-10-31 07:41:51 +00:00
|
|
|
|
example = "Alice Q. User";
|
|
|
|
|
description = ''
|
|
|
|
|
A short description of the user account, typically the
|
|
|
|
|
user's full name. This is actually the “GECOS” or “comment”
|
|
|
|
|
field in <filename>/etc/passwd</filename>.
|
|
|
|
|
'';
|
2011-11-29 06:08:55 +00:00
|
|
|
|
};
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
uid = mkOption {
|
|
|
|
|
type = with types; uniq (nullOr int);
|
|
|
|
|
default = null;
|
2012-04-20 13:55:09 +01:00
|
|
|
|
description = "The account UID. If undefined, NixOS will select a free UID.";
|
2011-11-29 06:08:55 +00:00
|
|
|
|
};
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
group = mkOption {
|
2013-10-30 10:02:04 +00:00
|
|
|
|
type = types.str;
|
2011-11-29 06:08:55 +00:00
|
|
|
|
default = "nogroup";
|
|
|
|
|
description = "The user's primary group.";
|
|
|
|
|
};
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
extraGroups = mkOption {
|
2013-10-30 16:37:45 +00:00
|
|
|
|
type = types.listOf types.str;
|
2011-11-29 06:08:55 +00:00
|
|
|
|
default = [];
|
|
|
|
|
description = "The user's auxiliary groups.";
|
|
|
|
|
};
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
home = mkOption {
|
2013-10-30 10:02:04 +00:00
|
|
|
|
type = types.str;
|
2011-11-29 06:08:55 +00:00
|
|
|
|
default = "/var/empty";
|
|
|
|
|
description = "The user's home directory.";
|
|
|
|
|
};
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
shell = mkOption {
|
2013-10-30 10:02:04 +00:00
|
|
|
|
type = types.str;
|
2012-07-16 16:27:59 +01:00
|
|
|
|
default = "/run/current-system/sw/sbin/nologin";
|
2011-11-29 06:08:55 +00:00
|
|
|
|
description = "The path to the user's shell.";
|
|
|
|
|
};
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
createHome = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = false;
|
|
|
|
|
description = "If true, the home directory will be created automatically.";
|
|
|
|
|
};
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
useDefaultShell = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = false;
|
|
|
|
|
description = "If true, the user's shell will be set to <literal>users.defaultUserShell</literal>.";
|
|
|
|
|
};
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
password = mkOption {
|
2013-10-30 16:37:45 +00:00
|
|
|
|
type = with types; uniq (nullOr str);
|
2011-11-29 06:08:55 +00:00
|
|
|
|
default = null;
|
2013-10-31 07:41:51 +00:00
|
|
|
|
description = ''
|
|
|
|
|
The user's password. If undefined, no password is set for
|
|
|
|
|
the user. Warning: do not set confidential information here
|
|
|
|
|
because it is world-readable in the Nix store. This option
|
|
|
|
|
should only be used for public accounts such as
|
|
|
|
|
<literal>guest</literal>.
|
|
|
|
|
'';
|
2011-11-29 06:08:55 +00:00
|
|
|
|
};
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
isSystemUser = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = true;
|
|
|
|
|
description = "Indicates if the user is a system user or not.";
|
|
|
|
|
};
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
createUser = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = true;
|
2013-10-31 07:41:51 +00:00
|
|
|
|
description = ''
|
2011-11-29 06:08:55 +00:00
|
|
|
|
Indicates if the user should be created automatically as a local user.
|
|
|
|
|
Set this to false if the user for instance is an LDAP user. NixOS will
|
|
|
|
|
then not modify any of the basic properties for the user account.
|
2013-10-31 07:41:51 +00:00
|
|
|
|
'';
|
2011-11-29 06:08:55 +00:00
|
|
|
|
};
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
|
|
|
|
isAlias = mkOption {
|
|
|
|
|
type = types.bool;
|
|
|
|
|
default = false;
|
|
|
|
|
description = "If true, the UID of this user is not required to be unique and can thus alias another user.";
|
|
|
|
|
};
|
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
};
|
2007-06-08 16:41:12 +01:00
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
config = {
|
|
|
|
|
name = mkDefault name;
|
|
|
|
|
uid = mkDefault (attrByPath [name] null ids.uids);
|
|
|
|
|
shell = mkIf config.useDefaultShell (mkDefault users.defaultUserShell);
|
|
|
|
|
};
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
};
|
2007-06-08 16:41:12 +01:00
|
|
|
|
|
2012-04-20 13:55:09 +01:00
|
|
|
|
groupOpts = { name, config, ... }: {
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2012-04-20 13:55:09 +01:00
|
|
|
|
options = {
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2012-04-20 13:55:09 +01:00
|
|
|
|
name = mkOption {
|
2013-10-30 10:02:04 +00:00
|
|
|
|
type = types.str;
|
2012-04-20 13:55:09 +01:00
|
|
|
|
description = "The name of the group. If undefined, the name of the attribute set will be used.";
|
|
|
|
|
};
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2012-04-20 13:55:09 +01:00
|
|
|
|
gid = mkOption {
|
|
|
|
|
type = with types; uniq (nullOr int);
|
|
|
|
|
default = null;
|
|
|
|
|
description = "The GID of the group. If undefined, NixOS will select a free GID.";
|
|
|
|
|
};
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2012-04-20 13:55:09 +01:00
|
|
|
|
};
|
2007-06-08 16:41:12 +01:00
|
|
|
|
|
2012-04-20 13:55:09 +01:00
|
|
|
|
config = {
|
|
|
|
|
name = mkDefault name;
|
|
|
|
|
gid = mkDefault (attrByPath [name] null ids.gids);
|
|
|
|
|
};
|
2012-10-23 12:35:06 +01:00
|
|
|
|
|
2012-04-20 13:55:09 +01:00
|
|
|
|
};
|
2009-01-02 16:07:01 +00:00
|
|
|
|
|
2009-09-02 18:35:24 +01:00
|
|
|
|
# Note: the 'X' in front of the password is to distinguish between
|
|
|
|
|
# having an empty password, and not having a password.
|
2012-10-23 12:35:06 +01:00
|
|
|
|
serializedUser = u: "${u.name}\n${u.description}\n${if u.uid != null then toString u.uid else ""}\n${u.group}\n${toString (concatStringsSep "," u.extraGroups)}\n${u.home}\n${u.shell}\n${toString u.createHome}\n${if u.password != null then "X" + u.password else ""}\n${toString u.isSystemUser}\n${toString u.createUser}\n${toString u.isAlias}\n";
|
2011-11-29 06:08:55 +00:00
|
|
|
|
|
|
|
|
|
usersFile = pkgs.writeText "users" (
|
2012-10-23 12:35:06 +01:00
|
|
|
|
let
|
|
|
|
|
p = partition (u: u.isAlias) (attrValues config.users.extraUsers);
|
|
|
|
|
in concatStrings (map serializedUser p.wrong ++ map serializedUser p.right));
|
2011-09-14 19:20:50 +01:00
|
|
|
|
|
2009-01-02 16:07:01 +00:00
|
|
|
|
in
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
2009-09-02 18:35:24 +01:00
|
|
|
|
###### interface
|
2009-01-02 16:07:01 +00:00
|
|
|
|
|
2009-09-02 18:35:24 +01:00
|
|
|
|
options = {
|
2011-09-14 19:20:50 +01:00
|
|
|
|
|
2009-09-02 18:35:24 +01:00
|
|
|
|
users.extraUsers = mkOption {
|
2011-11-29 06:08:55 +00:00
|
|
|
|
default = {};
|
|
|
|
|
type = types.loaOf types.optionSet;
|
|
|
|
|
example = {
|
|
|
|
|
alice = {
|
|
|
|
|
uid = 1234;
|
2013-10-31 07:41:51 +00:00
|
|
|
|
description = "Alice Q. User";
|
2011-11-29 06:08:55 +00:00
|
|
|
|
home = "/home/alice";
|
|
|
|
|
createHome = true;
|
|
|
|
|
group = "users";
|
|
|
|
|
extraGroups = ["wheel"];
|
|
|
|
|
shell = "/bin/sh";
|
|
|
|
|
};
|
|
|
|
|
};
|
2009-09-02 18:35:24 +01:00
|
|
|
|
description = ''
|
|
|
|
|
Additional user accounts to be created automatically by the system.
|
2013-08-09 02:23:22 +01:00
|
|
|
|
This can also be used to set options for root.
|
2009-09-02 18:35:24 +01:00
|
|
|
|
'';
|
2011-11-29 06:08:55 +00:00
|
|
|
|
options = [ userOpts ];
|
2009-09-02 18:35:24 +01:00
|
|
|
|
};
|
2009-01-02 16:07:01 +00:00
|
|
|
|
|
2009-09-02 18:35:24 +01:00
|
|
|
|
users.extraGroups = mkOption {
|
2012-04-20 13:55:09 +01:00
|
|
|
|
default = {};
|
2009-09-02 18:35:24 +01:00
|
|
|
|
example =
|
2012-04-20 13:55:09 +01:00
|
|
|
|
{ students.gid = 1001;
|
|
|
|
|
hackers = { };
|
|
|
|
|
};
|
|
|
|
|
type = types.loaOf types.optionSet;
|
2009-09-02 18:35:24 +01:00
|
|
|
|
description = ''
|
|
|
|
|
Additional groups to be created automatically by the system.
|
|
|
|
|
'';
|
2012-04-20 13:55:09 +01:00
|
|
|
|
options = [ groupOpts ];
|
2009-09-02 18:35:24 +01:00
|
|
|
|
};
|
|
|
|
|
|
2013-11-01 13:45:56 +00:00
|
|
|
|
security.initialRootPassword = mkOption {
|
|
|
|
|
type = types.str;
|
|
|
|
|
default = "";
|
|
|
|
|
example = "!";
|
|
|
|
|
description = ''
|
|
|
|
|
The (hashed) password for the root account set on initial
|
|
|
|
|
installation. The empty string denotes that root can login
|
|
|
|
|
locally without a password (but not via remote services such
|
|
|
|
|
as SSH, or indirectly via <command>su</command> or
|
|
|
|
|
<command>sudo</command>). The string <literal>!</literal>
|
|
|
|
|
prevents root from logging in using a password.
|
|
|
|
|
'';
|
|
|
|
|
};
|
|
|
|
|
|
2009-09-02 18:35:24 +01:00
|
|
|
|
};
|
2011-09-14 19:20:50 +01:00
|
|
|
|
|
2009-09-02 18:35:24 +01:00
|
|
|
|
|
|
|
|
|
###### implementation
|
|
|
|
|
|
|
|
|
|
config = {
|
|
|
|
|
|
2011-11-29 06:08:55 +00:00
|
|
|
|
users.extraUsers = {
|
|
|
|
|
root = {
|
|
|
|
|
description = "System administrator";
|
|
|
|
|
home = "/root";
|
|
|
|
|
shell = config.users.defaultUserShell;
|
|
|
|
|
group = "root";
|
|
|
|
|
};
|
|
|
|
|
nobody = {
|
|
|
|
|
description = "Unprivileged account (don't use!)";
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
2012-04-20 13:55:09 +01:00
|
|
|
|
users.extraGroups = {
|
|
|
|
|
root = { };
|
|
|
|
|
wheel = { };
|
|
|
|
|
disk = { };
|
|
|
|
|
kmem = { };
|
|
|
|
|
tty = { };
|
|
|
|
|
floppy = { };
|
|
|
|
|
uucp = { };
|
|
|
|
|
lp = { };
|
|
|
|
|
cdrom = { };
|
|
|
|
|
tape = { };
|
|
|
|
|
audio = { };
|
|
|
|
|
video = { };
|
|
|
|
|
dialout = { };
|
|
|
|
|
nogroup = { };
|
|
|
|
|
users = { };
|
|
|
|
|
nixbld = { };
|
|
|
|
|
utmp = { };
|
2012-08-10 20:25:04 +01:00
|
|
|
|
adm = { }; # expected by journald
|
2012-04-20 13:55:09 +01:00
|
|
|
|
};
|
|
|
|
|
|
2010-09-13 16:41:38 +01:00
|
|
|
|
system.activationScripts.rootPasswd = stringAfter [ "etc" ]
|
|
|
|
|
''
|
|
|
|
|
# If there is no password file yet, create a root account with an
|
|
|
|
|
# empty password.
|
|
|
|
|
if ! test -e /etc/passwd; then
|
|
|
|
|
rootHome=/root
|
|
|
|
|
touch /etc/passwd; chmod 0644 /etc/passwd
|
|
|
|
|
touch /etc/group; chmod 0644 /etc/group
|
|
|
|
|
touch /etc/shadow; chmod 0600 /etc/shadow
|
|
|
|
|
# Can't use useradd, since it complains that it doesn't know us
|
|
|
|
|
# (bootstrap problem!).
|
|
|
|
|
echo "root:x:0:0:System administrator:$rootHome:${config.users.defaultUserShell}" >> /etc/passwd
|
2013-11-01 13:45:56 +00:00
|
|
|
|
echo "root:${config.security.initialRootPassword}:::::::" >> /etc/shadow
|
2010-09-13 16:41:38 +01:00
|
|
|
|
fi
|
|
|
|
|
'';
|
|
|
|
|
|
2013-11-01 13:50:24 +00:00
|
|
|
|
# Print a reminder for users to set a root password.
|
|
|
|
|
environment.interactiveShellInit =
|
|
|
|
|
''
|
|
|
|
|
if [ "$UID" = 0 ]; then
|
|
|
|
|
read _l < /etc/shadow
|
|
|
|
|
if [ "''${_l:0:6}" = root:: ]; then
|
|
|
|
|
cat >&2 <<EOF
|
|
|
|
|
[1;31mWarning:[0m Your root account has a null password, allowing local users
|
|
|
|
|
to login as root. Please set a non-null password using \`passwd', or
|
|
|
|
|
disable password-based root logins using \`passwd -l'.
|
|
|
|
|
EOF
|
|
|
|
|
fi
|
|
|
|
|
unset _l
|
|
|
|
|
fi
|
|
|
|
|
'';
|
|
|
|
|
|
2010-09-13 16:41:38 +01:00
|
|
|
|
system.activationScripts.users = stringAfter [ "groups" ]
|
2009-09-02 18:35:24 +01:00
|
|
|
|
''
|
2009-12-16 13:35:03 +00:00
|
|
|
|
echo "updating users..."
|
|
|
|
|
|
2009-03-06 12:26:16 +00:00
|
|
|
|
cat ${usersFile} | while true; do
|
2009-01-02 16:07:01 +00:00
|
|
|
|
read name || break
|
|
|
|
|
read description
|
|
|
|
|
read uid
|
|
|
|
|
read group
|
|
|
|
|
read extraGroups
|
|
|
|
|
read home
|
|
|
|
|
read shell
|
|
|
|
|
read createHome
|
2009-09-02 18:35:24 +01:00
|
|
|
|
read password
|
2011-06-27 09:50:30 +01:00
|
|
|
|
read isSystemUser
|
2011-11-29 06:08:55 +00:00
|
|
|
|
read createUser
|
2012-10-23 12:35:06 +01:00
|
|
|
|
read isAlias
|
2011-11-29 06:08:55 +00:00
|
|
|
|
|
2012-10-23 12:35:06 +01:00
|
|
|
|
if [ -z "$createUser" ]; then
|
2011-11-29 06:08:55 +00:00
|
|
|
|
continue
|
|
|
|
|
fi
|
2009-01-02 16:07:01 +00:00
|
|
|
|
|
|
|
|
|
if ! curEnt=$(getent passwd "$name"); then
|
2011-06-27 09:50:30 +01:00
|
|
|
|
useradd ''${isSystemUser:+--system} \
|
2009-01-02 16:07:01 +00:00
|
|
|
|
--comment "$description" \
|
|
|
|
|
''${uid:+--uid $uid} \
|
|
|
|
|
--gid "$group" \
|
|
|
|
|
--groups "$extraGroups" \
|
|
|
|
|
--home "$home" \
|
|
|
|
|
--shell "$shell" \
|
2010-06-02 22:10:48 +01:00
|
|
|
|
''${createHome:+--create-home} \
|
2012-10-23 12:35:06 +01:00
|
|
|
|
''${isAlias:+--non-unique} \
|
2010-06-02 22:10:48 +01:00
|
|
|
|
"$name"
|
2009-09-02 18:35:24 +01:00
|
|
|
|
if test "''${password:0:1}" = 'X'; then
|
2010-06-03 00:02:09 +01:00
|
|
|
|
(echo "''${password:1}"; echo "''${password:1}") | ${pkgs.shadow}/bin/passwd "$name"
|
2009-09-02 18:35:24 +01:00
|
|
|
|
fi
|
2009-01-02 16:07:01 +00:00
|
|
|
|
else
|
|
|
|
|
#echo "updating user $name..."
|
|
|
|
|
oldIFS="$IFS"; IFS=:; set -- $curEnt; IFS="$oldIFS"
|
|
|
|
|
prevUid=$3
|
|
|
|
|
prevHome=$6
|
|
|
|
|
# Don't change the home directory if it's the same to prevent
|
|
|
|
|
# unnecessary warnings about logged in users.
|
|
|
|
|
if test "$prevHome" = "$home"; then unset home; fi
|
|
|
|
|
usermod \
|
|
|
|
|
--comment "$description" \
|
|
|
|
|
--gid "$group" \
|
|
|
|
|
--groups "$extraGroups" \
|
|
|
|
|
''${home:+--home "$home"} \
|
2010-06-02 22:10:48 +01:00
|
|
|
|
--shell "$shell" \
|
|
|
|
|
"$name"
|
2009-12-16 13:35:03 +00:00
|
|
|
|
fi
|
|
|
|
|
|
2009-03-06 12:26:16 +00:00
|
|
|
|
done
|
2010-09-13 16:41:38 +01:00
|
|
|
|
'';
|
2009-01-02 16:07:01 +00:00
|
|
|
|
|
2010-09-13 16:41:38 +01:00
|
|
|
|
system.activationScripts.groups = stringAfter [ "rootPasswd" "binsh" "etc" "var" ]
|
2009-09-02 18:35:24 +01:00
|
|
|
|
''
|
2009-12-16 13:35:03 +00:00
|
|
|
|
echo "updating groups..."
|
2011-09-14 19:20:50 +01:00
|
|
|
|
|
2012-04-22 17:28:08 +01:00
|
|
|
|
createGroup() {
|
|
|
|
|
name="$1"
|
|
|
|
|
gid="$2"
|
2009-01-02 16:07:01 +00:00
|
|
|
|
|
|
|
|
|
if ! curEnt=$(getent group "$name"); then
|
|
|
|
|
groupadd --system \
|
2010-06-02 22:10:48 +01:00
|
|
|
|
''${gid:+--gid $gid} \
|
|
|
|
|
"$name"
|
2009-01-02 16:07:01 +00:00
|
|
|
|
fi
|
2012-04-22 17:28:08 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
${flip concatMapStrings (attrValues config.users.extraGroups) (g: ''
|
|
|
|
|
createGroup '${g.name}' '${toString g.gid}'
|
|
|
|
|
'')}
|
2010-09-13 16:41:38 +01:00
|
|
|
|
'';
|
2007-06-08 16:41:12 +01:00
|
|
|
|
|
2009-01-02 16:07:01 +00:00
|
|
|
|
};
|
2009-09-02 18:35:24 +01:00
|
|
|
|
|
2007-11-09 18:49:45 +00:00
|
|
|
|
}
|