aboutsummaryrefslogtreecommitdiff
path: root/nixpkgs/nixos/modules/config/users-groups.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/config/users-groups.nix')
-rw-r--r--nixpkgs/nixos/modules/config/users-groups.nix117
1 files changed, 79 insertions, 38 deletions
diff --git a/nixpkgs/nixos/modules/config/users-groups.nix b/nixpkgs/nixos/modules/config/users-groups.nix
index 141e43fec39..0ab303d0ae4 100644
--- a/nixpkgs/nixos/modules/config/users-groups.nix
+++ b/nixpkgs/nixos/modules/config/users-groups.nix
@@ -6,6 +6,16 @@ let
ids = config.ids;
cfg = config.users;
+ # Check whether a password hash will allow login.
+ allowsLogin = hash:
+ hash == "" # login without password
+ || !(lib.elem hash
+ [ null # password login disabled
+ "!" # password login disabled
+ "!!" # a variant of "!"
+ "*" # password unset
+ ]);
+
passwordDescription = ''
The options <option>hashedPassword</option>,
<option>password</option> and <option>passwordFile</option>
@@ -25,8 +35,19 @@ let
'';
hashedPasswordDescription = ''
- To generate hashed password install <literal>mkpasswd</literal>
+ To generate a hashed password install the <literal>mkpasswd</literal>
package and run <literal>mkpasswd -m sha-512</literal>.
+
+ If set to an empty string (<literal>""</literal>), this user will
+ be able to log in without being asked for a password (but not via remote
+ services such as SSH, or indirectly via <command>su</command> or
+ <command>sudo</command>). This should only be used for e.g. bootable
+ live systems. Note: this is different from setting an empty password,
+ which ca be achieved using <option>users.users.&lt;name?&gt;.password</option>.
+
+ If set to <literal>null</literal> (default) this user will not
+ be able to log in using a password (i.e. via <command>login</command>
+ command).
'';
userOpts = { name, config, ... }: {
@@ -354,18 +375,6 @@ let
};
};
- mkSubuidEntry = user: concatStrings (
- map (range: "${user.name}:${toString range.startUid}:${toString range.count}\n")
- user.subUidRanges);
-
- subuidFile = concatStrings (map mkSubuidEntry (attrValues cfg.users));
-
- mkSubgidEntry = user: concatStrings (
- map (range: "${user.name}:${toString range.startGid}:${toString range.count}\n")
- user.subGidRanges);
-
- subgidFile = concatStrings (map mkSubgidEntry (attrValues cfg.users));
-
idsAreUnique = set: idAttr: !(fold (name: args@{ dup, acc }:
let
id = builtins.toString (builtins.getAttr idAttr (builtins.getAttr name set));
@@ -385,6 +394,7 @@ let
{ inherit (u)
name uid group description home createHome isSystemUser
password passwordFile hashedPassword
+ isNormalUser subUidRanges subGidRanges
initialPassword initialHashedPassword;
shell = utils.toShellPath u.shell;
}) cfg.users;
@@ -406,6 +416,12 @@ in {
imports = [
(mkAliasOptionModule [ "users" "extraUsers" ] [ "users" "users" ])
(mkAliasOptionModule [ "users" "extraGroups" ] [ "users" "groups" ])
+ (mkChangedOptionModule
+ [ "security" "initialRootPassword" ]
+ [ "users" "users" "root" "initialHashedPassword" ]
+ (cfg: if cfg.security.initialRootPassword == "!"
+ then null
+ else cfg.security.initialRootPassword))
];
###### interface
@@ -447,7 +463,7 @@ in {
users.users = mkOption {
default = {};
- type = with types; loaOf (submodule userOpts);
+ type = with types; attrsOf (submodule userOpts);
example = {
alice = {
uid = 1234;
@@ -471,20 +487,12 @@ in {
{ students.gid = 1001;
hackers = { };
};
- type = with types; loaOf (submodule groupOpts);
+ type = with types; attrsOf (submodule groupOpts);
description = ''
Additional groups to be created automatically by the system.
'';
};
- # FIXME: obsolete - will remove.
- security.initialRootPassword = mkOption {
- type = types.str;
- default = "!";
- example = "";
- visible = false;
- };
-
};
@@ -499,7 +507,6 @@ in {
home = "/root";
shell = mkDefault cfg.defaultUserShell;
group = "root";
- initialHashedPassword = mkDefault config.security.initialRootPassword;
};
nobody = {
uid = ids.uids.nobody;
@@ -549,16 +556,7 @@ in {
# Install all the user shells
environment.systemPackages = systemShells;
- environment.etc = {
- subuid = {
- text = subuidFile;
- mode = "0644";
- };
- subgid = {
- text = subgidFile;
- mode = "0644";
- };
- } // (mapAttrs' (name: { packages, ... }: {
+ environment.etc = (mapAttrs' (name: { packages, ... }: {
name = "profiles/per-user/${name}";
value.source = pkgs.buildEnv {
name = "user-environment";
@@ -583,22 +581,65 @@ in {
# password or an SSH authorized key. Privileged accounts are
# root and users in the wheel group.
assertion = !cfg.mutableUsers ->
- any id (mapAttrsToList (name: cfg:
+ any id ((mapAttrsToList (name: cfg:
(name == "root"
|| cfg.group == "wheel"
|| elem "wheel" cfg.extraGroups)
&&
- ((cfg.hashedPassword != null && cfg.hashedPassword != "!")
+ (allowsLogin cfg.hashedPassword
|| cfg.password != null
|| cfg.passwordFile != null
|| cfg.openssh.authorizedKeys.keys != []
|| cfg.openssh.authorizedKeys.keyFiles != [])
- ) cfg.users);
+ ) cfg.users) ++ [
+ config.security.googleOsLogin.enable
+ ]);
message = ''
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:
+ {
+ assertion = (user.hashedPassword != null)
+ -> (builtins.match ".*:.*" user.hashedPassword == null);
+ message = ''
+ The password hash of 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."${name}".hashedPassword`.'';
+ }
+ );
+
+ warnings =
+ builtins.filter (x: x != null) (
+ flip mapAttrsToList cfg.users (name: user:
+ # This regex matches a subset of the Modular Crypto Format (MCF)[1]
+ # informal standard. Since this depends largely on the OS or the
+ # specific implementation of crypt(3) we only support the (sane)
+ # schemes implemented by glibc and BSDs. In particular the original
+ # DES hash is excluded since, having no structure, it would validate
+ # common mistakes like typing the plaintext password.
+ #
+ # [1]: https://en.wikipedia.org/wiki/Crypt_(C)
+ let
+ sep = "\\$";
+ base64 = "[a-zA-Z0-9./]+";
+ id = "[a-z0-9-]+";
+ value = "[a-zA-Z0-9/+.-]+";
+ options = "${id}(=${value})?(,${id}=${value})*";
+ scheme = "${id}(${sep}${options})?";
+ content = "${base64}${sep}${base64}";
+ mcf = "^${sep}${scheme}${sep}${content}$";
+ in
+ if (allowsLogin user.hashedPassword
+ && user.hashedPassword != "" # login without password
+ && builtins.match mcf user.hashedPassword == null)
+ then ''
+ The password hash of user "${name}" may be invalid. You must set a
+ valid hash or the user will be locked out of their account. Please
+ check the value of option `users.users."${name}".hashedPassword`.''
+ else null
+ ));
};