using multiple LDAP queries for authorization

Arran Cudbard-Bell a.cudbardb at freeradius.org
Fri Dec 19 17:30:28 CET 2014


> On 18 Dec 2014, at 21:45, Coy Hile <coy.hile at coyhile.com> wrote:
> 
> 
> On Dec 18, 2014, at 7:21 PM, Arran Cudbard-Bell <a.cudbardb at freeradius.org> wrote:
> 
>> 
>>> On 18 Dec 2014, at 18:16, Coy Hile <coy.hile at coyhile.com> wrote:
>>> 
>>> Hi all,
>>> 
>>> I admit this is quite a complicated first question, but I'm setting up FR in a lab to try to replace an existing production deployment of a commercial alternative.  Currently, we can permission a user for access to some network device via the following tuples (in order of less specificity): (user, device) (user, group of devices), (group of users, device) (group of users, group of devices).  So we say, effectively in pseudocode:
>>> 
>>> For authorization:
>>> Check if a user with uid=%{User-Name} exists and return the user's group
>>> 
>>> if exists(acl(user, device)) {
>>>   based on the assigned access profile, query LDAP for the approopriate VSAs
>>> } else if exists(acl(user, group of devices)) {
>>>   based on the assigned access profile, query LDAP for the approopriate VSAs
>>> } else if exists(acl(usergroup, device)) {
>>>   ....
>>> } else if exists(acl(usergroup,group of devices)) {
>>>   ....
>>> } else
>>>   return reject
>>> 
>>> For authentication:
>>> Kerberos
>>> 
>>> 
>>> The authentication part is trivial, as is the first check under authorization; the existing documentation explains how to do that sort of check.  I can see from the unlang manpage how to call out to one or the other modules.  Is what I'm trying to do something one can do with rlm_ldap, or is it something that would be better done with rlm_python.  (Yes, LDAP in python sucks rocks through straws, so I'm trying to avoid that if possible.
>> 
>> It's not clear what you mean by device.
>> 
>> Do you mean infrastructure devices, as in restricting a user so they can only
>> log in from specific locations, or devices as in laptops, phones, workstations?
>> 
> 
> Infrastructure devices, such as routers, switches, terminal servers, and the like. Users may be able to login to one class of devices (such as SAs being able to login to term servers that have system serial consoles on them, but not on any network devices). Or, for example, a user may be able to login to all network devices classified as being in a particular network cloud with ‘operations’ privilege by virtue of his role, but he could have special superuser privileges on lab-router.
> 
> I could use the radiusGroup LDAP attribute to match a user’s UserGroup, and I could probably do the same for a device object which also has ipHostNumber and a field that would tell the type of device (cisco, juniper, nexus, arista, etc) 
> 
> 
>> Were the ACLs represented in LDAP by the previous vendor or is LDAP a new 
>> requirement?
>> 
> 
> The ACLs are currently in LDAP; the only thing that I’d be adding here when setting this up in the lab is the use of the Kerberos password for authentication rather than the userPassword stored in LDAP.
> 
> 
>> If there's an existing schema please provide it, then i'll be able to tell you
>> if it's possible with the current rlm_ldap module in v3.0.x.
> 
> I can’t actually provide the schema, but the ACLs are of the form:
> 
> cn=<user>-<device>,OU=ACLs,dc=foo
> and has an attribute called that we’ll call accessProfile which is a token such as ’superuser’, ‘operator’, ‘readonly’.
> 
> Given a deviceClass=cisco and profile=superuser, I’d have the server return an shell:prig-lvl=15

OK, so the operations required would be something like:

try and retrieve the radiusProfile object cn=<user>-<device>,OU=ACLs,dc=foo

if that fails, get full group membership of device

search in OU=ACLs,dc=foo for radiusProfiles with filter (|(cn=<user>-<device_group_membership0>)(cn=<user>-<device_group_membershipN>))

if that fails, get full group membership of user

search in OU=ACLs,dc=foo for radiusProfiles with filter (|(cn=<user_group_membership0>-<device>)(cn=<user_group_membershipN>-<device>))

if that fails

search in OU=ACLs,dc=foo for user_group_membership x device_group_membership.

One of the things rlm_ldap can't do, is build filters for N number of items. The filter 
construction is static, though elements of it are dynamically expanded.

e.g. (cn=%{User-Name})

will expand to (cn=foo)

but My-Filter := "cn=foo"

(%{My-Filter})

will expand to something like (cn\=foo)

The other thing it can't do is query for radiusProfiles, it expects them to be specified (as DNs) by attributes in the user object.
But that's easy to get around, by treating the ACLs like user objects.

So really it's the lack of dynamic filter construction which is the killer here. Other people have also complained about it, it's 
definitely an issue, but not one that's easy to resolve.

If the number of ACLs are small < 100 and change fairly infrequently, you could pull back the DNs of all the ACLs, and cache them
using rlm_cache. There's also a command using the control socket to force the cache to be emptied, so if you have a front end 
application that pokes the ACLs, you could have it poke FreeRADIUS at the same time, to ensure they go live immediately.

You could then do all the comparisons locally, which may well be faster anyway.

The only enhancement required for rlm_ldap there, is to be able to map the DN of the ACL object to an internal attribute using an 
update section. Your LDAP directory may support https://tools.ietf.org/html/rfc5020, and provide the entryDN operational attribute,
in which case no additional functionality would be required.

Hmm, actually, I can't see a problem with adding the code to call ldap_get_dn if entryDN were requested but not provided by the 
directory, what do other people think?

-Arran

Arran Cudbard-Bell <a.cudbardb at freeradius.org>
FreeRADIUS development team

FD31 3077 42EC 7FCD 32FE 5EE2 56CF 27F9 30A8 CAA2



More information about the Freeradius-Users mailing list