guix-devel
[Top][All Lists]
Advanced

[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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]