- 1 The problem
- 2 The Solution
- 2.1 Task Groups and ACIs
- 2.2 Role Groups
- 2.3 The bottom line
- 2.4 ACI Details
- 2.5 Use Cases
- 3 Additional UI Capabilities Needed
- 4 Additional Possible Capabilities
- 5 Current State of Affairs
- 6 Who writes the ACIs, tasks and roles?
- 7 How will ACIs be created?
- 8 How will ACIs be modified?
- 9 Diagram
- 10 Examples using the aci commands
- 11 Limitations
- 12 Debugging
Fine-grained access to parts of the DIT
We need to be able to say WHO can do WHAT to WHOM
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
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:
The interesting rights that FDS provides that IPA would use are: read, write, add, delete, search, and selfwrite.
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.
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.
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
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.
- 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
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)
This will always be a Task group.
Will be: read, write, add, delete, search, and/or selfwrite
All ACIs will GRANT access, not deny it.
The target may be a set of attributes, a portion of the subtree a filter or a combination of these.
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=*,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";)
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")(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";)
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
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";)
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=*,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";)
We may need to add in specific ACIs that prevent the deletion of specific users and groups. admin comes to mind.
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 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
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
- 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
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
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.
A task group has 3 attributes:
- cn (the group name)
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
A role group has 3 attributes:
- cn (the group name)
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:
- time of day
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:// /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:
- 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.
A picture for your viewing pleasure.
- 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
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:
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.
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).
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.
Problem: I want HR to be able to update anyone's address.
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.
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.
- 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
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.