diff --git a/specs/keystone/queens/system-scope.rst b/specs/keystone/queens/system-scope.rst new file mode 100644 index 00000000..8193630c --- /dev/null +++ b/specs/keystone/queens/system-scope.rst @@ -0,0 +1,571 @@ +.. + This work is licensed under a Creative Commons Attribution 3.0 Unported + License. + + http://creativecommons.org/licenses/by/3.0/legalcode + +======================= +System Role Assignments +======================= + +`bp system-scope `_ + +This document describes the necessary changes in order to implement system role +assignments. + +Problem Description +=================== + +Today, role assignments are built by giving an actor a role on a target. An +actor can be a user or a group. A target is limited to a project or a domain. +This works great for controlling access to things that map into a project or +domain (e.g. instance ownership fits naturally within projects). This starts to +get confusing with operations that don't fit within that constraint. Performing +operations on hypervisors (e.g. `GET /os-hypervisors/`) is a good example of an +API that doesn't map well to a project. Instead, it's clearer to think about +these types of operations at a system-wide perspective, instead of a +project-specific one. + +A system role assignment would be an assignment with the target being `system` +instead of a project or a domain. + +Global Scope vs. System Scope +----------------------------- + +Initial discussions around this proposal used the term *global scope* as a way +to distinguish something other than project scope. For example, operations on +instances are easy to associate to a project because instances are owned by +projects. Endpoints or services were originally considered *global* in nature +since they applied to the whole deployment. After several discussions, it +became apparent that *global* still wasn't the term we were looking for. +Another example highlights the deficiency in the term *global*. Theoretically, +if a user has a role assignment on the root domain, or project, in an +deployment, shouldn't they be able to view all instances in the deployment +(e.g. the entire project tree under the root domain)? Is that not in some sense +also *global* because the user would be viewing all instances across the entire +deployment? After understanding that, it became apparent that *global* refers +to the root of a tree, when we really needed it to refer to *system* +operations. + +Using the term *system* helps clarify which resources and APIs are specific to +the function of the deployment, or system as a whole. For example, service and +endpoints are entities required for the *system* to function properly. They +clearly don't pertain to a single project or domain. Hypervisor management in +nova is also a *system* level resource that doesn't make sense to associate to +a single project. Multiple projects can have instances hosted on a single +hypervisor due to multi-tenancy. + +After realizing this, it became apparent that *system* is a more appropriate +term than *global* for isolating infrastructure and project operations. + +Proposed Change +=============== + +List system role assignments for a user +--------------------------------------- + +**Request:** `GET /v3/system/users/{user_id}/roles/` + +**Parameters** + +* `user_id` - The user ID. + +**Response** + +* 200 - OK +* 404 - Not Found if a role or user doesn't exist +* 401 - If the operation isn't permitted to the user + +**Response Body** + +.. code:: json + + { + "links": { + "self": "http://example.com/identity/v3/system/users/cf8e3ee7115b4a88897673ee61dd2919/roles", + "previous": null, + "next": null + }, + "roles": [ + { + "id": "46b213b41e7344cc8078ac5e7d161f17", + "links": { + "self": "http://example.com/identity/v3/roles/46b213b41e7344cc8078ac5e7d161f17" + }, + "name": "admin" + } + ] + } + +Assign a system role to a user +------------------------------ + +**Request:** `PUT /v3/system/users/{user_id}/roles/{role_id}` + +**Parameters** + +* `user_id` - The user ID. +* `role_id` - The role ID. + +**Response** + +* 204 - No Content +* 404 - Not Found if a role or user doesn't exist +* 401 - If the operation isn't permitted to the user + +Check if a user has a system role assignment +-------------------------------------------- + +**Request:** `HEAD /v3/system/users/{user_id}/roles/{role_id}` + +**Request:** `GET /v3/system/users/{user_id}/roles/{role_id}` + +**Parameters** + +* `user_id` - The user ID. +* `role_id` - The role ID. + +**Response** + +* 204 - No Content +* 404 - Not Found if a role or user doesn't exist +* 401 - If the operation isn't permitted to the user + +Unassign a system role from a user +---------------------------------- + +**Request:** `DELETE /v3/system/users/{user_id}/roles/{role_id}` + +**Parameters** + +* `user_id` - The user ID. +* `role_id` - The role ID. + +**Response** + +* 204 - No Content +* 404 - Not Found if a role or user doesn't exist +* 401 - If the operation isn't permitted to the user + +List system role assignments for a group +---------------------------------------- + +**Request:** `GET /v3/system/groups/{group_id}/roles/` + +**Parameters** + +* `group_id` - The group ID. + +**Response** + +* 200 - OK +* 404 - Not Found if a role or user doesn't exist +* 401 - If the operation isn't permitted to the user + +**Response Body** + +.. code:: json + + { + "links": { + "self": "http://example.com/identity/v3/system/groups/282051ffddcf4206a954ad838c86d39f/roles", + "previous": null, + "next": null + }, + "roles": [ + { + "id": "46b213b41e7344cc8078ac5e7d161f17", + "links": { + "self": "http://example.com/identity/v3/roles/46b213b41e7344cc8078ac5e7d161f17" + }, + "name": "admin" + } + ] + } + +Assign a system role to a group +------------------------------- + +**Request:** `PUT /v3/system/groups/{group_id}/roles/{role_id}` + +**Parameters** + +* `group_id` - The group ID. +* `role_id` - The role ID. + +**Response** + +* 204 - No Content +* 404 - Not Found if a role or group doesn't exist +* 401 - If the operation isn't permitted to the user + +Check if a group has a system role assignment +--------------------------------------------- + +**Request:** `HEAD /v3/system/groups/{group_id}/roles/{role_id}` + +**Request:** `GET /v3/system/groups/{group_id}/roles/{role_id}` + +**Parameters** + +* `group_id` - The group ID. +* `role_id` - The role ID. + +**Response** + +* 204 - No Content +* 404 - Not Found if a role or group doesn't exist +* 401 - If the operation isn't permitted to the user + +Unassign a system role from a group +----------------------------------- + +**Request:** `DELETE /v3/system/groups/{group_id}/roles/{role_id}` + +**Parameters** + +* `group_id` - The group ID. +* `role_id` - The role ID. + +**Response** + +* 204 - No Content +* 404 - Not Found if a role or user doesn't exist +* 401 - If the operation isn't permitted to the user + +List role assignments +--------------------- + +The existing API to list role assignment will have to be enhanced to return +system role assignment, in addition to the project and domain role assignment +information it returns today. + +**Request:** `GET /v3/role_assignments` + +**Paramters** + +A filter will be added, called `scope.system`, to filter role assignments by +system-specific role assignment. It will be a boolean value. + +**Response** + +* 200 - OK +* 400 - Bad Request +* 404 - Not Found if a role or user doesn't exist +* 401 - If the operation isn't permitted to the user + +**Response Body** + +.. code:: json + + { + "role_assignments": [ + { + "role": { + "id": "d6c89e9121304b6f87de57b0500b0526" + }, + "user": { + "id": "3f0c5f11e792494ab5de347696fa1421" + }, + "scope": { + "domain": { + "id": "6bfbd79b010e4405b92731479cbbe8e7" + } + }, + "links": { + "assignment": "http://example.com/identity/v3/domains/6bfbd79b010e4405b92731479cbbe8e7/users/3f0c5f11e792494ab5de347696fa1421/roles/d6c89e9121304b6f87de57b0500b0526" + } + }, + { + "role": { + "id": "2fb8d689a8744a42af926ea4f8f929c7" + }, + "group": { + "id": "a806d9029db7403e9869632aee082e5c" + }, + "scope": { + "project": { + "id": "2fae742cb86543af825471ea6b63ccea" + } + }, + "links": { + "assignment": "http://example.com/identity/v3/projects/2fae742cb86543af825471ea6b63ccea/groups/a806d9029db7403e9869632aee082e5c/roles/2fb8d689a8744a42af926ea4f8f929c7" + } + }, + { + "group": { + "id": "1d8d919f37d94f308d007e72737cf10a" + }, + "links": { + "assignment": "http://example.com/identity/v3/system/groups/1d8d919f37d94f308d007e72737cf10a/roles/b29d6fff51c43478b00bb16bfb771fc" + }, + "role": { + "id": "ab29d6fff51c43478b00bb16bfb771fc" + }, + "scope": { + "system": "true" + } + } + ], + "links": { + "self": "http://example.com/identity/v3/role_assignments", + "previous": null, + "next": null + } + } + +Authenticating for a system-scoped token +------------------------------------------ + +The following is an example request for a system-scoped token:: + + + { + "auth": { + "identity": { + "methods": [ + "password" + ], + "password": { + "user": { + "id": "8bbca32b850a4c22b64a1b7bc2c6bd13", + "password": "my-password" + } + } + }, + "scope": { + "system": { + "all": true + } + } + } + } + +An example response would be:: + + { + "token": { + "audit_ids": [ + "doIh18J8RyW3jXF50FV26g" + ], + "catalog": [ + ... + ], + "expires_at": "2017-05-15T21:58:29.000000Z", + "issued_at": "2017-05-15T20:58:29.000000Z", + "methods": [ + "password" + ], + "system": { + "all": true + }, + "roles": [ + { + "id": "c2145c84a802413fbac71479250c9378", + "name": "observer" + }, + { + "id": "fc2ec22e227941f8afd94a1587ac57d3", + "name": "admin" + } + ], + "user": { + "domain": { + "id": "default", + "name": "Default" + }, + "id": "8bbca32b850a4c22b64a1b7bc2c6bd13", + "name": "bob", + "password_expires_at": null + } + } + } + +System scope can be consumed by existing policies:: + + "system_admin": "role:admin and system:True" + "system_reader": "role:reader and system:True" + "admin_required": "rule:system_admin" + +The attributes of a system token response can also be consumed by +`oslo.context` and exposed to services for scope checks using `context.scope = +'system'` or some other method. The process of relaying this information to the +consuming service will contain follow on work to the `oslo.context` library to +ensure it handles system-scoped tokens properly. The primary purpose of this +specification is to allow for the scoping of roles at a system level and +exposing that ability to end users. Work can be done in parallel to consume +this information in policy files or shared libraries. + +System Roles, Implied Roles, & Inherited Roles +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Keystone supports other types of role behaviors. An administrator can have one +role imply another, or have roles be inherited according to the hierarchical +structure of projects. For example, if role ``Alpha`` implies role ``Beta``, a +user with role ``Alpha`` will automatically be given role ``Beta`` on the +target, since it's implied. Another example is if a role assignment is allowed +to be inherited through a tree of projects. For example, if ``project C`` is +the parent of ``project D`` and a user has role ``Echo`` on ``project C``, the +user also has role ``Echo`` on ``project D`` via role inheritance. These +concepts are known respectively, as implied roles and inherited roles. + +Part of introducing a system scoping mechanism is understanding how it applies +to these concepts. It is possible to apply both of these concepts to system +roles. A role assigned to a user on the system should be able to imply other +roles. There have been discussions about making the system a hierarchy +structure in the future. For example, what if a system was actually a tree of +regions. That would introduce another level of scope that allows users to have +role assignments on subsets of the entire system. This seems like a powerful +idea, but it does need more thought and discussion. For the time being, system +will be a single entity, but built to be refactored into a hierarchy later. + +In conclusion, the initial implementation of system roles should support +implied role assignment. It should be flexible enough to support inherited +roles if the system entity ever evolves into a tree of regions or services. + +Alternatives +------------ + +An alternative to this approach would be to leverage the `admin_project` in +order to achieve global scoping. The `admin_project` is a special project that +allows for elevated privileges if role assignments are given to that project. +Let's consider the following example. Let's say there is an `observer` role +that allows users to do perform read-only operations within a specific scope. +If Bob has the `observer` role on project `foo`, he should be able to view +things within that project. If Alice has the `observer` role on the +`admin_project`, she should be able to view things across the deployment, like +services and endpoints. + +In this model, system scope is determined by a specific project and the role +assignments that project has. Every user that requires a system role (i.e. +admin, observer, support, etc) in a deployment will be required to have a role +assignment on the `admin_project`. + +Benefits: + +* Reuse of existing project scope mechanisms/tokens +* Leveraging the `is_admin_project` attribute of tokens +* Most of this work is already done +* Not necessary to change how scope is stored + +Drawbacks: + +* Automated tooling might have to handle this project separately (i.e. coding + around an implementation detail of how policy is elevated) to ensure nothing + happens to the `admin_project` +* Operators may find it confusing to have a role on a super-special project in + order to have elevated privileges, which seems like an anti-pattern +* All users that require a system role of some kind must have a role assignment + on the `admin_project`, this could result in a large number of role + assignments on the `admin_project` +* Develop some sort of recovery plan in the event the `admin_project` is + accidentally deleted +* Certain resources can't belong in system scope today (i.e. instances must be + tied to a project), this approach doesn't stop users from creating resources + within the `admin_project`, which would be the equivalent to a system-wide + instance +* How does the `admin_project` conform to project hierarchy? Is it suppose to + be kept in it's own subtree under the default domain or can it have child + projects underneath it? + +Roadmap +------- + +The `is_admin_project` implementation exists in OpenStack today, is relayed +through keystone APIs, and present in some service policy files. It makes sense +to have compatibility for both moving forward. The `roadmap `_ +put together at the Queens PTG shows how we can improve admin-ness using both +approaches but end up in a place where system scope is required. + +Security Impact +--------------- + +This type of scoping will allow OpenStack services to separate system +operations from project or domain scoped operations. The result will be an +improved security model across OpenStack. Note that a system-scoped token is +still a bearer token and allows the holder the ability to do things on the +deployment system. + +Notifications Impact +-------------------- + +System scoping will be subject to the same notifications as project or domain +scope requests. + +Other End User Impact +--------------------- + +This is highly dependent on how operators have configured their policy across +OpenStack. Ideally, this will give operators more tools to provide better +security in their deployments. + +Performance Impact +------------------ + +None. + +Other Deployer Impact +--------------------- + +Deployers will now have the ability to control system operations by leveraging +system role assignments. The ability will be available by default but a +migration won't be supplied to migrate existing policy workarounds since policy +can vary wildly across deployments. + +An upgrade document can be provided to help operators visualize the process and +apply it to their specific policy scenario. + +Developer Impact +---------------- + +This work will most-likely require some changes to testing both inside and +outside of keystone, in order to guarantee isolation of system operations from +project operations. Mitigating this will be a required work item of the +implementation. + +Implementation +============== + +Assignee(s) +----------- + +Primary assignee: + Lance Bragstad lbragstad + +Other contributors: + None + +Work Items +---------- + +* Add a new database table to support system assignments +* Implement system role assignments +* Implement scoping a token to a system context +* Migrate tempest testing to leverage system roles +* Clearly document possible upgrade paths for operators +* Implement system context in `oslo.policy` and `keystonemiddleware` + +Follow on work items should be done to ensure system role assignments are +honored within policies across OpenStack: + +* Ensure default policies adhere to system scope +* Ensure scope checks across projects enforce system scope + + +Dependencies +============ + +None. + +Documentation Impact +==================== + +We will need to provide a more consistent authentication document that clearly +explains scope at the project and system level. A separate document that +describes possible upgrade paths from the existing system will also be a +requirement. + +References +========== + +None.