From c2c180d3041a1be1b716927ac981ba4f7bbe7c80 Mon Sep 17 00:00:00 2001 From: purian23 Date: Fri, 17 Jul 2026 18:07:06 -0400 Subject: [PATCH] feat(nix): opt-in dankshell-u2f PAM service for lock screen security keys - Updated instructional pushed: https://danklinux.com/docs Related: #2874 Port 1.5 --- distro/nix/greeter.nix | 4 +++- distro/nix/nixos.nix | 21 +++++++++++++++++++++ distro/nix/tests/nixos-module.nix | 2 ++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/distro/nix/greeter.nix b/distro/nix/greeter.nix index 748e896e5..01e2d6b74 100644 --- a/distro/nix/greeter.nix +++ b/distro/nix/greeter.nix @@ -200,7 +200,9 @@ in ]; # DMS currently relies on /etc/pam.d/login for lock screen password auth on NixOS. # Declare security.pam.services.dankshell only if you want to override that runtime fallback. - # U2F and fingerprint are handled separately by DMS — do not add pam_u2f or pam_fprintd here. + # Do not add pam_u2f or pam_fprintd here for security-key unlock, enable + # programs.dank-material-shell.lockscreen.securityKey.enable, which declares the + # dedicated dankshell-u2f service DMS drives on its own. # security.pam.services.dankshell = { # # Example: add faillock # faillock.enable = true; diff --git a/distro/nix/nixos.nix b/distro/nix/nixos.nix index 682ad8b11..68733340a 100644 --- a/distro/nix/nixos.nix +++ b/distro/nix/nixos.nix @@ -23,6 +23,15 @@ in description = "Systemd target to bind to."; default = "graphical-session.target"; }; + options.programs.dank-material-shell.lockscreen.securityKey = { + enable = lib.mkEnableOption "FIDO2/U2F security key unlock for the DMS lock screen via a dedicated dankshell-u2f PAM service"; + package = lib.mkPackageOption pkgs "pam_u2f" { }; + moduleArgs = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ "cue" ]; + description = "Arguments passed to pam_u2f.so in the dankshell-u2f PAM service."; + }; + }; config = lib.mkIf cfg.enable { systemd.user.services.dms = lib.mkIf cfg.systemd.enable { description = "DankMaterialShell"; @@ -46,6 +55,18 @@ in inherit value; }) common.plugins; + # DMS's bundled U2F fallback stack references pam_u2f.so by name, which NixOS's + # libpam cannot resolve; the dedicated service below uses the absolute store path + # and is picked up automatically by the lock screen when present. + security.pam.services."dankshell-u2f" = lib.mkIf cfg.lockscreen.securityKey.enable { + text = '' + auth required ${cfg.lockscreen.securityKey.package}/lib/security/pam_u2f.so ${lib.concatStringsSep " " cfg.lockscreen.securityKey.moduleArgs} + account required pam_permit.so + password required pam_deny.so + session required pam_permit.so + ''; + }; + services.power-profiles-daemon.enable = lib.mkDefault true; services.accounts-daemon.enable = lib.mkDefault true; services.geoclue2.enable = lib.mkDefault true; diff --git a/distro/nix/tests/nixos-module.nix b/distro/nix/tests/nixos-module.nix index 083cafd78..a792bf989 100644 --- a/distro/nix/tests/nixos-module.nix +++ b/distro/nix/tests/nixos-module.nix @@ -19,6 +19,7 @@ pkgs.testers.runNixOSTest { programs.dank-material-shell = { enable = true; systemd.enable = true; + lockscreen.securityKey.enable = true; plugins = { TestPlugin = { src = pkgs.emptyDirectory; @@ -39,6 +40,7 @@ pkgs.testers.runNixOSTest { machine.succeed("su -- danklinux -c 'dms --help >/dev/null'") machine.succeed("test -d /etc/xdg/quickshell/dms-plugins") machine.succeed("test -f /run/current-system/sw/lib/systemd/user/dms.service") + machine.succeed("grep -q 'lib/security/pam_u2f.so cue' /etc/pam.d/dankshell-u2f") payload = json.loads(machine.succeed("su -- danklinux -c 'dms doctor --json'")) t.assertIn("summary", payload)