Greetings.
I have another puzzle with my OpenLDAP configuration, where I'm not sure if what I'm seeing is unexpected.
Short version: should I expect a group in an olcLimits spec to work when the group is dynamic?
I have a dynamic group set up, using the dynlist overlay, which expands to a set of DNs which should be allowed slightly privileged access to a directory. That group seems to be working OK:
% ldapsearch -x -H ldap://localhost:8389 -b o=example -LLL '(cn=ldap-operators)' dn: cn=ldap-operators,ou=groups,o=example cn: ldap-operators objectClass: groupOfURLs description: Members of all of the LDAP admin and tech groups memberURL: ldap:///ou=groups,o=example?member?sub?(|(cn=ldap-admins-*)(cn=ldap-techs)) member: uid=norman,ou=staff,o=example [...]
One goal here is to remove query limits for this group. I can test that by adding an artificially low limit:
olcLimits: group/groupOfURLs/member="cn=ldap-operators,ou=groups,o=example" size=2
If I then make a query which has a few results, I do not get this limit imposed, and instead see in the logs
65c3ce83.0f52bea8 0x16e9d3000 => mdb_entry_get: found entry: "cn=ldap-operators,ou=groups,o=example" 65c3ce83.0f533f90 0x16e9d3000 <= mdb_entry_get: failed to find attribute member
(If, instead of this, I define an ldap-operators group of class groupOfNames, with the above 'member' included explicitly, and make the corresponding change to the olcLimits line, I get what I expect -- ie, a restricted-size response to the query -- which reassures me I'm not doing something stupid elsewhere.)
The slapo-dynlist(5) page says:
Any time an entry with a specific objectClass is being returned, the LDAP URI-valued occurrences of a specific attribute are expanded into the corresponding entries, and the values of the attributes listed in the URI are added to the original entry.
I note the ‘any time’.
My configuration appears to be working for the ldapsearch lookup; I don't see any text in that manpage that suggests this won't work for the (somehow internal?) lookup being done when processing the olcLimits expression.
The page slapd-config(5) says, under olcLimits:
The term group, with the optional objectClass oc and attributeType at fields, followed by pattern, sets the limits for any DN listed in the values of the at attribute (default member) of the oc group objectClass (default groupOfNames) whose DN exactly matches pattern.
That text doesn't seem to me to exclude this entry lookup from the ‘any time’ in the slapo-dynlist text above.
This is OpenLDAP 2.6.7.
I am of course open to a frame-challenge about the best way of achieving the underlying goal.
Best wishes,
Norman
Norman Gray wrote:
Greetings.
I have another puzzle with my OpenLDAP configuration, where I'm not sure if what I'm seeing is unexpected.
Short version: should I expect a group in an olcLimits spec to work when the group is dynamic?
Yes.
I have a dynamic group set up, using the dynlist overlay, which expands to a set of DNs which should be allowed slightly privileged access to a directory. That group seems to be working OK:
% ldapsearch -x -H ldap://localhost:8389 -b o=example -LLL '(cn=ldap-operators)' dn: cn=ldap-operators,ou=groups,o=example cn: ldap-operators objectClass: groupOfURLs description: Members of all of the LDAP admin and tech groups memberURL: ldap:///ou=groups,o=example?member?sub?(|(cn=ldap-admins-*)(cn=ldap-techs)) member: uid=norman,ou=staff,o=example [...]
One goal here is to remove query limits for this group. I can test that by adding an artificially low limit:
olcLimits: group/groupOfURLs/member="cn=ldap-operators,ou=groups,o=example" size=2
If I then make a query which has a few results, I do not get this limit imposed, and instead see in the logs
65c3ce83.0f52bea8 0x16e9d3000 => mdb_entry_get: found entry: "cn=ldap-operators,ou=groups,o=example" 65c3ce83.0f533f90 0x16e9d3000 <= mdb_entry_get: failed to find attribute member
And those logs are correct, the group entry you specified has no member attribute. What it has is a memberURL attribute, and that's what you should have configured in your olcLimits statement.
Howard, hello.
On 7 Feb 2024, at 19:36, Howard Chu wrote:
If I then make a query which has a few results, I do not get this limit imposed, and instead see in the logs
65c3ce83.0f52bea8 0x16e9d3000 => mdb_entry_get: found entry:
"cn=ldap-operators,ou=groups,o=example"
65c3ce83.0f533f90 0x16e9d3000 <= mdb_entry_get: failed to find attribute member
And those logs are correct, the group entry you specified has no member attribute. What it has is a memberURL attribute, and that's what you should have configured in your olcLimits statement.
Aha. I had taken the description to refer to the synthesised 'member' attributes in the dynamically generated group. Thanks for this.
On changing this, though, to
olcLimits: group/groupOfURLs/memberURL="cn=ldap-operators,ou=groups,o=example" size=2
and making a query, I now see in the logs (with -d-1):
65c3df21.21fa70c8 0x16cacf000 ==> limits_get: conn=1000 op=1 self="uid=norman,ou=staff,o=example" this="o=example" 65c3df21.21fa97d8 0x16cacf000 => mdb_entry_get: ndn: "cn=ldap-operators,ou=groups,o=example" 65c3df21.21fab718 0x16cacf000 => mdb_entry_get: oc: "groupOfURLs", at: "memberURL" 65c3df21.21fb1ca8 0x16cacf000 mdb_dn2entry("cn=ldap-operators,ou=groups,o=example") 65c3df21.21fb4b88 0x16cacf000 => mdb_dn2id("cn=ldap-operators,ou=groups,o=example") 65c3df21.21fb8a08 0x16cacf000 <= mdb_dn2id: got id=0x2857 65c3df21.21fbb8e8 0x16cacf000 => mdb_entry_decode: 65c3df21.21fbd440 0x16cacf000 <= mdb_entry_decode 65c3df21.21fbef98 0x16cacf000 => mdb_entry_get: found entry: "cn=ldap-operators,ou=groups,o=example" 65c3df21.21fc0ed8 0x16cacf000 mdb_entry_get: rc=0 65c3df21.21fc2a30 0x16cacf000 ldap_url_parse_ext(ldap:///ou=groups,o=example?member?sub?(|(cn=ldap-admins-*)(cn=ldap-techs))) 65c3df21.21fc7c38 0x16cacf000 => mdb_search 65c3df21.21fcb6d0 0x16cacf000 mdb_dn2entry("o=example") 65c3df21.21fcd9f8 0x16cacf000 => mdb_dn2id("o=example") 65c3df21.21fcf938 0x16cacf000 <= mdb_dn2id: got id=0x1 65c3df21.21fd1490 0x16cacf000 => mdb_entry_decode: 65c3df21.21fd2fe8 0x16cacf000 <= mdb_entry_decode 65c3df21.21fd4b40 0x16cacf000 => access_allowed: search access to "o=example" "entry" requested
There's no mention of 'limits' after this point in the log.
Thus it's finding the right entry and attribute, and parsing the URL therein, but it's not clear what it's concluding. When a search is performed as a user who is included in the synthesised cn=ldap-operators (confirmed by a search for that group), the query results are not limited to 2 objects.
That 2-object limit is what I see in the corresponding configuration when ldap-operators is a groupOfNames with explicit member attributes:
65c3e6ae.1da1a5c8 0x16e80b000 ==> limits_get: conn=1000 op=1 self="uid=norman,ou=staff,o=example" this="o=example" 65c3e6ae.1da1c8f0 0x16e80b000 => mdb_entry_get: ndn: "cn=ldap-operators,ou=groups,o=example" 65c3e6ae.1da1e060 0x16e80b000 => mdb_entry_get: oc: "groupOfNames", at: "member" 65c3e6ae.1da226b0 0x16e80b000 mdb_dn2entry("cn=ldap-operators,ou=groups,o=example") 65c3e6ae.1da24dc0 0x16e80b000 => mdb_dn2id("cn=ldap-operators,ou=groups,o=example") 65c3e6ae.1da28088 0x16e80b000 <= mdb_dn2id: got id=0x2857 65c3e6ae.1da2ab80 0x16e80b000 => mdb_entry_decode: 65c3e6ae.1da2c6d8 0x16e80b000 <= mdb_entry_decode 65c3e6ae.1da2de48 0x16e80b000 => mdb_entry_get: found entry: "cn=ldap-operators,ou=groups,o=example" 65c3e6ae.1da2fd88 0x16e80b000 mdb_entry_get: rc=0 65c3e6ae.1da31cc8 0x16e80b000 dnMatch 0 "uid=norman,ou=staff,o=example" "uid=norman,ou=staff,o=example" 65c3e6ae.1da33c08 0x16e80b000 <== limits_get: type=GROUP match=EXACT dn="cn=ldap-operators,ou=groups,o=example" oc="groupOfNames" ad="member" 65c3e6ae.1da36700 0x16e80b000 => mdb_search 65c3e6ae.1da3bcf0 0x16e80b000 mdb_dn2entry("o=example") 65c3e6ae.1da3e018 0x16e80b000 => mdb_dn2id("o=example") 65c3e6ae.1da3fb70 0x16e80b000 <= mdb_dn2id: got id=0x1 65c3e6ae.1da41ab0 0x16e80b000 => mdb_entry_decode: 65c3e6ae.1da43220 0x16e80b000 <= mdb_entry_decode 65c3e6ae.1da44d78 0x16e80b000 => access_allowed: search access to "o=example" "entry" requested
(interestingly, the string 'limit' doesn't subsequently appear in this -d-1 log, either)
So I'm afraid I'm still puzzled.
Norman
Norman Gray wrote:
Howard, hello.
On 7 Feb 2024, at 19:36, Howard Chu wrote:
If I then make a query which has a few results, I do not get this limit imposed, and instead see in the logs
65c3ce83.0f52bea8 0x16e9d3000 => mdb_entry_get: found entry:
"cn=ldap-operators,ou=groups,o=example"
65c3ce83.0f533f90 0x16e9d3000 <= mdb_entry_get: failed to find attribute member
And those logs are correct, the group entry you specified has no member attribute. What it has is a memberURL attribute, and that's what you should have configured in your olcLimits statement.
Aha. I had taken the description to refer to the synthesised 'member' attributes in the dynamically generated group. Thanks for this.
On changing this, though, to
olcLimits: group/groupOfURLs/memberURL="cn=ldap-operators,ou=groups,o=example" size=2
and making a query, I now see in the logs (with -d-1):
65c3df21.21fa70c8 0x16cacf000 ==> limits_get: conn=1000 op=1 self="uid=norman,ou=staff,o=example" this="o=example" 65c3df21.21fa97d8 0x16cacf000 => mdb_entry_get: ndn: "cn=ldap-operators,ou=groups,o=example" 65c3df21.21fab718 0x16cacf000 => mdb_entry_get: oc: "groupOfURLs", at: "memberURL" 65c3df21.21fb1ca8 0x16cacf000 mdb_dn2entry("cn=ldap-operators,ou=groups,o=example") 65c3df21.21fb4b88 0x16cacf000 => mdb_dn2id("cn=ldap-operators,ou=groups,o=example") 65c3df21.21fb8a08 0x16cacf000 <= mdb_dn2id: got id=0x2857 65c3df21.21fbb8e8 0x16cacf000 => mdb_entry_decode: 65c3df21.21fbd440 0x16cacf000 <= mdb_entry_decode 65c3df21.21fbef98 0x16cacf000 => mdb_entry_get: found entry: "cn=ldap-operators,ou=groups,o=example" 65c3df21.21fc0ed8 0x16cacf000 mdb_entry_get: rc=0 65c3df21.21fc2a30 0x16cacf000 ldap_url_parse_ext(ldap:///ou=groups,o=example?member?sub?(|(cn=ldap-admins-*)(cn=ldap-techs)))
The above URL is not valid for a dynamic group. The attrs portion of the URL must be empty.
Since it's invalid, after it is parsed it gets ignored.
There's no mention of 'limits' after this point in the log.
Howard, hello.
On 8 Feb 2024, at 0:34, Howard Chu wrote:
65c3df21.21fc2a30 0x16cacf000 ldap_url_parse_ext(ldap:///ou=groups,o=example?member?sub?(|(cn=ldap-admins-*)(cn=ldap-techs)))
The above URL is not valid for a dynamic group. The attrs portion of the URL must be empty.
Since it's invalid, after it is parsed it gets ignored.
That's true when constructing what slapo-dynlist(5) calls a dynamic group, but that's not what I'm constructing here, but instead a group entry which is dynamically expanded, to a group, by a search.
Hmm: I realise there's a collision of terminology here, and that I used the specific phrase 'dynamic group' in the first message (but managed to avoid it in the second).
slapo-dynlist(5) does indeed consistently use 'dynamic group' to refer to the case where the olcDynListAttrSet has three values, and the generated entry contains the DNs of the matching entries.
Here, however, I'm using the two-argument case, and defining a group as the union of a number of groupOfNames groups. That's a group which is dynamic, but perhaps 'dynamically generated group' would be a less colliding name than 'dynamic group'.
Anyway...
slapd.ldif:
dn: olcOverlay=dynlist,olcDatabase={3}mdb,cn=config objectClass: olcOverlayConfig objectClass: olcDynlistConfig olcOverlay: dynlist olcDynListAttrSet: groupOfURLs memberURL
and group definition:
dn: cn=ldap-operators,ou=groups,o=example cn: ldap-operators objectClass: groupOfURLs description: Members of all of the LDAP admin and tech groups memberURL: ldap:///ou=groups,o=example?member?sub?(|(cn=ldap-admins-*)(cn=ldap-techs))
When I search for this, I do indeed get a group that looks as I'd hope:
% ldapsearch -x -H ldap://localhost:8389 -b o=example -LLL '(cn=ldap-operators)' dn: cn=ldap-operators,ou=groups,o=example cn: ldap-operators objectClass: groupOfURLs description: Members of all of the LDAP admin and tech groups memberURL: ldap:///ou=groups,o=example?member?sub?(|(cn=ldap-admins-*)(cn=ldap-techs)) member: uid=norman,ou=staff,o=example [...]
That is, the dynlist overlay has synthesised an entry which has a number of 'member' attributes.
That looks good, but this doesn't have the expected effect when I use this group in the olcLimits directive.
olcLimits: group/groupOfURLs/memberURL="cn=ldap-operators,ou=groups,o=example" size=2
(or group/groupOfURLs/member).
Speculation: The objectClass of the above group is groupOfURLs, and not groupOfNames, but the olcLimits documentation mentions groupOfNames only as the default for the /oc element of this spec, and not as a general requirement.
The short version is that if I look at the documentation for olcLimits, it says:
The term group, with the optional objectClass oc and attributeType at fields, followed by pattern, sets the limits for any DN listed in the values of the at attribute (default member) of the oc group objectClass (default groupOfNames) whose DN exactly matches pattern.
Using dynlist, I have synthesised a group with what appear to be the required properties, but olcLimits isn't processing it as I expect. The only difference is that the group is dynamic rather than fixed. What is wrong with my expectation?
Best wishes,
Norman
Norman Gray wrote:
Howard, hello.
On 8 Feb 2024, at 0:34, Howard Chu wrote:
65c3df21.21fc2a30 0x16cacf000 ldap_url_parse_ext(ldap:///ou=groups,o=example?member?sub?(|(cn=ldap-admins-*)(cn=ldap-techs)))
The above URL is not valid for a dynamic group. The attrs portion of the URL must be empty.
Since it's invalid, after it is parsed it gets ignored.
That's true when constructing what slapo-dynlist(5) calls a dynamic group, but that's not what I'm constructing here, but instead a group entry which is dynamically expanded, to a group, by a search.
Whatever you've constructed is not a dynamic group, as defined in slapo-dynlist. As such, it is not supported for the purpose you're asking.
Howard, hello.
On 8 Feb 2024, at 15:07, Howard Chu wrote:
Norman Gray wrote:
Howard, hello.
On 8 Feb 2024, at 0:34, Howard Chu wrote:
65c3df21.21fc2a30 0x16cacf000 ldap_url_parse_ext(ldap:///ou=groups,o=example?member?sub?(|(cn=ldap-admins-*)(cn=ldap-techs)))
The above URL is not valid for a dynamic group. The attrs portion of the URL must be empty.
Since it's invalid, after it is parsed it gets ignored.
That's true when constructing what slapo-dynlist(5) calls a dynamic group, but that's not what I'm constructing here, but instead a group entry which is dynamically expanded, to a group, by a search.
Whatever you've constructed is not a dynamic group, as defined in slapo-dynlist. As such, it is not supported for the purpose you're asking.
Indeed -- it's not a 'dynamic group' in the terms of slapo-dynlist, but it is an entry which has a set of 'member' attributes, which is dynamically constructed (whatever one wants to call this).
But I can't see that matters, since the slapd-config(5) text covering the olcLimits configuration attribute seems to clearly indicate that
olcLimits: group/groupOfURLs/member="cn=ldap-operators,ou=groups,o=example" size=2
'sets the limits for any DN listed in the values of the [member] attribute of the [groupOfURLs] group whose DN exactly matches ["cn=ldap-operators,ou=groups,o=example"]' (where [...] fills in the blanks in the text there as I understand it). I can't see a way of interpreting this manpage text which doesn't match this situation. This works as expected when cn=ldap-operators is an entry which is not dynamically expanded.
It doesn't say that that group has to be a 'dynamic group in the terms of slapo-dynlist', it just says 'group'.
And slapo-dynlist says:
Any time an entry with a specific objectClass is being returned, the LDAP URI-valued occurrences of a specific attribute are expanded into the corresponding entries, and the values of the attributes listed in the URI are added to the original entry.
This is exactly what happens when I ldapsearch the directory for this cn=ldap-operators entry, and what does not happen (because slapd logs that it can't find an attribute 'member') when the same group is returned from a search during processing of olcLimits.
The slapo-dynlist text says 'Any time an entry with a specific objectClass is being returned...'. It doesn't say 'returned in response to an external query', it just says 'returned', which I of course take to include returned in response to an internal query such as this one.
Or, stepping back more, how _should_ I dynamically create an entry which olcLimits will respect? I'm quite happy to be told I'm barking up the wrong tree here. Is OpenLDAP simply unable to do this, or is dynlist expansion documented somewhere as happening only in restricted circumstances?
Best wishes,
Norman
Norman Gray wrote:
Howard, hello.
On 8 Feb 2024, at 15:07, Howard Chu wrote:
Norman Gray wrote:
Howard, hello.
On 8 Feb 2024, at 0:34, Howard Chu wrote:
65c3df21.21fc2a30 0x16cacf000 ldap_url_parse_ext(ldap:///ou=groups,o=example?member?sub?(|(cn=ldap-admins-*)(cn=ldap-techs)))
The above URL is not valid for a dynamic group. The attrs portion of the URL must be empty.
Since it's invalid, after it is parsed it gets ignored.
That's true when constructing what slapo-dynlist(5) calls a dynamic group, but that's not what I'm constructing here, but instead a group entry which is dynamically expanded, to a group, by a search.
Whatever you've constructed is not a dynamic group, as defined in slapo-dynlist. As such, it is not supported for the purpose you're asking.
Indeed -- it's not a 'dynamic group' in the terms of slapo-dynlist, but it is an entry which has a set of 'member' attributes, which is dynamically constructed (whatever one wants to call this).
But I can't see that matters, since the slapd-config(5) text covering the olcLimits configuration attribute seems to clearly indicate that
olcLimits: group/groupOfURLs/member="cn=ldap-operators,ou=groups,o=example" size=2
'sets the limits for any DN listed in the values of the [member] attribute of the [groupOfURLs] group whose DN exactly matches ["cn=ldap-operators,ou=groups,o=example"]' (where [...] fills in the blanks in the text there as I understand it). I can't see a way of interpreting this manpage text which doesn't match this situation. This works as expected when cn=ldap-operators is an entry which is not dynamically expanded.
It doesn't say that that group has to be a 'dynamic group in the terms of slapo-dynlist', it just says 'group'.
And slapo-dynlist says:
Any time an entry with a specific objectClass is being returned, the LDAP URI-valued occurrences of a specific attribute are expanded into the corresponding entries, and the values of the attributes listed in the URI are added to the original entry.
The text above is for a *dynamic list* - which is not a *dynamic group*. The code supports groups, not lists.
Howard, hello.
On 8 Feb 2024, at 16:22, Howard Chu wrote:
And slapo-dynlist says:
Any time an entry with a specific objectClass is being returned, the LDAP URI-valued occurrences of a specific attribute are expanded into the corresponding entries, and the values of the attributes listed in the URI are added to the original entry.
The text above is for a *dynamic list* - which is not a *dynamic group*.
Sure -- no dispute about that.
But we're talking about olcLimits.
The documentation for olcLimits includes the words
the oc group objectClass (default groupOfNames) whose DN exactly matches pattern.
That doesn't say anything about restricting these to 'dynamic groups' (in slapo-dynlist terminology). Those words seem to cover any entry of the designated objectClass which has the designated DN.
The olcLimits declaration I quoted works one way when the entry with the given DN is a static/normal/explicit group, and works a different way when an entry with the same DN and the _same_ set of 'member' attributes is produced on expansion by dynlist. The documentation of olcLimits doesn't suggest that's deliberate.
Again, if OpenLDAP/dynlist is incapable of generating this entry, then that's fine -- I'll bodge some different way of getting what I need.
Best wishes,
Norman
Greetings.
A summary, for the archive and for google....
The missing piece, from my point of view, is that it looks like the group selector, for the olcLimits option (which is what I started off looking at; and see slapd-config(5)) has similar semantics to that for the corresponding olcAccess option, more fully documented in slapd.access(5).
In the documentation of the <who> field there, we learn that 'The statement group=<group> means that access is granted to requests whose DN is listed in the group entry whose DN is given by <group>.' But despite slapo-dynlist saying 'Any time an entry with a specific objectClass is being returned...', this does _not_ apply here, since the next paragraph of slapd.access says 'For dynamic groups the attributeType must be a subtype of the labeledURI attributeType. Only LDAP URIs of the form ldap:///<base>??<scope>?<filter> will be evaluated in a dynamic group, by searching the local server only.' That is, the olcAccess group processing is, in effect, restricted to the three-argument version of the attrset option of slapo-dynlist -- that's what I had missed.
Presuming the olcLimits option has the same restriction, then the effect I was initially aiming to achieve -- setting a limit for members of a particular group which is dynamically populated -- is not possible for me by this route.
The groups I'm aiming to set limits and access for are most naturally defined from the union of other groups. Such groups are easy to define via the two-argument dynlist-attrset value (which uses ldap:///<base>?member?sub?<filter>), but not, as far as I can see, via the three-argument one. I can probably instead synthesise the groups I want, dynamically, by introducing a memberOf attribute attached to the groups' members, but I worry that has the potential to get a little messy in practice; I notice group.expand, which might help.
I notice that the documentation of olcAccess doesn't actually mention the dynlist overlay, and thus may be entirely independent of it. Something for me to investigate.
Best wishes,
Norman
openldap-technical@openldap.org