[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: LDAP authentication + Configuring PAM
From: |
Ricardo Wurmus |
Subject: |
Re: LDAP authentication + Configuring PAM |
Date: |
Fri, 23 Aug 2019 22:08:14 +0200 |
User-agent: |
mu4e 1.2.0; emacs 26.2 |
Hi Ludo,
> Ricardo Wurmus <address@hidden> 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