Massaging the behavior of LDAP-Group caching?
All, I ran into a problem with LDAP-Group and a DN that it was not able to find a friendly name for. I have a function called "group_authz" (loaded from a file in policy.d) that gets called in the post-auth{} section of my site. The function just does a few if comparisons against &LDAP-Group[*] and then updates the reply to the NAS with attributes. I ran into a situation where one of the groups it pulled from a user's memberOf list "did not resolve to an object," which then causes rlm_ldap to return "invalid" and thus rejects the whole attempt. The group it choked on is something used internally by FreeIPA. It's not a group I care about, and not user-created (it's not even visible inside the standard FreeIPA interface). There are actually multiple groups like this that I presume it will complain about, if it wasn't failing at the first one. I was able to work around the problem by trying to match on the full DN instead of the friendly name, but this is obviously a bit harder to read in the config, and I'd prefer to use the friendly names if I can find a way to make it work. Is there a way to tell LDAP-Group to ignore any DN that it finds that it can't "resolve", instead of throwing an "invalid"? Or, is there a way to get the LDAP-Group lookup process to only attempt to resolve DNs that match a specified base/filter? The base_dn in the group{} stanza of the ldap module is already set to the portion of the tree I care about, but LDAP-Group seems to still try to resolve the DNs that aren't in that base (presumably because it's just reading them from "memberOf"). If I have to just stick to matching on full DNs, I will... was just trying to learn if there was a way to deal with this lookup failure gracefully. :-) Snippet of log output in the failure state is below... In this case, I had cacheable_names=yes enabled, but I disabled caching later to test further (caching itself isn't the problem, it's just the unresoveable DN). (0) ldap: Performing search in "cn=users,cn=accounts,dc=foobar,dc=org" with filter "(uid=admin-person)", scope "sub" (0) ldap: Waiting for search result... (0) ldap: User object found at DN "uid=admin-person,cn=users,cn=accounts,dc=foobar,dc=org" (0) ldap: Resolving group DN "cn=admins,cn=groups,cn=accounts,dc=foobar,dc=org" to group name (0) ldap: Performing unfiltered search in "cn=admins,cn=groups,cn=accounts,dc=foobar,dc=org", scope "base" (0) ldap: Waiting for search result... (0) ldap: Group DN "cn=admins,cn=groups,cn=accounts,dc=foobar,dc=org" resolves to name "admins" (0) ldap: Resolving group DN "cn=Replication Administrators,cn=privileges,cn=pbac,dc=foobar,dc=org" to group name (0) ldap: Performing unfiltered search in "cn=Replication Administrators,cn=privileges,cn=pbac,dc=foobar,dc=org", scope "base" (0) ldap: Waiting for search result... (0) ldap: Search returned no results (0) ldap: ERROR: Group DN "cn=Replication Administrators,cn=privileges,cn=pbac,dc=foobar,dc=org" did not resolve to an object rlm_ldap (ldap): Released connection (0) Need 5 more connections to reach 10 spares rlm_ldap (ldap): Opening additional connection (5), 1 of 27 pending slots used rlm_ldap (ldap): Connecting to ldap://ipa1.foobar.org:389 ldap://ipa2.foobar.org:389 ldap://ipa3.foobar.org:389 rlm_ldap (ldap): Waiting for bind result... rlm_ldap (ldap): Bind successful (0) [ldap] = invalid (0) } # authorize = invalid (0) Invalid user (ldap: Group DN "cn=Replication Administrators,cn=privileges,cn=pbac,dc=foobar,dc=org" did not resolve to an object): [admin-person] (from client dc-srv- sw01 port 11526) (0) Using Post-Auth-Type Reject I appreciate any suggestions! Cheers, Braden
On Mar 31, 2021, at 7:08 PM, Braden McGrath via Freeradius-Users <freeradius-users@lists.freeradius.org> wrote:
I ran into a situation where one of the groups it pulled from a user's memberOf list "did not resolve to an object," which then causes rlm_ldap to return "invalid" and thus rejects the whole attempt.
It's difficult to know what to do here. The "right thing" isn't always clear. Especially when the server has to work across multiple different LDAP servers. The simple answer is that FreeIPA is broken, and is returning the wrong things. It shouldn't be returning memberOf fields which don't resolve. That just doesn't make any sense.
I was able to work around the problem by trying to match on the full DN instead of the friendly name, but this is obviously a bit harder to read in the config, and I'd prefer to use the friendly names if I can find a way to make it work.
Sure.
Is there a way to tell LDAP-Group to ignore any DN that it finds that it can't "resolve", instead of throwing an "invalid"?
There's no configuration option for that, sorry.
Or, is there a way to get the LDAP-Group lookup process to only attempt to resolve DNs that match a specified base/filter? The base_dn in the group{} stanza of the ldap module is already set to the portion of the tree I care about, but LDAP-Group seems to still try to resolve the DNs that aren't in that base (presumably because it's just reading them from "memberOf").
Yes, that's what it's doing. The simple answer is to fix FreeIPA. Or, to use the full DN as you've done. Alan DeKok.
(Replies inline) On Thu, Apr 1, 2021 at 5:40 PM Alan DeKok <aland@deployingradius.com> wrote:
On Mar 31, 2021, at 7:08 PM, Braden McGrath via Freeradius-Users <freeradius-users@lists.freeradius.org> wrote:
I ran into a situation where one of the groups it pulled from a user's memberOf list "did not resolve to an object," which then causes rlm_ldap to return "invalid" and thus rejects the whole attempt.
It's difficult to know what to do here. The "right thing" isn't always clear. Especially when the server has to work across multiple different LDAP servers.
The simple answer is that FreeIPA is broken, and is returning the wrong things. It shouldn't be returning memberOf fields which don't resolve. That just doesn't make any sense. I was thinking the same way... I suspect it's something LDAP permissions-related, where those groups are only used internally by FreeIPA. If they are "internal," presumably 389DS (the underlying LDAP server for FreeIPA) doesn't normally expect a "user" (or even a "service" account, which is what my FR server is using) to query for details on those groups, hence it is denying access. I need to go play with ldapsearch to confirm that.
I don't think I want to follow the permission rabbit-hole I'd need to "unhide" these groups from the FR LDAP credentials, though... I guess it depends on what other security implications this might have. Research required.
Or, is there a way to get the LDAP-Group lookup process to only attempt to resolve DNs that match a specified base/filter? The base_dn in the group{} stanza of the ldap module is already set to the portion of the tree I care about, but LDAP-Group seems to still try to resolve the DNs that aren't in that base (presumably because it's just reading them from "memberOf").
Yes, that's what it's doing. I guess another option would be to comment out membership_attribute, but this then requires FR to walk through all the groups looking for a match based on the CN, which is obviously going to be slower than just pulling the group list from memberOf. But since I know all of the groups I'd be searching for will be readable, it should never get to a state where it fails the search (unless I fat-finger the group name in the FR config).
If I disable membership_attribute and fall back to membership_filter and the ensuing string of queries, are those still cached by cacheable_name or cacheable_dn so it doesn't have to run the lookups for each unlang comparison?
The simple answer is to fix FreeIPA. Or, to use the full DN as you've done. I'll dig into FreeIPA a little further, but this may be "by design" and "fixing" might not be worth the headache, especially when using the full DN works. ;-) I can always set up some variables to make it a little more readable.
I appreciate all of the support, and thank you and the rest of the FR contributors for the entire project. As a RADIUS noob, it's... a bit overwhelming at times, but the documentation *is* pretty darn thorough. (I've seen much worse from paid vendors of unrelated products!) It's not FR's fault that the protocol is complicated and a little strange, and your users are all trying to do thousands of different things with millions of different systems. :-) Regards, Braden
On Apr 1, 2021, at 7:48 PM, Braden McGrath <braden@big-geek.net> wrote:
I was thinking the same way... I suspect it's something LDAP permissions-related, where those groups are only used internally by FreeIPA. If they are "internal," presumably 389DS (the underlying LDAP server for FreeIPA) doesn't normally expect a "user" (or even a "service" account, which is what my FR server is using) to query for details on those groups, hence it is denying access. I need to go play with ldapsearch to confirm that.
Then 389DS shouldn't be returning them. :(
If I disable membership_attribute and fall back to membership_filter and the ensuing string of queries, are those still cached by cacheable_name or cacheable_dn so it doesn't have to run the lookups for each unlang comparison?
The LDAP-Group attribute is cached. So it won't do DB queries for every unlang operation. You can check this by running in debug mode.
I appreciate all of the support, and thank you and the rest of the FR contributors for the entire project.
Thanks.
As a RADIUS noob, it's... a bit overwhelming at times, but the documentation *is* pretty darn thorough. (I've seen much worse from paid vendors of unrelated products!) It's not FR's fault that the protocol is complicated and a little strange, and your users are all trying to do thousands of different things with millions of different systems. :-)
Exactly. There's no button labelled "do what I want". :( RADIUS does (WiFi, DSL, Dial-up, VPN) * (EAP-TLS, TTLS, PEAP, PAP, CHAP, MS-CHAP) * (VLAN, IP assignment) * (LDAP, SQL, Active Directory, ...) The combination makes life almost impossible. For what it's worth, I know that a popular Open Source DNS server looked into implementing DHCP. After all, they wrote a complex DNS server, how hard can it be to do DHCP? They got told to look at FreeRADIUS for policies, not ISC. After looking at FreeRADIUS, and the complex policies we support, the DNS guys gave up and decided to stick with DNS. Doing a complex, fast, policy server is *hard*. People use DNS, and expect that that they can do the equivalent to "drop in a zone file, and it just works". This is very, very, far from the truth. In other news, we're looking into implementing DNS in v4. :) It might be ~3K LoC to do the 99% of DNS that most people need. Because all of the policies, configuration file reading, and network IO is already abstracted. i.e. if you build a complex policy server which plugs into every possible database, then *any* networking protocol is easy. Alan DeKok.
I had one very minor thing I ran across during troubleshooting that hopefully someone can answer, but after that question I'm going to provide my results of a deeper dive and "final solutions" for the list archive. When membership_attribute is set, and cacheable_name = yes, the ldap module pops this message in debug output during an access-request: ldap: Adding cacheable user object memberships ldap: &control:LDAP-Group += "foo" [etc, one line per group] ldap: Skipping caching group objects as directive 'group.membership_filter' is not set But later, when actually running an unlang compare against &LDAP-Group[*], FR says: User found. Matched cached membership The comments in the ldap config imply that membership_filter and membership_attribute aren't normally used simultaneously. :) I wasn't seeing additional queries during the actual unlang comparison, so I trust the message that it "Matched cached membership," but that leaves me confused about what "skipping caching group objects as directive 'group.membership_filter' is not set" meant. Anyway, here's my attempt at "Wisdom of the Ancients" (see https://xkcd.com/979/), in case anybody else is trying to tie FreeRADIUS to FreeIPA / 389DS... On Fri, Apr 2, 2021 at 7:08 AM Alan DeKok <aland@deployingradius.com> wrote:
On Apr 1, 2021, at 7:48 PM, Braden McGrath <braden@big-geek.net> wrote:
I was thinking the same way... I suspect it's something LDAP permissions-related, where those groups are only used internally by FreeIPA. If they are "internal," presumably 389DS (the underlying LDAP server for FreeIPA) doesn't normally expect a "user" (or even a "service" account, which is what my FR server is using) to query for details on those groups, hence it is denying access. I need to go play with ldapsearch to confirm that.
Then 389DS shouldn't be returning them. :(
FreeIPA / 389DS returns the "unfiltered" contents of a DN's "memberOf" attribute, even if the LDAP bind account in use doesn't have the ability to directly access each of the individual DNs listed in that attribute. In other words, it will *return* the list of everything a user is a "memberOf," but it won't necessarily allow you to pull more details about each of those DNs (like the friendly name). I don't know enough about LDAP to know if this is "correct" behavior for the memberOf attribute or not, but it's how 389DS seems to work. :) By default, an account able to bind to a FreeIPA server *can* pull all of the groups and users out of the "cn=accounts,dn=example,dn=org" tree. But, FreeIPA uses other sub-trees (cn=pbac and cn=hbac and probably a few more) in order to store its own permissions and other Magic, and a typical bind account with no extra privileges does _not_ have the ability to read those other sub-trees and return the actual names of the groups. A test with ldapsearch will get you a result of "0 Success" with a "numResponses: 1" but no actual LDAP Entries (i.e. no usable data) in the response. FR will Have A Bad Time if it attempts to query those permission groups for short names and gets blank stares from FreeIPA/389DS LDAP server. So, you need to be able to read them *if* you are using friendly/short name matches. (You can ignore all of this if you match on the full DN, because FR doesn't waste its time trying to turn the contents in memberOf into friendly names if it seems like you're doing a full DN match.) Long story short, as long as you don't mind the bind account you're using having *read* access to the names and other details of those FreeIPA permissions / etc, you can add a "Role" of your own naming in FreeIPA, grant it the permission "RBAC Readers" (this is a predefined permission in FreeIPA), and then assign that role to your LDAP bind account(s). The bind account will then be able to read all of the names on these otherwise "hidden" permission groups, and things should work. ;) If you don't want the bind account to have permissions to read about... permissions, the other option is to comment out membership_attribute and use membership_filter *instead*. In this case, FR walks through the group tree, and checks to make sure the user is in each of the groups. In many cases this is probably slower. However, if the user has a lot of these hidden FreeIPA permission groups (which are not part of the normal user-defined groups container/tree, and hence wouldn't be walked when using membership_attribute), and you do _not_ have many user-created groups, the membership_filter may actually have *fewer* LDAP queries involved. Personally, I'm just leaving my config with full DN matches, but I also granted the above role on the bind account in case I ever need to move to friendly name matches in the future. Good luck!
participants (2)
-
Alan DeKok -
Braden McGrath