mirror of
https://github.com/NixOS/nixpkgs.git
synced 2026-07-06 17:13:24 -05:00
account-utils: init at 1.3.0, nixos/account-utils: init (#453557)
This commit is contained in:
commit
91cd0708d0
7 changed files with 318 additions and 8 deletions
|
|
@ -387,6 +387,7 @@
|
|||
./programs/zsh/zsh-syntax-highlighting.nix
|
||||
./programs/zsh/zsh.nix
|
||||
./rename.nix
|
||||
./security/account-utils.nix
|
||||
./security/acme
|
||||
./security/agnos.nix
|
||||
./security/apparmor.nix
|
||||
|
|
|
|||
65
nixos/modules/security/account-utils.nix
Normal file
65
nixos/modules/security/account-utils.nix
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.security.account-utils;
|
||||
in
|
||||
{
|
||||
options.security.account-utils = {
|
||||
enable = lib.mkEnableOption "the account-utils implementation of Unix user authentication and management";
|
||||
package = lib.mkPackageOption pkgs "account-utils" { };
|
||||
extraArgs = lib.mkOption {
|
||||
type = lib.types.listOf lib.types.nonEmptyStr;
|
||||
default = [ ];
|
||||
example = [
|
||||
"--debug"
|
||||
"-v"
|
||||
];
|
||||
description = ''
|
||||
List of arguments to pass to the socket activated service executables.
|
||||
::: {.note}
|
||||
This is passed to both pwupdd and pwaccessd, which support identical flags.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# use account-utils reimplementation of pam_unix
|
||||
security.pam = {
|
||||
pam_unixModulePath = "${cfg.package}/lib/security/pam_unix_ng.so";
|
||||
enableLegacySettings = false;
|
||||
};
|
||||
|
||||
systemd = {
|
||||
packages = [ cfg.package ];
|
||||
sockets.pwaccessd.wantedBy = [ "sockets.target" ];
|
||||
sockets.pwupdd.wantedBy = lib.optional config.users.mutableUsers "sockets.target"; # immutable users do not need password updating
|
||||
sockets.newidmapd.wantedBy = [ "sockets.target" ];
|
||||
services."pwupdd@".environment.PWUPDD_OPTS = lib.escapeShellArgs cfg.extraArgs;
|
||||
services."pwaccessd".environment.PWACCESSD_OPTS = lib.escapeShellArgs cfg.extraArgs;
|
||||
};
|
||||
|
||||
environment.systemPackages = [ cfg.package ];
|
||||
|
||||
security.pam.services = {
|
||||
pwupd-passwd = { };
|
||||
pwupd-chsh = { };
|
||||
pwupd-chfn = { };
|
||||
};
|
||||
|
||||
# covered by account-utils via socket-activated service
|
||||
security.wrappers = {
|
||||
# shadow suid binaries are no longer necessary, but disabling the entire shadow module is too intrusive
|
||||
newuidmap.enable = false;
|
||||
newgidmap.enable = false;
|
||||
chsh.enable = false;
|
||||
passwd.enable = false;
|
||||
|
||||
unix_chkpwd.enable = false; # Not necessary when using pam_unix_ng.so
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1008,7 +1008,7 @@ let
|
|||
{
|
||||
name = "unix";
|
||||
control = "required";
|
||||
modulePath = "${package}/lib/security/pam_unix.so";
|
||||
modulePath = config.security.pam.pam_unixModulePath;
|
||||
}
|
||||
# pam_slurm_adopt must be the last module in the account stack.
|
||||
{
|
||||
|
|
@ -1217,11 +1217,11 @@ let
|
|||
name = "unix-early";
|
||||
enable = cfg.unixAuth;
|
||||
control = "optional";
|
||||
modulePath = "${package}/lib/security/pam_unix.so";
|
||||
modulePath = config.security.pam.pam_unixModulePath;
|
||||
settings = {
|
||||
nullok = cfg.allowNullPassword;
|
||||
inherit (cfg) nodelay;
|
||||
likeauth = true;
|
||||
likeauth = lib.mkIf config.security.pam.enableLegacySettings true;
|
||||
};
|
||||
}
|
||||
{
|
||||
|
|
@ -1315,11 +1315,11 @@ let
|
|||
name = "unix";
|
||||
enable = cfg.unixAuth;
|
||||
control = "sufficient";
|
||||
modulePath = "${package}/lib/security/pam_unix.so";
|
||||
modulePath = config.security.pam.pam_unixModulePath;
|
||||
settings = {
|
||||
nullok = cfg.allowNullPassword;
|
||||
inherit (cfg) nodelay;
|
||||
likeauth = true;
|
||||
likeauth = lib.mkIf config.security.pam.enableLegacySettings true;
|
||||
try_first_pass = true;
|
||||
};
|
||||
}
|
||||
|
|
@ -1404,10 +1404,10 @@ let
|
|||
{
|
||||
name = "unix";
|
||||
control = "sufficient";
|
||||
modulePath = "${package}/lib/security/pam_unix.so";
|
||||
modulePath = config.security.pam.pam_unixModulePath;
|
||||
settings = {
|
||||
nullok = true;
|
||||
yescrypt = true;
|
||||
yescrypt = lib.mkIf config.security.pam.enableLegacySettings true;
|
||||
};
|
||||
}
|
||||
{
|
||||
|
|
@ -1493,7 +1493,7 @@ let
|
|||
{
|
||||
name = "unix";
|
||||
control = "required";
|
||||
modulePath = "${package}/lib/security/pam_unix.so";
|
||||
modulePath = config.security.pam.pam_unixModulePath;
|
||||
}
|
||||
{
|
||||
name = "loginuid";
|
||||
|
|
@ -1860,6 +1860,14 @@ in
|
|||
|
||||
security.pam.package = lib.mkPackageOption pkgs "pam" { };
|
||||
|
||||
security.pam.pam_unixModulePath = lib.mkOption {
|
||||
type = lib.types.pathInStore;
|
||||
default = "${package}/lib/security/pam_unix.so";
|
||||
defaultText = "\${config.security.pam.package}/lib/security/pam_unix.so";
|
||||
description = "The pam_unix module to use in all the default pam services.";
|
||||
internal = true;
|
||||
};
|
||||
|
||||
security.pam.loginLimits = lib.mkOption {
|
||||
default = [ ];
|
||||
type = limitsType;
|
||||
|
|
@ -1904,6 +1912,19 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
security.pam.enableLegacySettings = lib.mkOption {
|
||||
default = true;
|
||||
type = lib.types.bool;
|
||||
description = ''
|
||||
Alternative implementations of pam_unix may not support all legacy arguments.
|
||||
This option will disable all known legacy settings.
|
||||
::: {.note}
|
||||
Setting this option to false will omit arguments, such as `yescrypt`.
|
||||
Doing so is only safe if the defaults used by pam_unix are sensible.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
||||
security.pam.makeHomeDir.skelDirectory = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/var/empty";
|
||||
|
|
|
|||
|
|
@ -940,6 +940,7 @@ in
|
|||
localsend = runTest ./localsend.nix;
|
||||
locate = runTest ./locate.nix;
|
||||
login = runTest ./login.nix;
|
||||
login-nosuid = runTest ./login-nosuid.nix;
|
||||
logkeys = runTest ./logkeys.nix;
|
||||
logrotate = runTest ./logrotate.nix;
|
||||
loki = runTest ./loki.nix;
|
||||
|
|
|
|||
105
nixos/tests/login-nosuid.nix
Normal file
105
nixos/tests/login-nosuid.nix
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
{
|
||||
name = "login-nosuid";
|
||||
meta = {
|
||||
maintainers = [ ];
|
||||
};
|
||||
|
||||
# node.pkgsReadOnly = false; # needed when overriding pam to debug mode
|
||||
|
||||
nodes.machine =
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
security.enableWrappers = false;
|
||||
systemd.settings.Manager.NoNewPrivileges = true;
|
||||
security.account-utils.enable = true;
|
||||
users.mutableUsers = true;
|
||||
security.account-utils.extraArgs = [
|
||||
"-v"
|
||||
"--debug"
|
||||
];
|
||||
security.loginDefs.chfnRestrict = "f"; # allow allice to change name
|
||||
|
||||
environment.systemPackages = [
|
||||
pkgs.which
|
||||
pkgs.fish # environment.shells does not actually link fish to /run/current-system/sw/bin, causing chsh to fail unexpectedly
|
||||
];
|
||||
environment.shells = [ pkgs.fish ];
|
||||
|
||||
# pam debug without giant rebuild
|
||||
# system.replaceDependencies.replacements = [
|
||||
# {
|
||||
# oldDependency = pkgs.linux-pam;
|
||||
# newDependency = pkgs.linux-pam.override { debugMode = true; };
|
||||
# }
|
||||
# ];
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
machine.start(allow_reboot = True)
|
||||
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
|
||||
machine.screenshot("postboot")
|
||||
|
||||
with subtest("account-utils passwd has priority"):
|
||||
passwd_path = machine.succeed("realpath $(which passwd)")
|
||||
print(f"passwd path is: {passwd_path}")
|
||||
assert "account-utils" in passwd_path
|
||||
|
||||
with subtest("create user"):
|
||||
machine.succeed("useradd -m alice")
|
||||
machine.succeed("(echo foobar; echo foobar) | passwd alice")
|
||||
|
||||
with subtest("Check whether switching VTs works"):
|
||||
machine.fail("pgrep -f 'agetty.*tty2'")
|
||||
machine.send_key("alt-f2")
|
||||
machine.wait_until_succeeds("[ $(fgconsole) = 2 ]")
|
||||
machine.wait_for_unit("getty@tty2.service")
|
||||
machine.wait_until_succeeds("pgrep -f 'agetty.*tty2'")
|
||||
|
||||
with subtest("Log in as alice on a virtual console"):
|
||||
machine.wait_until_tty_matches("2", "login: ")
|
||||
machine.send_chars("alice\n")
|
||||
machine.wait_until_tty_matches("2", "login: alice")
|
||||
machine.wait_until_succeeds("pgrep login")
|
||||
machine.wait_until_tty_matches("2", "Password: ")
|
||||
machine.sleep(1) # something is racy here, so lets just sleep a bit... Not great, but seems to work
|
||||
machine.send_chars("foobar\n")
|
||||
machine.wait_until_succeeds("pgrep -u alice bash")
|
||||
machine.send_chars("touch done\n")
|
||||
machine.wait_for_file("/home/alice/done")
|
||||
|
||||
with subtest("Systemd gives and removes device ownership as needed"):
|
||||
machine.succeed("getfacl /dev/snd/timer | grep -q alice")
|
||||
machine.send_key("alt-f1")
|
||||
machine.wait_until_succeeds("[ $(fgconsole) = 1 ]")
|
||||
machine.fail("getfacl /dev/snd/timer | grep -q alice")
|
||||
machine.succeed("chvt 2")
|
||||
machine.wait_until_succeeds("getfacl /dev/snd/timer | grep -q alice")
|
||||
|
||||
with subtest("User can change their login shell"):
|
||||
machine.send_chars("clear\n") # remove previous password prompts
|
||||
machine.send_chars("chsh -s /run/current-system/sw/bin/fish\n")
|
||||
machine.wait_until_tty_matches("2", "Password: ")
|
||||
machine.send_chars("foobar\n")
|
||||
machine.wait_until_fails("pgrep pwupdd")
|
||||
login_shell = machine.succeed("getent passwd alice | cut -d: -f7").strip()
|
||||
print(f"login shell of user alice: {login_shell}")
|
||||
assert "/run/current-system/sw/bin/fish" == login_shell
|
||||
|
||||
with subtest("User can change their name"):
|
||||
machine.send_chars("clear\n") # remove previous password prompts
|
||||
machine.send_chars("chfn -f 'Alice in Wonderland'\n")
|
||||
machine.wait_until_tty_matches("2", "Password: ")
|
||||
machine.send_chars("foobar\n")
|
||||
machine.wait_until_fails("pgrep pwupdd")
|
||||
full_name = machine.succeed("getent passwd alice | cut -d: -f5").strip()
|
||||
print(f"full name of user alice: {full_name}")
|
||||
assert "Alice in Wonderland" == full_name
|
||||
|
||||
with subtest("Virtual console logout"):
|
||||
machine.send_chars("exit\n")
|
||||
machine.wait_until_fails("pgrep -u alice bash")
|
||||
machine.screenshot("getty")
|
||||
'';
|
||||
}
|
||||
77
pkgs/by-name/ac/account-utils/package.nix
Normal file
77
pkgs/by-name/ac/account-utils/package.nix
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
lib,
|
||||
stdenv,
|
||||
fetchFromGitHub,
|
||||
meson,
|
||||
ninja,
|
||||
pkg-config,
|
||||
linux-pam,
|
||||
systemdLibs,
|
||||
libcap,
|
||||
libxcrypt,
|
||||
libeconf,
|
||||
libselinux,
|
||||
docbook-xsl-ns,
|
||||
libxslt,
|
||||
nixosTests,
|
||||
}:
|
||||
let
|
||||
selinuxSupport = lib.meta.availableOn stdenv.hostPlatform libselinux;
|
||||
in
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "account-utils";
|
||||
version = "1.3.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "thkukuk";
|
||||
repo = "account-utils";
|
||||
tag = "v${finalAttrs.version}";
|
||||
hash = "sha256-9l+y7FLb0IZXXp4RstlhNR6yA7b4b831obFuiVtO9+k=";
|
||||
};
|
||||
|
||||
__structuredAttrs = true;
|
||||
strictDeps = true;
|
||||
|
||||
nativeBuildInputs = [
|
||||
meson
|
||||
ninja
|
||||
pkg-config
|
||||
libxslt
|
||||
docbook-xsl-ns
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
linux-pam
|
||||
systemdLibs
|
||||
libxcrypt
|
||||
libeconf
|
||||
libcap
|
||||
]
|
||||
++ lib.optional selinuxSupport libselinux;
|
||||
|
||||
mesonFlags = [
|
||||
(lib.mesonEnable "selinux" selinuxSupport)
|
||||
(lib.mesonOption "c_args" "-ffat-lto-objects")
|
||||
];
|
||||
|
||||
passthru.tests = {
|
||||
inherit (nixosTests) login-nosuid;
|
||||
};
|
||||
|
||||
meta = {
|
||||
description = "Services, utilities and PAM modules, which allow authentication and account management on systems with the NoNewPrivs flag set (no setuid/setgid binaries)";
|
||||
homepage = "https://github.com/thkukuk/account-utils";
|
||||
changelog = "https://github.com/thkukuk/account-utils/releases/tag/v${finalAttrs.version}";
|
||||
license =
|
||||
with lib.licenses;
|
||||
AND [
|
||||
gpl2Plus
|
||||
lgpl21Plus
|
||||
bsd2
|
||||
];
|
||||
maintainers = with lib.maintainers; [ grimmauld ];
|
||||
platforms = lib.platforms.linux;
|
||||
# take precedence over shadow
|
||||
priority = -1;
|
||||
};
|
||||
})
|
||||
40
pkgs/by-name/li/libeconf/package.nix
Normal file
40
pkgs/by-name/li/libeconf/package.nix
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
lib,
|
||||
stdenv,
|
||||
fetchFromGitHub,
|
||||
meson,
|
||||
ninja,
|
||||
nix-update-script,
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "libeconf";
|
||||
version = "0.8.3";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "openSUSE";
|
||||
repo = "libeconf";
|
||||
tag = "v${finalAttrs.version}";
|
||||
hash = "sha256-ZXZcXQdG3hXAMwwftrIWL5GbVdPXk+AyqdhGTnaKL1I=";
|
||||
};
|
||||
|
||||
__structuredAttrs = true;
|
||||
strictDeps = true;
|
||||
|
||||
nativeBuildInputs = [
|
||||
meson
|
||||
ninja
|
||||
];
|
||||
|
||||
passthru.updateScript = nix-update-script { };
|
||||
|
||||
meta = {
|
||||
description = "Enhanced config file parser, which merges config files placed in several locations into one";
|
||||
homepage = "https://github.com/openSUSE/libeconf";
|
||||
changelog = "https://github.com/openSUSE/libeconf/blob/${finalAttrs.src.tag}/NEWS";
|
||||
license = lib.licenses.mit;
|
||||
maintainers = with lib.maintainers; [ grimmauld ];
|
||||
mainProgram = "econftool";
|
||||
platforms = lib.platforms.all;
|
||||
};
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue