Yesterday, someone asked me about inherited role assignments in Keystone projects. Here is what we worked out.



For access control to resources in Nova or other remote services, all that matters is the roles that the user has on the project. Domains are a Keystone only concept.

Before Keystone adopted the concept of Domains, users were “owned” by projects/tenants. When we added domains, we decided that DOmains owneder users, and users were only assigned roles in projects.

Yeah, this is confusing.

This operator wanted to assign a role once, at the domain level, and have it work for the user in all the projects under the domain. Turns out we can do this. The trick here is realizing that a domain is a project. But the domain id is different from the project Id. An example. Here are the domains I have in my deployment:

$ openstack domain list +----------------------------------+------------+---------+--------------------+ | ID | Name | Enabled | Description | +----------------------------------+------------+---------+--------------------+ | 1ef534c4cb9349188870cac6ccd6bbef | test | True | | | d09e75c4cacb4f37b363dcdce0ae0046 | heat_stack | True | | | default | Default | True | The default domain | +----------------------------------+------------+---------+--------------------+

Looking closer at Default:

openstack domain show default +-------------+--------------------+ | Field | Value | +-------------+--------------------+ | description | The default domain | | enabled | True | | id | default | | name | Default | +-------------+--------------------+

The fact that it is a project is hidden from the end user. To see it, we need to peek into the database:

select * from project where name = 'Default'; +---------+---------+-------+--------------------+---------+--------------------------+-----------+-----------+ | id | name | extra | description | enabled | domain_id | parent_id | is_domain | +---------+---------+-------+--------------------+---------+--------------------------+-----------+-----------+ | default | Default | {} | The default domain | 1 | < > | NULL | 1 | +---------+---------+-------+--------------------+---------+--------------------------+-----------+-----------+

So, can we assign a role on this as a project? Let’s see.

openstack role add --user phred --project default Member

MariaDB [keystone]> select * from local_user where name='phred'; +----+----------------------------------+-----------+-------+-------------------+----------------+ | id | user_id | domain_id | name | failed_auth_count | failed_auth_at | +----+----------------------------------+-----------+-------+-------------------+----------------+ | 41 | 0dcaca9d38754312b3f1b87b020cffe6 | default | phred | NULL | NULL | +----+----------------------------------+-----------+-------+-------------------+----------------+ 1 row in set (0.00 sec) MariaDB [keystone]> select * from assignment where actor_id = '0dcaca9d38754312b3f1b87b020cffe6'; +-------------+----------------------------------+-----------+----------------------------------+-----------+ | type | actor_id | target_id | role_id | inherited | +-------------+----------------------------------+-----------+----------------------------------+-----------+ | UserProject | 0dcaca9d38754312b3f1b87b020cffe6 | default | 0d57208fd76945a4bb089667f6640f02 | 0 | +-------------+----------------------------------+-----------+----------------------------------+-----------+ 1 row in set (0.00 sec)

How about to make it inherited to all projects under default?

openstack role add --user phred --project default Member --inherited

MariaDB [keystone]> select * from assignment where actor_id = '0dcaca9d38754312b3f1b87b020cffe6'; +-------------+----------------------------------+-----------+----------------------------------+-----------+ | type | actor_id | target_id | role_id | inherited | +-------------+----------------------------------+-----------+----------------------------------+-----------+ | UserProject | 0dcaca9d38754312b3f1b87b020cffe6 | default | 0d57208fd76945a4bb089667f6640f02 | 0 | | UserProject | 0dcaca9d38754312b3f1b87b020cffe6 | default | 0d57208fd76945a4bb089667f6640f02 | 1 | +-------------+----------------------------------+-----------+----------------------------------+-----------+

$ openstack project list --domain default +----------------------------------+---------+ | ID | Name | +----------------------------------+---------+ | d72a9d2fb6f94bf5ab7b24ee14fcf5da | admin | | bc160905eeba4ba5b07d3c0894af2c3a | devel | | 3a771606472f4aeda2996af40e41615f | live | | bc2b077e3b814f2988b706c383d90b50 | service | | caf1d27635874ba79a6681b2a98b9843 | stage | +----------------------------------+---------+ $ openstack project show --domain default stage +-------------+----------------------------------+ | Field | Value | +-------------+----------------------------------+ | description | | | domain_id | default | | enabled | True | | id | caf1d27635874ba79a6681b2a98b9843 | | is_domain | False | | name | stage | | parent_id | default | +-------------+----------------------------------+

Let’s get a token for that project.

$ cat phred.stackrc.v3 for key in $( set | sed 's!=.*!!g' | grep -E '^OS_') ; do unset ; done export OS_AUTH_URL=https://192.0.2.2:13000/v3 export OS_USERNAME=phred export OS_PASSWORD=FreeIPA4All export OS_USER_DOMAIN_NAME=Default export OS_PROJECT_DOMAIN_NAME=Default export OS_PROJECT_NAME=stage export OS_IDENTITY_API_VERSION=3 [stack@undercloud ~]$ openstack token issue +------------+----------------------------------+ | Field | Value | +------------+----------------------------------+ | expires | 2016-11-03T23:13:26+0000 | | id | 9ed88f4989f440218791862704be3911 | | project_id | caf1d27635874ba79a6681b2a98b9843 | | user_id | 0dcaca9d38754312b3f1b87b020cffe6 | +------------+----------------------------------+

Change the project name to devel:

[stack@undercloud ~]$ export OS_PROJECT_NAME=devel [stack@undercloud ~]$ openstack token issue +------------+----------------------------------+ | Field | Value | +------------+----------------------------------+ | expires | 2016-11-03T23:14:17+0000 | | id | f53c7527a1d5496aa5cde3d9f2527169 | | project_id | bc160905eeba4ba5b07d3c0894af2c3a | | user_id | 0dcaca9d38754312b3f1b87b020cffe6 | +------------+----------------------------------+

To revoke:

openstack role remove –user phred –project default Member

and now:

[stack@undercloud ~]$ . phred.stackrc.v3 [stack@undercloud ~]$ openstack token issue +------------+----------------------------------+ | Field | Value | +------------+----------------------------------+ | expires | 2016-11-03T23:15:59+0000 | | id | 44c5373f401c4eb18c4b2142f676a9ab | | project_id | caf1d27635874ba79a6681b2a98b9843 | | user_id | 0dcaca9d38754312b3f1b87b020cffe6 | +------------+----------------------------------+

What happened? I only removed the non-inherited role:

MariaDB [keystone]> select * from assignment where actor_id = '0dcaca9d38754312b3f1b87b020cffe6'; +-------------+----------------------------------+-----------+----------------------------------+-----------+ | type | actor_id | target_id | role_id | inherited | +-------------+----------------------------------+-----------+----------------------------------+-----------+ | UserProject | 0dcaca9d38754312b3f1b87b020cffe6 | default | 0d57208fd76945a4bb089667f6640f02 | 1 | +-------------+----------------------------------+-----------+----------------------------------+-----------+

To remove the inherited role:

[stack@undercloud ~]$ . ./stackrc.v3 [stack@undercloud ~]$ openstack role remove --user phred --project default Member --inherited [stack@undercloud ~]$ . ./phred.stackrc.v3 [stack@undercloud ~]$ openstack token issue The request you have made requires authentication. (HTTP 401) (Request-ID: req-d665a1ae-beb4-429b-a844-c8fe50f30159)