Convert users.extraUsers to an option set and add support for openssh
authorized_keys file generation. svn path=/nixos/trunk/; revision=30611
This commit is contained in:
parent
e4ba69aecf
commit
e264d1ab79
@ -5,43 +5,83 @@ with pkgs.lib;
|
||||
let
|
||||
|
||||
ids = config.ids;
|
||||
users = config.users;
|
||||
|
||||
userOpts = {name, config, ...}:
|
||||
|
||||
# User accounts to be created/updated by NixOS.
|
||||
users =
|
||||
let
|
||||
defaultUsers =
|
||||
[ { name = "root";
|
||||
uid = ids.uids.root;
|
||||
description = "System administrator";
|
||||
home = "/root";
|
||||
shell = config.users.defaultUserShell;
|
||||
group = "root";
|
||||
}
|
||||
{ name = "nobody";
|
||||
uid = ids.uids.nobody;
|
||||
description = "Unprivileged account (don't use!)";
|
||||
}
|
||||
];
|
||||
|
||||
# !!! Use NixOS module system to add default attributes.
|
||||
addAttrs =
|
||||
{ name
|
||||
, description
|
||||
, uid ? ""
|
||||
, group ? "nogroup"
|
||||
, extraGroups ? []
|
||||
, home ? "/var/empty"
|
||||
, shell ? (if useDefaultShell then config.users.defaultUserShell else "/noshell")
|
||||
, createHome ? false
|
||||
, useDefaultShell ? false
|
||||
, password ? null
|
||||
, isSystemUser ? true
|
||||
}:
|
||||
{ inherit name description uid group extraGroups home shell createHome password isSystemUser; };
|
||||
|
||||
in map addAttrs (defaultUsers ++ config.users.extraUsers);
|
||||
{
|
||||
options = {
|
||||
name = mkOption {
|
||||
type = with types; uniq string;
|
||||
description = "The name of the user account. If undefined, the name of the attribute set will be used.";
|
||||
};
|
||||
description = mkOption {
|
||||
type = with types; uniq string;
|
||||
default = "";
|
||||
description = "A short description of the user account.";
|
||||
};
|
||||
uid = mkOption {
|
||||
type = with types; uniq (nullOr int);
|
||||
default = null;
|
||||
description = "The account UID. If undefined, NixOS will select a UID.";
|
||||
};
|
||||
group = mkOption {
|
||||
type = with types; uniq string;
|
||||
default = "nogroup";
|
||||
description = "The user's primary group.";
|
||||
};
|
||||
extraGroups = mkOption {
|
||||
type = types.listOf types.string;
|
||||
default = [];
|
||||
description = "The user's auxiliary groups.";
|
||||
};
|
||||
home = mkOption {
|
||||
type = with types; uniq string;
|
||||
default = "/var/empty";
|
||||
description = "The user's home directory.";
|
||||
};
|
||||
shell = mkOption {
|
||||
type = with types; uniq string;
|
||||
default = "/noshell";
|
||||
description = "The path to the user's shell.";
|
||||
};
|
||||
createHome = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "If true, the home directory will be created automatically.";
|
||||
};
|
||||
useDefaultShell = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "If true, the user's shell will be set to <literal>users.defaultUserShell</literal>.";
|
||||
};
|
||||
password = mkOption {
|
||||
type = with types; uniq (nullOr string);
|
||||
default = null;
|
||||
description = "The user's password. If undefined, no password is set for the user. Warning: do not set confidential information here because this data would be readable by all. This option should only be used for public account such as guest.";
|
||||
};
|
||||
isSystemUser = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Indicates if the user is a system user or not.";
|
||||
};
|
||||
createUser = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "
|
||||
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.
|
||||
";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
name = mkDefault name;
|
||||
uid = mkDefault (attrByPath [name] null ids.uids);
|
||||
shell = mkIf config.useDefaultShell (mkDefault users.defaultUserShell);
|
||||
};
|
||||
};
|
||||
|
||||
# Groups to be created/updated by NixOS.
|
||||
groups =
|
||||
@ -109,11 +149,14 @@ let
|
||||
|
||||
# Note: the 'X' in front of the password is to distinguish between
|
||||
# having an empty password, and not having a password.
|
||||
serializedUser = u: "${u.name}\n${u.description}\n${toString u.uid}\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";
|
||||
serializedUser = userName: let u = getAttr userName config.users.extraUsers; in "${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${if u.createUser then "yes" else "no"}\n";
|
||||
|
||||
serializedGroup = g: "${g.name}\n${toString g.gid}";
|
||||
|
||||
# keep this extra file so that cat can be used to pass special chars such as "`" which is used in the avahi daemon
|
||||
usersFile = pkgs.writeText "users" (concatStrings (map serializedUser users));
|
||||
usersFile = pkgs.writeText "users" (
|
||||
concatMapStrings serializedUser (attrNames config.users.extraUsers)
|
||||
);
|
||||
|
||||
in
|
||||
|
||||
@ -124,22 +167,24 @@ in
|
||||
options = {
|
||||
|
||||
users.extraUsers = mkOption {
|
||||
default = [];
|
||||
example =
|
||||
[ { name = "alice";
|
||||
uid = 1234;
|
||||
description = "Alice";
|
||||
home = "/home/alice";
|
||||
createHome = true;
|
||||
group = "users";
|
||||
extraGroups = ["wheel"];
|
||||
shell = "/bin/sh";
|
||||
password = "foobar";
|
||||
}
|
||||
];
|
||||
default = {};
|
||||
type = types.loaOf types.optionSet;
|
||||
example = {
|
||||
alice = {
|
||||
uid = 1234;
|
||||
description = "Alice";
|
||||
home = "/home/alice";
|
||||
createHome = true;
|
||||
group = "users";
|
||||
extraGroups = ["wheel"];
|
||||
shell = "/bin/sh";
|
||||
password = "foobar";
|
||||
};
|
||||
};
|
||||
description = ''
|
||||
Additional user accounts to be created automatically by the system.
|
||||
'';
|
||||
options = [ userOpts ];
|
||||
};
|
||||
|
||||
users.extraGroups = mkOption {
|
||||
@ -154,6 +199,15 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
default = {};
|
||||
description = ''
|
||||
This option defines settings for individual users on the system.
|
||||
'';
|
||||
type = types.loaOf types.optionSet;
|
||||
options = [ ];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -161,6 +215,18 @@ in
|
||||
|
||||
config = {
|
||||
|
||||
users.extraUsers = {
|
||||
root = {
|
||||
description = "System administrator";
|
||||
home = "/root";
|
||||
shell = config.users.defaultUserShell;
|
||||
group = "root";
|
||||
};
|
||||
nobody = {
|
||||
description = "Unprivileged account (don't use!)";
|
||||
};
|
||||
};
|
||||
|
||||
system.activationScripts.rootPasswd = stringAfter [ "etc" ]
|
||||
''
|
||||
# If there is no password file yet, create a root account with an
|
||||
@ -192,6 +258,11 @@ in
|
||||
read createHome
|
||||
read password
|
||||
read isSystemUser
|
||||
read createUser
|
||||
|
||||
if ! test "$createUser" = "yes"; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if ! curEnt=$(getent passwd "$name"); then
|
||||
useradd ''${isSystemUser:+--system} \
|
||||
|
@ -14,6 +14,98 @@ let
|
||||
v == "forced-commands-only" ||
|
||||
v == "no";
|
||||
|
||||
userOptions = {
|
||||
openssh.authorizedKeys = {
|
||||
|
||||
preserveExistingKeys = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
If this option is enabled, the keys specified in
|
||||
<literal>keys</literal> and/or <literal>keyFiles</literal> will be
|
||||
placed in a special section of the user's authorized_keys file
|
||||
and any existing keys will be preserved. That section will be
|
||||
regenerated each time NixOS is activated. However, if
|
||||
<literal>preserveExisting</literal> isn't enabled, the complete file
|
||||
will be generated, and any user modifications will be wiped out.
|
||||
'';
|
||||
};
|
||||
|
||||
keys = mkOption {
|
||||
type = types.listOf types.string;
|
||||
default = [];
|
||||
description = ''
|
||||
A list of verbatim OpenSSH public keys that should be inserted into the
|
||||
user's authorized_keys file. You can combine the <literal>keys</literal> and
|
||||
<literal>keyFiles</literal> options.
|
||||
'';
|
||||
};
|
||||
|
||||
keyFiles = mkOption {
|
||||
type = types.listOf types.string;
|
||||
default = [];
|
||||
description = ''
|
||||
A list of files each containing one OpenSSH public keys that should be
|
||||
inserted into the user's authorized_keys file. You can combine
|
||||
the <literal>keyFiles</literal> and
|
||||
<literal>keys</literal> options.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
mkAuthkeyScript =
|
||||
let
|
||||
marker1 = "### NixOS will regenerate this line and every line below it.";
|
||||
marker2 = "### NixOS will regenerate this file. Do not edit!";
|
||||
users = map (userName: getAttr userName config.users.extraUsers) (attrNames config.users.extraUsers);
|
||||
usersWithKeys = flip filter users (u:
|
||||
length u.openssh.authorizedKeys.keys != 0 || length u.openssh.authorizedKeys.keyFiles != 0
|
||||
);
|
||||
userLoop = flip concatMapStrings usersWithKeys (u:
|
||||
let
|
||||
authKeys = concatStringsSep "," u.openssh.authorizedKeys.keys;
|
||||
authKeyFiles = concatStringsSep "," u.openssh.authorizedKeys.keyFiles;
|
||||
preserveExisting = if u.openssh.authorizedKeys.preserveExistingKeys then "true" else "false";
|
||||
in ''
|
||||
mkAuthKeysFile "${u.name}" "${authKeys}" "${authKeyFiles}" "${preserveExisting}"
|
||||
''
|
||||
);
|
||||
in ''
|
||||
mkAuthKeysFile() {
|
||||
local userName="$1"
|
||||
local authKeys="$2"
|
||||
local authKeyFiles="$3"
|
||||
local preserveExisting="$4"
|
||||
IFS=","
|
||||
|
||||
for f in $authKeyFiles; do
|
||||
if [ -f "$f" ]; then
|
||||
authKeys="$(${pkgs.coreutils}/bin/cat "$f"),$authKeys"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$authKeys" ]; then
|
||||
eval authfile=~$userName/.ssh/authorized_keys
|
||||
${pkgs.coreutils}/bin/mkdir -p "$(dirname $authfile)"
|
||||
${pkgs.coreutils}/bin/touch "$authfile"
|
||||
if [ "$preserveExisting" == "false" ]; then
|
||||
rm -f "$authfile"
|
||||
authKeys="${marker2},$authKeys"
|
||||
else
|
||||
${pkgs.gnused}/bin/sed -i '/^### NixOS.*$/,$d' "$authfile"
|
||||
authKeys="${marker1},$authKeys"
|
||||
fi
|
||||
for key in $authKeys; do ${pkgs.coreutils}/bin/echo "$key" >> "$authfile"; done
|
||||
fi
|
||||
|
||||
unset IFS
|
||||
}
|
||||
${userLoop}
|
||||
'';
|
||||
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
@ -102,6 +194,10 @@ in
|
||||
|
||||
};
|
||||
|
||||
users.extraUsers = mkOption {
|
||||
options = [ userOptions ];
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -135,6 +231,8 @@ in
|
||||
|
||||
preStart =
|
||||
''
|
||||
${mkAuthkeyScript}
|
||||
|
||||
mkdir -m 0755 -p /etc/ssh
|
||||
|
||||
if ! test -f /etc/ssh/ssh_host_dsa_key; then
|
||||
|
Loading…
Reference in New Issue
Block a user