Delegation#
The problem#
Fine-grained access to parts of the DIT
We need to be able to say WHO can do WHAT to WHOM
Writes#
The WHO can be:
a user or users
a group or groups
The WHAT can be:
modify one or more attributes on an entry (ex.)
reset a password
modify an address
add a user to a group
add a new entry
remove an existing entry
The WHOM can be:
a user or all users
a group or groups
self
Reads#
Any user can read any other user or group. This is simply the unix way. Some attributes such as password will always be protected. This was extended to services in IPA v1 and will continue in v2 so one can see what services are available.
We will not by default grant read access to:
Machines
Policy
Other rights#
The interesting rights that FDS provides that IPA would use are: read, write, add, delete, search, and selfwrite.
Anonymous Access#
Currently there is read access to the entire subtree (minus a few important attributes like userPassword) for anonymous users. In order to limit access to services, machines, etc. I propose that we modify the anonymous read access to include just users and groups. Basically just enough so that nss_ldap will still work with an anonymous bind.
The Solution#
From a high level we will:
Create groups (task groups) that are allowed to perform tasks
Create an ACI that grants permission for a group to perform a specific task
Create groups (role groups) that will be be members of task groups, granting the role group the underlying permission
Assign users and groups to role groups
Task Groups and ACIs#
For the purposes of this discussion one task == one ACI “group”. These groups will be stored outside of cn=accounts so they don’t interfere with standard groups. I propose cn=taskgroup, dc=SUFFIX. The structural objectclass will be groupOfNames.
1 task == 1 ACI
There may be multiple ACIs for a given high level task but it will be implemented by combining low level tasks.
Take adding a user as an example. When a user is added we also automatically add that user to the default IPA group (ipausers). For the “Add a user” task we will need 3 ACIs:
one to allow creating users and one to allow adding a user to a group (access to the member attribute).
one to allow writing the member attribute of a group
allow the a password reset
A 3rd task group may be created, “Add an IPA User”, that is a member of these 3 task groups, combining them into a logical task. This will help limit the total number of ACIs in the system, particularly with duplication, because a task may be a member of several groups.
The ACIs will be stored in cn=accounts, the same way as in v1.
Role Groups#
We will also have role groups. A role is a collection of tasks or other roles. These will be things like helpdesk, user admin, group admin, replica admin, etc. These will also be stored outside of the cn=accounts tree. These groups are for data management only and not for system management. Perhaps in cn=delegationgroup, dc=SUFFIX. The structural objectclass will be groupOfNames.
A role here is not to be confused with RBAC or native FDS/RHDS roles. It is simply a term I’m using to designate the permissions of an administrative group.
Rights are assigned by adding tasks as a member to these role groups. So a helpdesk user may be able to see users, groups, and systems but only have write access on user passwords. We would have several ACIs/task groups to handle this:
reset password
view users
view groups
view systems
An IPA install will include a default set of canned tasks, including things like:
Add/Delete user
Add/Remove user to group
View Machines
Change password
etc
Each one of these is a discrete ACI granting the appropriate permission to a unique group (like add_user group, add user_to_group, etc). The task group name itself is unimportant, it just needs to be unique and hopefully meaningful.
We will continue to have the admin user and the admins group. This is the super-user and group of users that can do whatever they would like in the IPA world.
The bottom line#
An ACI grants permissions to a task group. There is a single ACI for each task group. One may group ACIs together by adding their task groups the same group.
A role is the member of one or more task groups
A user/group/role is a member of one or more roles
ACI Details#
An ACI is made up 3 major components that we’re interested in:
source/bind rule (WHO is being granted access)
rights (read, write, etc) (WHAT is being granted)
target (WHO you are granting rights to)
Source#
This will always be a Task group.
Rights#
Will be: read, write, add, delete, search, and/or selfwrite
All ACIs will GRANT access, not deny it.
Target#
The target may be a set of attributes, a portion of the subtree a filter or a combination of these.
Examples#
Note that in these examples I’m not using the new location to store the task groups. You can apply these to a v1 IPA server to see how it works.
Create a new user#
aci: (target="
`ldap:///uid=
<ldap:///uid=>`__*,cn=users,cn=accounts,dc=example,dc=com")(version 3.0;acl "add_user";allow (add) groupdn="
`ldap:///cn=add_user,cn=taskgroups,dc=example,dc=com
<ldap:///cn=add_user,cn=taskgroups,dc=example,dc=com>`__";)
But this isn’t enough. We also add the new user to the default IPA group. Here is an ACI which allows that, specifically limiting the write operation to the default group. This would be difficult to keep in sync in reality but illustrates how tight we can make things.
aci: (targetattr=member)(target="
`ldap:///cn=ipausers,cn=groups,cn=accounts,dc=example,dc=com
<ldap:///cn=ipausers,cn=groups,cn=accounts,dc=example,dc=com>`__")(version 3.0;acl "add_user_to_default_group";allow (write) groupdn="
`ldap:///cn=add_user_to_default_group,cn=taskgroups,dc=example,dc=com
<ldap:///cn=add_user_to_default_group,cn=taskgroups,dc=example,dc=com>`__";)
And still this isn’t enough. We also try to set the password. Rather than doing this by setting the userPassword attribute we do an LDAP password change. So we need to grant permission to change passwords as well (see Reset password).
So now we have 3 task groups to add a user. If we wanted we could create a 4th task group which combines these as a shortcut. This shows that good descriptions will be required so that people making delegations can understand what each task does. So this combined task should be named something like create_ipa_user.
The task entry for the add_user ACI will look like:
dn: cn=add_user,cn=taskgroups,dc=example,dc=com
objectclass: top
objectclass: groupofnames
cn: add_user
description: Allowed to add new users
member: uid=tuser,cn=users,cn=accounts,dc=example,dc=com
The task entry for create_ipa_user will look like:
dn: cn=create_ipa_user,cn=taskgroups,dc=example,dc=com
objectclass: top
objectclass: groupofnames
cn: create_ipa_user
description: Allowed to create IPA users
member: cn=add_user,cn=taskgroups,dc=example,dc=com
member: cn=add_user_to_default_group,cn=taskgroups,dc=example,dc=com
member: cn=change_password,cn=taskgroups,dc=example,dc=com
Reset password#
Crafting some rules may require a fairly detailed knowledge of LDAP and the IPA implementation, as demonstrated with this long list of attributes that may be written when resetting a password.
aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "change_password"; allow (write) groupdn = "
`ldap:///cn=change_password,cn=taskgroups,dc=example,dc=com
<ldap:///cn=change_password,cn=taskgroups,dc=example,dc=com>`__";)
Remove user#
Once the basic structure of the ACIs is found then granting specific rights becomes easier and easier. This is the same ACI as add_user simply with a different right. One would also need to be a member of the “modify group membership” group so that the membership may be modified.
aci: (target="
`ldap:///uid=
<ldap:///uid=>`__*,cn=users,cn=accounts,dc=example,dc=com")(version 3.0;acl "delete_user";allow (delete) groupdn="
`ldap:///cn=delete_user,cn=taskgroups,dc=example,dc=com
<ldap:///cn=delete_user,cn=taskgroups,dc=example,dc=com>`__";)
Side effects#
We may need to add in specific ACIs that prevent the deletion of specific users and groups. admin comes to mind.
Roles#
These ACIs will be rolled up into a set of Roles, a set of which will be pre-defined when IPA is shipped. These roles can then be customized by IPA administrators to fit the site needs.
Helpdesk
Helpdesk users can typically reset passwords.
So we start with a helpdesk role:
dn: cn=helpdesk,cn=rolegroups,dc=example,dc=com
objectclass: top
objectclass: groupofnames
cn: helpdesk
description: Helpdesk
member: uid=tuser,cn=users,cn=accounts,dc=example,dc=com
And add that role to the task for changing passwords:
dn: cn=change_password,cn=taskgroups,dc=example,dc=com
objectclass: top
objectclass: groupofnames
cn: create_ipa_user
description: Allowed to change passwords
member: cn=helpdesk,cn=rolegroups,dc=example,dc=com
Use Cases#
Separate admins for separate containers#
Currently all users are in cn=users,cn=accounts. If we allow users to be created in another part of the tree (aka another user container) then we can create per-container admins, granting full access to this container to that admin.
Alternatively we can grant access based on the value of an attribute in
a record that isn’t part of the DN using targetfilter
to set the
target based on the value of an attribute:
(targetfilter = "(|(ou=accounting)(ou=engineering))")
Limit self-service changes by attribute#
Add ability to limit what attributes can be modified on the self-service page
Flexibility in attributes that may be delegated#
The list of attributes that one can grant write access to needs to be configurable
Delegate entry (user, group, whatever) creation#
In v1 we only delegate attribute writes, not add or delete permissions. Some granularity can be obtained by granting access only to users (cn=users), groups (cn=groups), etc. along with attributes.
Additional UI Capabilities Needed#
Means to select entries by container (if supported)
Means to select one or more entries (could be users or groups or both)
Means to manage list of attributes that may be delegated
Means to manage add/list/delete delegations
Delegate anything#
add users/groups/systems/other
delete users/groups/systems/other
allow arbitrary attributes (potential for abuse, breakage?)
An admin is a special kind of delegation, need a way to recognize this in the UI
New ACI Parser#
a fuller ACI class that can handle more complex syntax
Needs to understand LDAP target
Ability to set source to targetattr, targetfilter and/or target
Set rights as a list
Validate ACIs before they are written
UI Requirements#
It is difficult to select an individual ACI over LDAP. What we will do instead is slurp in all of them and prove that to the UI to display. This should be refreshed between operations to avoid concurrency issues.
Once an ACI is written to LDAP it is immediately in effect.
The following operations are needed:
CRUD for managing Task groups
CRUD for managing ACIs
CRUD for managing Role groups
ACI#
An ACI has 4 attributes:
name - a description of the ACI
source/bind rule - who is being authorized. This will generally be a task group*
rights - read, write, add, delete, search, and selfwrite (may be more than one)
target - May specify whether = or != one or more of the following:
target - an LDAP uri pointing at a specific entry or a subtree
targetattr - one or more attributes
targetfilter - an LDAP filter
There are a couple of special LDAP bind rules:
userdn = “ldap:///self”
userdn = “ldap:///anyone”
self is used when defining an ACI for self-service. These are things that you can do in your own record.
anyone is any bind, including an anonymous one.
Task Groups#
A task group has 3 attributes:
cn (the group name)
description
member
A member is a role group(s)
The membership of task groups will be read only. This will be managed from the Role Groups. Otherwise may seem a bit backwards. What we are doing with Role groups is defining what tasks a role may execute. To do that we add the Role to the task group.
How to create a new Task#
Create a new task group for the task
Create an ACI and assign it to the task group you just created
Role Groups#
A role group has 3 attributes:
cn (the group name)
description
member
A member can either be another role group, a group or a user.
The Add/Update operations need to provide the ability to manage membership of the task group. This defines the users/groups/roles that may do the tasks associated with the role.
It also needs to provide the ability to manage which tasks a role may operate on. By adding a task to a role the role gets added as a group member of the task.
Additional Possible Capabilities#
These would be for v3 or beyond.
Limit Bind Rules#
We can add on additional bind rules for making changes if desired by:
IP
time of day
IP
hostname
Current State of Affairs#
Currently all ACIs are put into cn=accounts,dc=example,dc=com and can grant the ability to write a fixed set of attributes from one group of users to another.
aci: (targetattr="title")(targetfilter="(memberOf=cn=bar,cn=groups,cn=accounts
,dc=example,dc=com)")(version 3.0;acl "foobar";allow (write) groupdn="``\ ```ldap://`` <ldap://>`__
/cn=foo,cn=groups,cn=accounts,dc=example,dc=com";)
Who writes the ACIs, tasks and roles?#
Plugin authors, who know best what access may be granted for their given operations, will create a list of ACIs for the plugin. This will likely revolve around the CRUD operations to grant create, read, update and delete access. There are special cases too, such as granting write access to specific attributes in the case of passwords.
A predefined set of Roles will be created as well. The initial list will be:
Helpdesk
User admin
Group admin
Replica admin
Host admin
Service admin
CA admin
Netgroup admin
automount admin
netgroups admin
How will ACIs be created?#
There will be no web UI for creating new ACIs.
How will ACIs be modified?#
The only modifications to ACIs will be in the list of attributes that they cover and will only be available from the CLI.
For example, the edit User ACI may need to be expanded to include more attributes than we grant by default. This CLI capability will let an admin select from a set of attributes those whihc may be written.
Diagram#
A picture for your viewing pleasure.
Remember:
An ACI grants access to an operation to a single Task group
A Role is a member of one or more Task groups
A user, group or Role is a member of one or more Roles

Picture#
Examples using the aci commands#
The ACI rules that are shipped with IPA were all created using the api plugin commands. This command should be used with great caution as you are granting access to objects within IPA.
There isn’t exactly a 1-1 relationship between ACI’s and tasks but for the most part this is true and thinking of it this way generally simplifies things.
For a thorough description of ACIs you should read Creating ACIs Manually
Adding an ACI#
An ACI has 3 basic components and here are the aci-add options that manage them:
target#
The target of the ACI tells us which entries are controlled by the ACI. In general the more specific you can be the better.
attrs - controls the attributes you are granting access to
type - a method to apply controls across a broad set of objects. Possible options are: user, group and host
memberof - matches members of a group. Enter the group name
filter - Takes any valid LDAP filter to target the ACI. For example, (ou=engineering)
subtree - A subtree the ACI applies to. This is effectively what the type option does.
targetgroup - target a specific group, similar to memberof.
permissions#
The permissions option tells the ACI what access you are granting
write - can update existing entries, limited to a list of attributes if used with –attrs
read - used to grant read access to attributes that might not otherwise be available (potentially dangerous as you could grant read access to the password, for example).
bind rule#
The bind rule tells who this ACI is granting access to.
taskgroup - A taskgroup, this is the model. The taskgroup will be added automatically if it does not exist (beware of typos)
group - Grant access to a group of users directly.
Examples#
Problem: I want HR to be able to update anyone’s address.
ACI:
ipa aci-add --taskgroup=updateaddress --attrs=street,st,postalcode,telephonenumber --permissions=write --type=user "Update Address"
Let’s say you have an HR>
role group. You would add it as a member
of the taskgroup updateaddress
. This will grant all members of the
HR role to modify addresses.
Problem: I want to delegate the management of hosts.
ACI:
ipa aci-add --taskgroup=hostmgmt --permissions=write,add,delete --type=host --attr=description,localityname,nshostlocation,nshardwareplatform,nsosversion "Host Management"
This will grant someone the ability to add, remove and update hosts.
Limitations#
The aci plugin uses raw LDAP attributes. You can see the actual attribute names in the plugin source
The output from the plugin is raw Directory Server ACIs which can be a little difficult to understand at first
Debugging#
If you’ve created an ACI and it isn’t working as expected, one option is to enable ACI debugging in the Directory Server.
Fair Warning: This will have a tremendous performance impact on your server. Do not do this on an operational system if you can avoid it.
You need to set nsslapd-errorlog-level to 128 in cn=config, like:
% ldapmodify -x -D "cn=Directory Manager" -W
dn: cn=config
changetype: modify
replace: nsslapd-errorlog-level
nsslapd-errorlog-level: 128
Set it back to 0 to disable debugging. Your DS errors log file will fill up with ACI evaluations. The last entry should give you a pointer to where the denial is originating.