Customizing OpenStack RBAC policies
OpenStack uses a role based access control (RBAC) mechanism to
manage accesses to its resources. With the current architecture,
users' roles granted on each project and domain are stored into
Keystone, and can be updated through Keystone's API. However,
policy enforcement (actually allowing or not the access to resources
according to a user's roles) is performed independently in each
service, based on the rules defined in each policy.json
file.
In a default OpenStack setup (like Devstack), two roles are created:
-
The
Member
role, which when granted to a user on a project, allows him to manage resources (instances, volumes, ...) in this project. -
The
admin
role, which when granted to a user on any project, offers to this user a total control over the whole OpenStack platform. Although this is the current behavior, it has been marked as a bug.
However, the OpenStack policy engine allows operators to specify fine grained set of rules to control access to resources of each OpenStack service (Keystone, Nova, Cinder, ...).
Attributes available to build custom policies
Four types of attributes can be used to set policy rules:
-
User roles, which can be checked by using the following syntax:
role:<requires_role>
-
Other user related attributes (stored into or obtained through the token). The following attributes are available: user_id, domain_id or project_id (depending on the scope), and can be checked against constants or other attributes:
project_id:<some_attribute>
-
API call attributes are any data sent along with the API call. They can be checked against constants or user attributes. For instance, the following statement checks that a user being created is in the same domain as his creator (note that API call attributes have to be on the right side of the expression, while user attributes are on the left side):
domain_id:user.domain_id
-
The fourth category of attributes are what I'd call contextual attributes. These are the attributes of objects referenced (or targeted) by an API call; i.e. any object whose id appear somewhere in the API call. For instance, when granting a new role on a project to a user, all attributes related to the role, the project and the user are available to the policy engine, through the
target
keyword. The following syntax checks that the role of the context is theMember
role:'Member':target.role.name
Depending on the type of API calls, some of the following attributes will be available, according to the objects impacted by the action:
-
domain:
- target.domain.enabled
- target.domain.id
- target.domain.name
-
group:
- target.group.description
- target.group.domain_id
- target.group.id
- target.group.name
-
project:
- target.project.description
- target.project.domain_id
- target.project.enabled
- target.project.id
- target.project.name
-
role:
- target.role.id
- target.role.name
-
user:
- target.user.default_project_id
- target.user.description
- target.user.domain_id
- target.user.enabled
- target.user.id
- target.user.name
Example: admin and super_admin
The following example is taken from a User Story that we were considering at CloudWatt. As a cloud service provider, we wanted to be able to have 2 different levels of administrator roles:
- An
admin
role, which allows its users to grant theMember
role to any other user. - While the
super_admin
role allows granting any role.
When added to Keystone's ̀policy.json
file, the following rules
implements the two roles described previously:
"admin_grant_member": "role:admin and 'Member':%(target.role.name)s",
"identity:create_grant": "role:super_admin or rule:admin_grant_member",
The first rule describes a new rule called admin_grant_member
, which
checks that the user authenticated by the token has the admin
role
(on its scope), and that the role in the context (the role the admin
is trying to grant) is the Member
role (we used the name
attribute, but could use the role's id instead).
The second rule is checked whenever an API call is made to grant a
role to a user (action identity:create_grant
). This rule tells the
policy engine that in order for a user to be allowed to grant a role
to another user, the user authenticated by the token must either have
the super_admin
role, or satisfy the admin_grant_member
rule.
Put together these two rules actually meet the use case. Any user with
the admin
role will only be able to grant the Member
role to other
users, while users with the super_admin
role will be able to grant
any role.
Notes
One of the most powerful rules that the OpenStack policy engine allows, are those limiting a user's actions to his own domain or project. These kind of rules are widely used in Keystone's policy.v3cloudsample.json.
Also note, that a recent patch merged into oslo-incubator implements the blueprint allowing the policy engine to check contextual attributes against constant values. This patch will have to be synchronized into the OpenStack projects for them to benefit from this feature.