Hi Ludo,
> Ricardo Wurmus <rek...@elephly.net> skribis: > >> My findings so far: >> >> * NSCD *must* be extended with caches for “passwd” and “group” >> databases or else applications will make the lookup for user accounts >> directly and will not consult LDAP at all. > > Nscd already has caches for those in our default configuration, doesn’t > it? No, it only provides caches for “hosts” and “services” by default. I think this should be changed as it seems that NSCD will not use plugins when it doesn’t also cache the resource. >> * it’s a pity that “name-service-switch” is not a system service and has >> to be extended manually. … Or is this perhaps how we should treat PAM >> configuration as well? > > We should turn ‘name-service-switch’ into an extensible service, IMO. > It just happens to predate the service type graph. I think so too. >> But back to the problem of authenticating users via LDAP. With our >> default unix-pam-service “pam_unix.so” (which checks that a user account >> exists locally and checks its password) is required, so LDAP >> authentication will always fail. >> >> I extended the pam-root-service-type with a service that matches on all >> pam_unix.so modules and makes them use the “sufficient” keyword instead >> to overcome this. > > So you’re extending ‘pam-root-service-type’ with a “transformation” > procedure, right? I first wanted to respond that I did, but I forgot about “pam-configuration” and its “transform” field. (I didn’t find it in the manual.) Instead I used an extension procedure of the pam-root-service-type. Here’s the actual definition that I used. It’s a bit long. --8<---------------cut here---------------start------------->8--- (define nslcd-custom-pam-rules-service-type (let* ((my-pam-ldap-pam-service (lambda (config) "Return a PAM service for LDAP authentication." (define pam-ldap-module #~(string-append #$((@@ (gnu services authentication) nslcd-configuration-nss-pam-ldapd) config) "/lib/security/pam_ldap.so")) (lambda (pam) (if (member (pam-service-name pam) ((@@ (gnu services authentication) nslcd-configuration-pam-services) config)) (let ((sufficient (pam-entry (control "sufficient") (module pam-ldap-module) (arguments '("minimum_uid=1000" "try_first_pass" "ignore_unknown_user")))) (auth (pam-entry (control "sufficient") (module pam-ldap-module) (arguments '("minimum_uid=1000" "try_first_pass")))) (session-mkhomedir (pam-entry (control "required") (module "pam_mkhomedir.so") (arguments '("skel=/etc/skel" "umask=0022" "minimum_uid=1000" "debug")))) (make-unix-sufficient (lambda (entry) (match (pam-entry-module entry) ("pam_unix.so" (pam-entry (inherit entry) (control "sufficient"))) (_ entry)))) ;; This is really ugly. We have to do this because ;; pam-entry-module may return a G-expression and ;; there's no easy way for us to match the module name ;; any other way. (ldap-entry? (lambda (entry) (match (pam-entry-module entry) ((? gexp? g) (match (lowered-gexp-sexp (with-store store (run-with-store store (lower-gexp g)))) (('string-append _ tail . rest) (string-suffix? "pam_ldap.so" tail)))) (_ #f))))) ;; Replace any existing pam_ldap.so entry and make the unix "sufficient" instead of "required" so that ;; LDAP authentication has a chance. (pam-service (inherit pam) (auth (cons auth (filter (negate ldap-entry?) (map make-unix-sufficient (pam-service-auth pam))))) (session (cons session-mkhomedir (filter (negate ldap-entry?) (map make-unix-sufficient (pam-service-session pam))))) (account (cons sufficient (filter (negate ldap-entry?) (map make-unix-sufficient (pam-service-account pam))))) (password (cons sufficient (filter (negate ldap-entry?) (map make-unix-sufficient (pam-service-account pam))))))) pam)))) (my-pam-ldap-pam-services (lambda (config) (list (my-pam-ldap-pam-service config))))) (service-type (name 'nslcd-custom-pam-rules) (description "Install PAM records to enable authentication with LDAP.") (extensions (list (service-extension pam-root-service-type my-pam-ldap-pam-services)))))) --8<---------------cut here---------------end--------------->8--- I use it like this in the “services” field: --8<---------------cut here---------------start------------->8--- ;; Run NSLCD and bind to Active Directory (service nslcd-service-type ldap-config) (service nslcd-custom-pam-rules-service-type ldap-config) --8<---------------cut here---------------end--------------->8--- The both share the same LDAP configuration only for nslcd-configuration-nss-pam-ldapd and nslcd-configuration-pam-services. >> This is problematic when nslcd-service-type is involved because it >> extends pam-root-service-type to add entries for pam_ldap.so. Instead >> of using a string “pam_ldap.so” it uses a G-expression to compute the >> absolute file name of “pam_ldap.so”. When extending the service and >> matching on PAM entries, however, we don’t have a string of the >> absolute file name to match on — we have a G-expression that is really >> awkward to match against. > > Could you match against a <file-append> record? Perhaps, I haven’t tried yet. >> I worked around this (by lowering the G-expression first), but it’s >> ugly. And even then I still have the problem that I can’t control the >> order of PAM entries at all. > > Perhaps we could add a field in <pam-configuration> that would be > transformation procedure that takes the complete list of entries? I don’t know if that makes sense without a guaranteed order. >> Perhaps we should implement a different mechanism for specifying PAM >> entries for the system, perhaps similar to what the name-service-switch >> field does? > > We could also allow users to specify > > (service pam-root-service-type (pam-configuration …)) > > in their ‘services’ field. Would that help? I think it would. It would be annoying, though, that I would have to essentially re-create the pam entries that would otherwise be provided by other services. I wonder if we could tag pam entries with the service that provided them, so that one could more easily match on them and rearrange them as needed in a finalizer procedure that is guaranteed to be invoked at the very end, after all other extensions to the pam-root-service-type have been evaluated. I’m thinking of this as a big pile of pam entries that individual services contribute to and that the user can re-arrange and perhaps modify. >> I also recommend using “sufficient” as the default keyword for >> “pam_unix.so” and ending the stack with “required pam_deny.so”. This >> would make it easier to extend the stack without having to rewrite >> existing module entries. > > Why not. As far as I can tell this should not have any downsides. > Tricky issues! NixOS has lots of hard-coded cases instead of a generic > way to extend PAM settings: > > > https://github.com/NixOS/nixpkgs/blob/release-19.03/nixos/modules/security/pam.nix#L304 I prefer the Guix way here. It is more generic and more flexible. It just misses a few convenience procedures, in my opinion. > From what you wrote, it may be that PAM configuration is simply not > “composable”, in the sense that you cannot assemble bits without viewing > the global picture. I think individual services cannot generically extend the PAM configuration, because they cannot know what order is correct with respect to all other services that contribute to the configuration. But they *can* provide at least their own entries. -- Ricardo