diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index c2f16e5ed8e..12d9be94663 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/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 ,
and
@@ -25,17 +35,19 @@ let
'';
hashedPasswordDescription = ''
- To generate hashed password install mkpasswd
+ To generate a hashed password install the mkpasswd
package and run mkpasswd -m sha-512.
- For password-less logins without password prompt, use
- the empty string "".
+ If set to an empty string (""), 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 su or
+ sudo). 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 .
- For logins with a fixed password (including the empty-string password with
- prompt), use one of the un-hashed password options instead, such as
- .
-
- Such unprotected logins should only be used for e.g. bootable live systems.
+ If set to null (default) this user will not
+ be able to log in using a password (i.e. via login
+ command).
'';
userOpts = { name, config, ... }: {
@@ -415,6 +427,12 @@ in {
imports = [
(mkAliasOptionModule [ "users" "extraUsers" ] [ "users" "users" ])
(mkAliasOptionModule [ "users" "extraGroups" ] [ "users" "groups" ])
+ (mkChangedOptionModule
+ [ "security" "initialRootPassword" ]
+ [ "users" "users" "root" "initialHashedPassword" ]
+ (cfg: if cfg.security.initialHashedPassword == "!"
+ then null
+ else cfg.security.initialHashedPassword))
];
###### interface
@@ -486,14 +504,6 @@ in {
'';
};
- # FIXME: obsolete - will remove.
- security.initialRootPassword = mkOption {
- type = types.str;
- default = "!";
- example = "";
- visible = false;
- };
-
};
@@ -508,7 +518,6 @@ in {
home = "/root";
shell = mkDefault cfg.defaultUserShell;
group = "root";
- initialHashedPassword = mkDefault config.security.initialRootPassword;
};
nobody = {
uid = ids.uids.nobody;
@@ -597,7 +606,7 @@ in {
|| 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 != []
@@ -607,7 +616,17 @@ in {
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) (
@@ -630,14 +649,13 @@ in {
content = "${base64}${sep}${base64}";
mcf = "^${sep}${scheme}${sep}${content}$";
in
- if (user.hashedPassword != null
+ if (allowsLogin user.hashedPassword
+ && user.hashedPassword != "" # login without password
&& builtins.match mcf user.hashedPassword == null)
- then
- ''
+ 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`.
- ''
+ check the value of option `users.users."${name}".hashedPassword`.''
else null
));