From 165cce6d6e11504c85a71449fdca9608c7fd1d6c Mon Sep 17 00:00:00 2001 From: Lance Bragstad Date: Thu, 25 Feb 2021 22:22:20 +0000 Subject: [PATCH] Update the task policies At one point, these policies were used to protect actual task API endpoints. Since then, they have also been used internally within glance when spawning a task on behalf of the user for long-running operations (like import). These policies should not apply to the internal usage, as doing so prevents the operator from setting them to restrictive values in order to provide granular access to some roles. In the future we will fix that by moving those checks out of "the onion" and into the task API operations themselves, thus decoupling the internal and external uses. This adds documentation and scope definitions for these policies, as well as deprecates the "modify_task" policy which is never used and will be removed in the future. Control over the actual tasks API remains coarse with the "tasks_api_access" policy until a future release completes the above decoupling. Implements: blueprint secure-rbac Change-Id: I70a58acd78053b54187dba8e35273366f14c47a4 --- glance/api/policy.py | 3 ++ glance/policies/tasks.py | 87 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 85 insertions(+), 5 deletions(-) diff --git a/glance/api/policy.py b/glance/api/policy.py index 6c494fd90e..40af5aa677 100644 --- a/glance/api/policy.py +++ b/glance/api/policy.py @@ -449,14 +449,17 @@ class TaskRepoProxy(glance.domain.proxy.TaskRepo): task_proxy_class=TaskProxy, task_proxy_kwargs=proxy_kwargs) + # TODO(lbragstad): Move this to the tasks api itself def get(self, task_id): self.policy.enforce(self.context, 'get_task', {}) return super(TaskRepoProxy, self).get(task_id) + # TODO(lbragstad): Move this to the tasks api itself def add(self, task): self.policy.enforce(self.context, 'add_task', {}) super(TaskRepoProxy, self).add(task) + # TODO(lbragstad): Remove this after Xena def save(self, task): self.policy.enforce(self.context, 'modify_task', {}) super(TaskRepoProxy, self).save(task) diff --git a/glance/policies/tasks.py b/glance/policies/tasks.py index 3dd674ed58..f221af2cea 100644 --- a/glance/policies/tasks.py +++ b/glance/policies/tasks.py @@ -10,15 +10,92 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_log import versionutils from oslo_policy import policy +TASK_DESCRIPTION = """ +This granular policy controls access to tasks, both from the tasks API as well +as internal locations in Glance that use tasks (like import). Practically this +cannot be more restrictive than the policy that controls import or things will +break, and changing it from the default is almost certainly not what you want. +Access to the external tasks API should be restricted as desired by the +tasks_api_access policy. This may change in the future. +""" + +MODIFY_TASK_DEPRECATION = """ +This policy check has never been honored by the API. It will be removed in a +future release. +""" + +TASK_ACCESS_DESCRIPTION = """ +This is a generic blanket policy for protecting all task APIs. It is not +granular and will not allow you to separate writable and readable task +operations into different roles. +""" + task_policies = [ - policy.RuleDefault(name="get_task", check_str="rule:default"), - policy.RuleDefault(name="get_tasks", check_str="rule:default"), - policy.RuleDefault(name="add_task", check_str="rule:default"), - policy.RuleDefault(name="modify_task", check_str="rule:default"), - policy.RuleDefault(name="tasks_api_access", check_str="role:admin"), + policy.DocumentedRuleDefault( + name="get_task", + # All policies except tasks_api_access are internal policies that are + # only called by glance as a result of some other operation. + check_str='rule:default', + scope_types=['system', 'project'], + description='Get an image task.\n' + TASK_DESCRIPTION, + operations=[ + {'path': '/v2/tasks/{task_id}', + 'method': 'GET'} + ], + ), + policy.DocumentedRuleDefault( + name="get_tasks", + check_str='rule:default', + scope_types=['system', 'project'], + description='List tasks for all images.\n' + TASK_DESCRIPTION, + operations=[ + {'path': '/v2/tasks', + 'method': 'GET'} + ], + ), + policy.DocumentedRuleDefault( + name="add_task", + check_str='rule:default', + scope_types=['system', 'project'], + description='List tasks for all images.\n' + TASK_DESCRIPTION, + operations=[ + {'path': '/v2/tasks', + 'method': 'POST'} + ], + ), + policy.DocumentedRuleDefault( + name="modify_task", + check_str='rule:default', + scope_types=['system', 'project'], + description="This policy is not used.", + operations=[ + {'path': '/v2/tasks/{task_id}', + 'method': 'DELETE'} + ], + deprecated_for_removal=True, + deprecated_reason=MODIFY_TASK_DEPRECATION, + deprecated_since=versionutils.deprecated.WALLABY, + ), + policy.DocumentedRuleDefault( + name="tasks_api_access", + check_str="role:admin", + scope_types=['system', 'project'], + description=TASK_ACCESS_DESCRIPTION, + operations=[ + {'path': '/v2/tasks/{task_id}', + 'method': 'GET'}, + {'path': '/v2/tasks', + 'method': 'GET'}, + {'path': '/v2/tasks', + 'method': 'POST'}, + {'path': '/v2/tasks/{task_id}', + 'method': 'DELETE'} + ], + ) ]