Add default service role support to boostrap command
Added service role support to bootstrap command. Closes-Bug: #1951632 Change-Id: I9cb25a111c84ecb3a09158cbe44b0954df89096c
This commit is contained in:
parent
f5db9801c2
commit
d0eacc4729
@ -25,21 +25,28 @@ Default roles and behaviors across scopes allow operators to delegate more
|
||||
functionality to their team, auditors, customers, and users without maintaining
|
||||
custom policies.
|
||||
|
||||
In addition to ``admin``, ``member``, and ``reader`` role, from 2023.2 (Bobcat)
|
||||
release keystone will provide ``service`` role by default as well. Operators
|
||||
can use this role for service to service API calls instead of using ``admin``
|
||||
role for the same. The service role will be separate from ``admin``,
|
||||
``member``, ``reader`` and will not implicate any of these roles.
|
||||
|
||||
.. _`token guide`: https://docs.openstack.org/keystone/latest/admin/tokens-overview.html#authorization-scopes
|
||||
|
||||
-----------------
|
||||
Roles Definitions
|
||||
-----------------
|
||||
|
||||
The default roles provided by keystone, via ``keystone-manage bootstrap``, are
|
||||
related through role implications. The ``admin`` role implies the ``member``
|
||||
role, and the ``member`` role implies the ``reader`` role. These implications
|
||||
mean users with the ``admin`` role automatically have the ``member`` and
|
||||
``reader`` roles. Additionally, users with the ``member`` role automatically
|
||||
have the ``reader`` role. Implying roles reduces role assignments and forms a
|
||||
natural hierarchy between the default roles. It also reduces the complexity of
|
||||
default policies by making check strings short. For example, a policy that
|
||||
requires ``reader`` can be expressed as:
|
||||
The default roles provided by keystone via ``keystone-manage bootstrap``
|
||||
(except for the ``service`` role) are related through role implications. The
|
||||
``admin`` role implies the ``member`` role, and the ``member`` role implies
|
||||
the ``reader`` role. These implications mean users with the ``admin`` role
|
||||
automatically have the ``member`` and ``reader`` roles. Additionally,
|
||||
users with the ``member`` role automatically have the ``reader`` role.
|
||||
Implying roles reduces role assignments and forms a natural hierarchy between
|
||||
the default roles. It also reduces the complexity of default policies by
|
||||
making check strings short. For example, a policy that requires ``reader``
|
||||
can be expressed as:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
@ -127,6 +134,36 @@ shouldn't be able to manage things outside the project because it would violate
|
||||
the tenancy of their role assignment (this doesn't apply consistently since
|
||||
services are addressing this individually at their own pace).
|
||||
|
||||
Service
|
||||
=======
|
||||
|
||||
We reserve the ``service`` role for Service-to-service communication. The aim
|
||||
of a ``service`` role is to allow a service to communicate with another service
|
||||
and possibly be granted elevated privileges by the service receiving the
|
||||
request. Before the introduction of the ``service`` role, a service had to be
|
||||
granted the ``admin`` role in order to have elevated privileges, which gave a
|
||||
service powers way beyond what was necessary. With the ``service`` role in
|
||||
place, we can now allow all service-to-service APIs to default to the
|
||||
``service`` role only. For example, a policy that requires
|
||||
``service`` can be expressed as:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
"identity:create_foo": "role:service"
|
||||
|
||||
There might be exception service-to-service APIs which project think are
|
||||
useful to be used by admin or non-admin user then they can take the
|
||||
exceptional decision to default them to user role and ``service`` role. For
|
||||
example, a policy that requires ``service`` and ``admin`` can be expressed as:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
"identity:create_foo": "role:service" or "role:admin"
|
||||
|
||||
.. note::
|
||||
Unlike the other default roles, the ``service`` role is *not* a member
|
||||
of a role hierarchy. It is a standalone role.
|
||||
|
||||
.. note::
|
||||
|
||||
As of the Train release, keystone applies the following personas
|
||||
|
@ -337,6 +337,58 @@ simplified check string expression:
|
||||
"service:foobar:update": "role:member"
|
||||
"service:foobar:delete": "role:admin"
|
||||
|
||||
In addition to above roles, from 2023.2 (Bobcat) release
|
||||
``keystone-manage bootstrap`` will provide `service` role as well. If a
|
||||
``service`` role is already present in the deployment, then a new one
|
||||
is not created. This way any local scripts relying on the role ID will not
|
||||
be broken.
|
||||
|
||||
.. note::
|
||||
If you already have a ``service`` role in your deployment, you should
|
||||
review its usage to make sure it is used only for service-to-service
|
||||
communication.
|
||||
|
||||
Once ``service`` role is created, OpenStack service
|
||||
developers can start integrating it into their default policies as expressed:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
policy.DocumentedRuleDefault(
|
||||
name='os_compute_api:os-server-external-events:create',
|
||||
check_str='role:service',
|
||||
scope_types=['project']
|
||||
)
|
||||
|
||||
It is important to note that we need to keep all the service-to-service APIs
|
||||
default to ``service`` role only. For example, a policy that requires
|
||||
``service`` can be expressed as:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
"service:foobar:create": "role:service"
|
||||
|
||||
There might be exception service-to-service APIs which project think are
|
||||
useful to be used by admin or non-admin user then they can take the
|
||||
exceptional decision to default them to user role and ``service`` role. For
|
||||
example, a policy that requires ``service`` and ``admin`` can be expressed as:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
"service:foobar:create": "role:service" OR "role:admin"
|
||||
|
||||
Additionally, any deployment tools that create service accounts for OpenStack
|
||||
services, should start preparing for these policy changes by updating their
|
||||
role assignments and performing the deployment language equivalent of the
|
||||
following:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack role add --user nova --project service service
|
||||
$ openstack role add --user cinder --project service service
|
||||
$ openstack role add --user neutron --project service service
|
||||
$ openstack role add --user glance --project service service
|
||||
$ openstack role add --user manila --project service service
|
||||
|
||||
How do I incorporate authorization scopes into a service?
|
||||
=========================================================
|
||||
|
||||
|
@ -45,6 +45,9 @@ class Bootstrapper(object):
|
||||
self.admin_role_id = None
|
||||
self.admin_role_name = None
|
||||
|
||||
self.service_role_id = None
|
||||
self.service_role_name = 'service'
|
||||
|
||||
self.region_id = None
|
||||
|
||||
self.service_name = None
|
||||
@ -66,6 +69,7 @@ class Bootstrapper(object):
|
||||
self._bootstrap_reader_role()
|
||||
self._bootstrap_member_role()
|
||||
self._bootstrap_admin_role()
|
||||
self._bootstrap_service_role()
|
||||
self._bootstrap_project_role_assignment()
|
||||
self._bootstrap_system_role_assignment()
|
||||
self._bootstrap_region()
|
||||
@ -160,6 +164,10 @@ class Bootstrapper(object):
|
||||
implied_role_id
|
||||
)
|
||||
|
||||
def _bootstrap_service_role(self):
|
||||
role = self._ensure_role_exists(self.service_role_name)
|
||||
self.service_role_id = role['id']
|
||||
|
||||
def _bootstrap_reader_role(self):
|
||||
role = self._ensure_role_exists(self.reader_role_name)
|
||||
self.reader_role_id = role['id']
|
||||
|
@ -183,6 +183,7 @@ class BootStrap(BaseApp):
|
||||
self.bootstrapper.immutable_roles = True
|
||||
|
||||
self.bootstrapper.bootstrap()
|
||||
self.service_role_id = self.bootstrapper.service_role_id
|
||||
self.reader_role_id = self.bootstrapper.reader_role_id
|
||||
self.member_role_id = self.bootstrapper.member_role_id
|
||||
self.role_id = self.bootstrapper.admin_role_id
|
||||
|
@ -34,9 +34,9 @@ class _SystemUserRoleTests(object):
|
||||
with self.test_client() as c:
|
||||
r = c.get('/v3/roles', headers=self.headers)
|
||||
# With bootstrap setup and the role we just created, there should
|
||||
# be four roles present in the deployment. Bootstrap creates
|
||||
# ``admin``, ``member``, and ``reader``.
|
||||
self.assertEqual(4, len(r.json['roles']))
|
||||
# be five roles present in the deployment. Bootstrap creates
|
||||
# ``service``, ``admin``, ``member``, and ``reader``.
|
||||
self.assertEqual(5, len(r.json['roles']))
|
||||
|
||||
def test_user_can_get_a_role(self):
|
||||
role = PROVIDERS.role_api.create_role(
|
||||
|
@ -134,14 +134,24 @@ class CliBootStrapTestCase(unit.SQLDriverOverrides, unit.TestCase):
|
||||
admin_role = PROVIDERS.role_api.get_role(bootstrap.role_id)
|
||||
reader_role = PROVIDERS.role_api.get_role(bootstrap.reader_role_id)
|
||||
member_role = PROVIDERS.role_api.get_role(bootstrap.member_role_id)
|
||||
service_role = PROVIDERS.role_api.get_role(bootstrap.service_role_id)
|
||||
role_list = (
|
||||
PROVIDERS.assignment_api.get_roles_for_user_and_project(
|
||||
user['id'],
|
||||
project['id']))
|
||||
self.assertIs(3, len(role_list))
|
||||
|
||||
role_list_len = 4
|
||||
if bootstrap.bootstrapper.project_name:
|
||||
role_list_len = 3
|
||||
|
||||
self.assertIs(role_list_len, len(role_list))
|
||||
self.assertIn(admin_role['id'], role_list)
|
||||
self.assertIn(reader_role['id'], role_list)
|
||||
self.assertIn(member_role['id'], role_list)
|
||||
|
||||
if not bootstrap.bootstrapper.project_name:
|
||||
self.assertIn(service_role['id'], role_list)
|
||||
|
||||
system_roles = (
|
||||
PROVIDERS.assignment_api.list_system_grants_for_user(
|
||||
user['id']
|
||||
@ -352,7 +362,7 @@ class CliBootStrapTestCase(unit.SQLDriverOverrides, unit.TestCase):
|
||||
domain = PROVIDERS.resource_api.create_domain(domain['id'], domain)
|
||||
domain_roles = {}
|
||||
|
||||
for name in ['admin', 'member', 'reader']:
|
||||
for name in ['admin', 'member', 'reader', 'service']:
|
||||
domain_role = {
|
||||
'domain_id': domain['id'],
|
||||
'id': uuid.uuid4().hex,
|
||||
|
12
releasenotes/notes/bug-1951632-11272e49e2fa439d.yaml
Normal file
12
releasenotes/notes/bug-1951632-11272e49e2fa439d.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
[`bug 1951632 <https://bugs.launchpad.net/keystone/+bug/1951632>`_]
|
||||
``Support has been added for deploying `service` role during the bootstrap
|
||||
process in addition to the `admin`, `member` and `reader` role.``
|
||||
upgrades:
|
||||
- |
|
||||
``If the bootstrap process is re-run, and a `service` role already exists,
|
||||
it does not recreate the `service` role. See
|
||||
[`bug 1951632 <https://bugs.launchpad.net/keystone/+bug/1951632>`_]
|
||||
for more details.``
|
Loading…
x
Reference in New Issue
Block a user