New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: mutable user eval test #31034
WIP: mutable user eval test #31034
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
let | ||
ensurePass = name: config: | ||
builtins.seq | ||
(builtins.unsafeDiscardOutputDependency | ||
(import ../lib/eval-config.nix { | ||
modules = [ | ||
config | ||
({ | ||
fileSystems."/".device = "/dev/bogus"; | ||
boot.loader.grub.device = "/dev/bogus"; | ||
}) | ||
]; | ||
}).config.system.build.toplevel.drvPath | ||
) "ok" ; | ||
|
||
|
||
ensureFail = name: config: | ||
if (builtins.tryEval (ensurePass name config)).success == false | ||
then "ok" | ||
else throw "unexpected success in ${name}"; | ||
in { | ||
user-with-password = ensurePass "user-with-password" { | ||
# services.openssh.enable = true; | ||
users.mutableUsers = false; | ||
users.users.foo = { | ||
password = "foob"; | ||
extraGroups = [ "wheel" ]; | ||
}; | ||
users.users.root.hashedPassword = null; | ||
}; | ||
|
||
root-only-no-password = ensureFail "root-only-no-password" { | ||
users.mutableUsers = false; | ||
users.users.root.hashedPassword = null; | ||
}; | ||
|
||
root-only-with-bang-password = ensureFail "root-only-with-bang-password" { | ||
users.mutableUsers = false; | ||
users.users.root.hashedPassword = "!"; | ||
}; | ||
|
||
root-only-with-real-password = ensurePass "root-only-with-real-password" { | ||
users.mutableUsers = false; | ||
users.users.root.hashedPassword = "w00t"; | ||
}; | ||
|
||
root-only-with-real-password-ssh-no-login = ensureFail "root-only-with-real-password-ssh-no-login" { | ||
users.mutableUsers = false; | ||
users.users.root.hashedPassword = "w00t"; | ||
services.openssh.enable = true; | ||
services.openssh.permitRootLogin = "no"; | ||
}; | ||
|
||
root-only-with-real-password-ssh-without-pass-login = ensureFail "root-only-with-real-password-ssh-without-pass-login" { | ||
users.mutableUsers = false; | ||
users.users.root.hashedPassword = "w00t"; | ||
services.openssh.enable = true; | ||
services.openssh.permitRootLogin = "without-password"; | ||
}; | ||
|
||
root-only-with-real-password-ssh-prohibit-password-login = ensureFail "root-only-with-real-password-ssh-prohibit-password-login" { | ||
users.mutableUsers = false; | ||
users.users.root.hashedPassword = "w00t"; | ||
services.openssh.enable = true; | ||
services.openssh.permitRootLogin = "prohibit-password"; | ||
}; | ||
|
||
root-only-with-real-password-ssh-no-login-ssh-disabled = ensurePass "root-only-with-real-password-ssh-no-login-ssh-disabled" { | ||
users.mutableUsers = false; | ||
users.users.root.hashedPassword = "w00t"; | ||
services.openssh.enable = false; | ||
services.openssh.permitRootLogin = "no"; | ||
}; | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -557,18 +557,77 @@ 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: | ||
(name == "root" | ||
|| cfg.group == "wheel" | ||
|| elem "wheel" cfg.extraGroups) | ||
&& | ||
((cfg.hashedPassword != null && cfg.hashedPassword != "!") | ||
|| cfg.password != null | ||
|| cfg.passwordFile != null | ||
|| cfg.openssh.authorizedKeys.keys != [] | ||
|| cfg.openssh.authorizedKeys.keyFiles != []) | ||
) cfg.users); | ||
(let | ||
userHasKeys = cfg: | ||
( | ||
cfg.openssh.authorizedKeys.keys != [] | ||
|| cfg.openssh.authorizedKeys.keyFiles != [] | ||
); | ||
|
||
userHasPassword = cfg: | ||
( | ||
(cfg.hashedPassword != null && cfg.hashedPassword != "!") | ||
|| cfg.password != null | ||
|| cfg.passwordFile != null | ||
); | ||
|
||
userHasSSHCreds = name: cfg: | ||
let | ||
ssh = config.services.openssh; | ||
passwordWorks = if ssh.passwordAuthentication | ||
then userHasPassword cfg | ||
else false; | ||
|
||
keyWorks = userHasKeys cfg; | ||
in if name == "root" then | ||
( | ||
# Is root allowed explicitly to use a password? | ||
if ssh.permitRootLogin == "yes" | ||
then passwordWorks || keyWorks | ||
else | ||
# Is root allowed explicitly denied from using a password? | ||
if (ssh.permitRootLogin == "without-password" | ||
|| ssh.permitRootLogin == "prohibit-password") | ||
then keyWorks | ||
else | ||
# Forced commands don't count as being allowed in | ||
# and no means no | ||
if (ssh.permitRootLogin == "forced-command" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be "forced-commands-only" |
||
|| ssh.permitRootLogin == "no") | ||
then false | ||
else | ||
builtins.trace ("Cannot handle openssh.permitRootLogin" | ||
+ " = ${ssh.permitRootLogin} in determining if it" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add quotes around |
||
+ " can be used for SSH login. Assuming no, for" | ||
+ " safety.") false | ||
) | ||
else passwordWorks || keyWorks; | ||
|
||
userIsWheel = cfg: | ||
( | ||
cfg.group == "wheel" | ||
|| elem "wheel" cfg.extraGroups | ||
); | ||
|
||
userCanBecomeRoot = name: cfg: | ||
if name == "root" | ||
then true | ||
else if config.security.sudo.enable == false | ||
then false | ||
else if config.security.sudo.wheelNeedsPassword | ||
then (userIsWheel cfg && userHasPassword cfg) | ||
else userIsWheel cfg; | ||
|
||
in any id (mapAttrsToList (name: cfg: | ||
userCanBecomeRoot name cfg | ||
&& ( | ||
if config.services.openssh.enable | ||
then userHasSSHCreds name cfg | ||
else userHasPassword cfg | ||
) | ||
) cfg.users)); | ||
message = '' | ||
No account can log in at the console or over SSH. Please set | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line seem to be unfinished |
||
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.''; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,7 +21,7 @@ let | |
daemon reads in addition to the the user's authorized_keys file. | ||
You can combine the <literal>keys</literal> and | ||
<literal>keyFiles</literal> options. | ||
Warning: If you are using <literal>NixOps</literal> then don't use this | ||
Warning: If you are using <literal>NixOps</literal> then don't use this | ||
option since it will replace the key required for deployment via ssh. | ||
''; | ||
}; | ||
|
@@ -114,6 +114,8 @@ in | |
|
||
permitRootLogin = mkOption { | ||
default = "prohibit-password"; | ||
# When changing the allowed types, you must also update | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. s/the allowed types/the allowed values/ |
||
# userHasSSHCreds in nixos/modules/config/users-groups.nix! | ||
type = types.enum ["yes" "without-password" "prohibit-password" "forced-commands-only" "no"]; | ||
description = '' | ||
Whether the root user can login using ssh. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC, this assertion can not be accurate if
security.sudo.configFile
is overwritten orsecurity.sudo.extraConfig
is set. I see two options: