diff options
author | Majiir Paktu <majiir@nabaal.net> | 2023-09-16 15:29:11 -0400 |
---|---|---|
committer | Majiir Paktu <majiir@nabaal.net> | 2023-10-10 21:11:34 -0400 |
commit | 077cdcc7e9a9ed406860e97ce3590fba15aed18b (patch) | |
tree | 9101baf6da6804c7d75731e1f7f6281c0a367e02 /nixos/modules/security/pam.nix | |
parent | e86487e579271dc5f0d7627a8f7f5a496d133d59 (diff) |
nixos/pam: convert rules to attrs, add order field
Makes it possible to override properties of a rule by name. Introduces an 'order' field that can be overridden to change the sequence of rules. For now, the order value for each built-in rule is derived from its place in the hardcoded list of rules.
Diffstat (limited to 'nixos/modules/security/pam.nix')
-rw-r--r-- | nixos/modules/security/pam.nix | 50 |
1 files changed, 43 insertions, 7 deletions
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix index 8c9e17bd9cf21..b7a5d7131b578 100644 --- a/nixos/modules/security/pam.nix +++ b/nixos/modules/security/pam.nix @@ -11,14 +11,18 @@ let # These options are experimental and subject to breaking changes without notice. description = lib.mdDoc '' PAM `${type}` rules for this service. + + Attribute keys are the name of each rule. ''; - type = types.listOf (types.submodule ({ config, ... }: { + type = types.attrsOf (types.submodule ({ name, config, ... }: { options = { name = mkOption { type = types.str; description = lib.mdDoc '' Name of this rule. ''; + internal = true; + readOnly = true; }; enable = mkOption { type = types.bool; @@ -27,6 +31,23 @@ let Whether this rule is added to the PAM service config file. ''; }; + order = mkOption { + type = types.int; + description = lib.mdDoc '' + Order of this rule in the service file. Rules are arranged in ascending order of this value. + + ::: {.warning} + The `order` values for the built-in rules are subject to change. If you assign a constant value to this option, a system update could silently reorder your rule. You could be locked out of your system, or your system could be left wide open. When using this option, set it to a relative offset from another rule's `order` value: + + ```nix + { + security.pam.services.login.rules.auth.foo.order = + config.security.pam.services.login.rules.auth.unix.order + 10; + } + ``` + ::: + ''; + }; control = mkOption { type = types.str; description = lib.mdDoc '' @@ -60,6 +81,7 @@ let }; }; config = { + inherit name; # Formats an attrset of settings as args for use as `module-arguments`. args = concatLists (flip mapAttrsToList config.settings (name: value: if isBool value @@ -557,13 +579,21 @@ let limits = mkDefault config.security.pam.loginLimits; text = let + ensureUniqueOrder = type: rules: + let + checkPair = a: b: assert assertMsg (a.order != b.order) "security.pam.services.${name}.rules.${type}: rules '${a.name}' and '${b.name}' cannot have the same order value (${toString a.order})"; b; + checked = zipListsWith checkPair rules (drop 1 rules); + in take 1 rules ++ checked; # Formats a string for use in `module-arguments`. See `man pam.conf`. formatModuleArgument = token: if hasInfix " " token then "[${replaceStrings ["]"] ["\\]"] token}]" else token; formatRules = type: pipe cfg.rules.${type} [ + attrValues (filter (rule: rule.enable)) + (sort (a: b: a.order < b.order)) + (ensureUniqueOrder type) (map (rule: concatStringsSep " " ( [ type rule.control rule.modulePath ] ++ map formatModuleArgument rule.args @@ -587,8 +617,14 @@ let # !!! TODO: move the LDAP stuff to the LDAP module, and the # Samba stuff to the Samba module. This requires that the PAM # module provides the right hooks. - rules = { - account = [ + rules = let + autoOrderRules = flip pipe [ + (imap1 (index: rule: rule // { order = mkDefault (10000 + index * 100); } )) + (map (rule: nameValuePair rule.name (removeAttrs rule [ "name" ]))) + listToAttrs + ]; + in { + account = autoOrderRules [ { name = "ldap"; enable = use_ldap; control = "sufficient"; modulePath = "${pam_ldap}/lib/security/pam_ldap.so"; } { name = "mysql"; enable = cfg.mysqlAuth; control = "sufficient"; modulePath = "${pkgs.pam_mysql}/lib/security/pam_mysql.so"; settings = { config_file = "/etc/security/pam_mysql.conf"; @@ -607,7 +643,7 @@ let { name = "unix"; control = "required"; modulePath = "pam_unix.so"; } ]; - auth = [ + auth = autoOrderRules ([ { name = "oslogin_login"; enable = cfg.googleOsLoginAuthentication; control = "[success=done perm_denied=die default=ignore]"; modulePath = "${pkgs.google-guest-oslogin}/lib/security/pam_oslogin_login.so"; } { name = "rootok"; enable = cfg.rootOK; control = "sufficient"; modulePath = "pam_rootok.so"; } { name = "wheel"; enable = cfg.requireWheel; control = "required"; modulePath = "pam_wheel.so"; settings = { @@ -730,9 +766,9 @@ let use_first_pass = true; }; } { name = "deny"; control = "required"; modulePath = "pam_deny.so"; } - ]; + ]); - password = [ + password = autoOrderRules [ { name = "systemd_home"; enable = config.services.homed.enable; control = "sufficient"; modulePath = "${config.systemd.package}/lib/security/pam_systemd_home.so"; } { name = "unix"; control = "sufficient"; modulePath = "pam_unix.so"; settings = { nullok = true; @@ -758,7 +794,7 @@ let }; } ]; - session = [ + session = autoOrderRules [ { name = "env"; enable = cfg.setEnvironment; control = "required"; modulePath = "pam_env.so"; settings = { conffile = "/etc/pam/environment"; readenv = 0; |