From 381da74e6cc19c78e1d46a3c3cca244636051aba Mon Sep 17 00:00:00 2001 From: Colin Date: Sun, 28 Jan 2024 17:55:19 +0000 Subject: [PATCH] users: enable pam_cap for "login" program --- hosts/common/users/colin.nix | 163 +++++++++++++++++++++++++++-------- 1 file changed, 125 insertions(+), 38 deletions(-) diff --git a/hosts/common/users/colin.nix b/hosts/common/users/colin.nix index 8150eb46..32ed4816 100644 --- a/hosts/common/users/colin.nix +++ b/hosts/common/users/colin.nix @@ -53,45 +53,125 @@ security.pam.mount.enable = true; - # in the future, may add this to sshd/login/sudo - # security.pam.services.systemd-user.rules = { - # auth.pam_cap = { - # order = 11500; - # # order = 12500; # before pam_unix but after all the others - # control = "required"; # ? - # modulePath = "${pkgs.libcap.pam}/lib/security/pam_cap.so"; - # }; - # }; - # security.pam.services.greetd.rules = { - # auth.pam_cap = { - # order = 12500; # before pam_unix but after all the others - # control = "required"; # ? - # modulePath = "${pkgs.libcap.pam}/lib/security/pam_cap.so"; - # }; - # }; - # environment.etc."/security/capability.conf".text = '' - # # The pam_cap.so module accepts the following arguments: - # # - # # debug - be more verbose logging things (unused by pam_cap for now) - # # config= - override the default config for the module with file - # # keepcaps - workaround for applications that setuid without this - # # autoauth - if you want pam_cap.so to always succeed for the auth phase - # # default= - provide a fallback IAB value if there is no '*' rule - # # - # # format: - # # [,...] USER|@GROUP|* - # # - # # the part of each line before the delimiter (" \t\n") is parsed with `cap_iab_from_text`. - # # so each CAP can be prefixed to indicate which set it applies to: - # # [!][^][%] - # # where ! adds to the NB set - # # ^ for AI - # # % (or empty) for I + # pam.d ordering: + # /etc/pam.d/greetd: + # auth optional pam_unix.so likeauth nullok # unix-early (order 11600) + # auth optional /nix/store/051v0pwqfy1z7ld6087y99fdrv12113n-pam_mount-2.20/lib/security/pam_mount.so disable_interactive # mount (order 12000) + # auth optional /nix/store/82zqzh7i88pxybcf48zapnz4v0jf19nm-gnome-keyring-42.1/lib/security/pam_gnome_keyring.so # gnome_keyring (order 12200) + # auth sufficient pam_unix.so likeauth nullok try_first_pass # unix (order 12800) + # auth required pam_deny.so # deny (order 13600) + # /etc/pam.d/login: + # auth optional pam_unix.so likeauth nullok # unix-early (order 11600) + # auth optional /nix/store/051v0pwqfy1z7ld6087y99fdrv12113n-pam_mount-2.20/lib/security/pam_mount.so disable_interactive # mount (order 12000) + # auth optional /nix/store/82zqzh7i88pxybcf48zapnz4v0jf19nm-gnome-keyring-42.1/lib/security/pam_gnome_keyring.so # gnome_keyring (order 12200) + # auth sufficient pam_unix.so likeauth nullok try_first_pass # unix (order 12800) + # auth required pam_deny.so # deny (order 13600) + # /etc/pam.d/sshd: `auth required pam_deny.so # deny (order 12400)` + # /etc/pam.d/sudo: + # auth optional pam_unix.so likeauth # unix-early (order 11600) + # auth optional /nix/store/051v0pwqfy1z7ld6087y99fdrv12113n-pam_mount-2.20/lib/security/pam_mount.so disable_interactive # mount (order 12000) + # auth sufficient pam_unix.so likeauth try_first_pass # unix (order 12800) + # auth required pam_deny.so # deny (order 13600) + # /etc/pam.d/systemd-user: + # auth sufficient pam_unix.so likeauth try_first_pass # unix (order 11600) + # auth required pam_deny.so # deny (order 12400) - # ^cap_net_admin,^cap_new_raw colin - # # include this `none *` line otherwise non-matching users get maximum inheritable capabilities - # none * - # ''; + # brief overview of PAM order/control: + # - rules are executed sequentially + # - a rule marked "optional": doesn't affect control flow. + # - a rule marked "sufficient": on success, early-returns a success value and no further rules are executed. + # on failure, control flow is normal. + # - a rule marked "required": on failure, early-returns a fail value and no further rules are executed. + # on success, control flow is normal. + # hence, supplementary things like pam_mount, pam_cap, should be marked "optional" and occur before the first "sufficient" rule. + # + # pam_cap module args are in pam_cap/pam_cap.c:parse_args: + # - debug + # - config= + # - keepcaps + # - autoauth + # - default= + # - defer + # + # about propagating capabilities to PAM consumers: + # - `setuid` call typically drops all ambient capabilities. + # but setting keepcaps first will preserve the caps across a setuid call + # - pam_cap bug, and fix: + # - may need to use keepcaps + defer: + # security.pam.services.greetd.rules = { + # # 2024/01/28: greetd seems to get its caps from systemd (pid1), no matter what i do. + # auth.pam_cap = { + # order = 12700; + # control = "optional"; + # modulePath = "${pkgs.libcap.pam}/lib/security/pam_cap.so"; + # # args = [ "keepcaps" "defer" ]; #< doesn't take effect + # # args = [ "keepcaps" ]; #< doesn't take effect + # # args = []; #< doesn't take effect + # }; + # }; + security.pam.services.login.rules = { + # keepcaps + defer WORKS + auth.pam_cap = { + order = 12700; + control = "optional"; + modulePath = "${pkgs.libcap.pam}/lib/security/pam_cap.so"; + args = [ "keepcaps" "defer" ]; + }; + }; + # security.pam.services.sshd.rules = { + # 2024/01/28: sshd only supports caps in the I set, because of the keep-caps/setuid issue (above) + # auth.pam_cap = { + # order = 12300; + # control = "optional"; + # modulePath = "${pkgs.libcap.pam}/lib/security/pam_cap.so"; + # args = [ "keepcaps" "defer" ]; #< doesn't take effect + # }; + # }; + # security.pam.services.sudo.rules = { + # 2024/01/28: sudo only supports caps in the I set, because of the keep-caps/setuid issue (above) + # auth.pam_cap = { + # # order = 11500; + # order = 12700; + # control = "optional"; + # modulePath = "${pkgs.libcap.pam}/lib/security/pam_cap.so"; + # args = [ "keepcaps" "defer" ]; #< doesn't take effect + # }; + # }; + security.pam.services.systemd-user.rules = { + # 2024/01/28: systemd-user seems to override whatever pam_cap tries to set (?) + auth.pam_cap = { + order = 11500; + control = "optional"; + modulePath = "${pkgs.libcap.pam}/lib/security/pam_cap.so"; + # args = [ "keepcaps" "defer" ]; #< doesn't take effect + args = [ "keepcaps" ]; + }; + }; + environment.etc."/security/capability.conf".text = '' + # The pam_cap.so module accepts the following arguments: + # + # debug - be more verbose logging things (unused by pam_cap for now) + # config= - override the default config for the module with file + # keepcaps - workaround for applications that setuid without this + # autoauth - if you want pam_cap.so to always succeed for the auth phase + # default= - provide a fallback IAB value if there is no '*' rule + # + # format: + # [,...] USER|@GROUP|* + # + # the part of each line before the delimiter (" \t\n") is parsed with `cap_iab_from_text`. + # so each CAP can be prefixed to indicate which set it applies to: + # [!][^][%] + # where ! adds to the NB set (bounding) + # ^ for AI (ambient + inherited) + # % (or empty) for I (inherited) + # + # special capabilities "all" and "none" enable all/none of the caps known to the system. + + ^cap_net_admin,^cap_net_raw colin + # include this `none *` line otherwise non-matching users get maximum inheritable capabilities + none * + ''; # grant myself extra capabilities so that i can e.g.: # - run wireshark without root/setuid @@ -100,6 +180,13 @@ # # userName and uid have to be explicitly set here, to pass systemd's sanity checks. # other values like `home`, `shell` can be omitted and systemd will grab those from other sources (/etc/passwd) + # + # user records are JSON dicts, keys are found in systemd: src/shared/user-record.c:user_record_load + # notable keys: + # - capabilityBoundingSet + # - capabilityAmbientSet + # - service + # - privileged environment.etc."userdb/colin.user".text = '' { "userName" : "colin",