249 lines
7.0 KiB

This work is licensed under a Creative Commons Attribution 3.0 Unported
Pass Targets to Glance's Policy Enforcer
Currently it's possible to define custom rules in Glance's ``policy.json``
that rely on attributes other than a user's roles. Unfortunately, if you
attempt to apply one of those rules, it will always cause the user to be
prevented from performing the associated action. This specification proposes
that we pass the proper target objects to the enforcer so these rules can be
used and properly enforced.
Problem description
Currently, Glance promises that permissions can be configured in the
``policy.json`` file but any rule other than a role check currently
results in a ``403 Forbidden`` response. As this is a promised feature,
implementing this specification is merely fixing the already promised
It is not possible to restrict access to actions in Glance's ``policy.json``
based on anything other than a user's roles. The policy enforcer that Glance
extends from oslo expects a dictionary-like object to be passed as a target of
the action. Every method that uses the policy enforcer and enforces the policy
defined for the corresponding action currently passes an empty dictionary
(``{}``) which provides absolutely no data about the actual target.
If we define a rule similar to the following::
"tenant_is_owner": "tenant%(owner)s"
And we apply it to an action, e.g.,
"delete_image": "rule:tenant_is_owner"
Then every request to delete any image will be denied with a 403
(Unauthorized) response from Glance's API. The reason stems from how the rule
is parsed and the target is passed in. The ``"rule:tenant_is_owner"`` rule
will be parsed as a ``GenericCheck``. These checks are split on the ``:`` into
a ``kind`` and a ``match`` (roughly, ``"<kind>:<match>"``). The match portion
is then interpolated with the target, i.e.,
.. code-block:: python
match = self.match % target
So using our example above, we would do
.. code-block:: python
match = "%(owner)s" % {}
Except that this raises a ``KeyError`` which means the check immediately
returns ``False`` and fails. In this particular instance (deleting an image),
if we passed an instance of ``glance.api.policy.ImageTarget``, then what would
instead happen is that the interpolation would succeed.
Proposed change
The solution for image-based resources is simple. We have the ``image`` on the
policy proxies that relate to images. We simply pass that to
``glance.api.policy.ImageTarget`` and pass the resulting instance to the
policy enforcer so it can be accessed like a dictionary when interpolated.
For members and tasks, there is no target class that we can use. These are
very thin classes that could easily be written.
Once we have the appropriately defined target classes, we would then update
the places where the policy is enforced to use instances of those target
With proper targets in place, we can also implement safer default policy rules
for operators who rely solely on the default ``policy.json`` file.
Custom rule creation based on attributes of the target object could be
disabled. This would severely limit an operator's ability to restrict actions
based on a user's tenant and other properties of the target of the action.
Data model impact
REST API impact
Security impact
This will give operators a significant amount of control over the security of
their Glance installations. Currently they can only restrict actions based on
roles which may be sufficient in some cases. If the operator, however, wishes
to restrict access based on other factors (besides role) they cannot do this.
If they try to do it, there is no indication that it will not work but they
can essentially produce a Denial of Service to users who should be able to
perform actions based on the policy defined.
Notifications impact
Other end user impact
Performance Impact
Other deployer impact
To leverage the fixes in this specification, operators need to update their
versions of ``policy.json`` used in their deployments. To write a rule,
operators need to know that there are three values provided by glance that can
be used in a rule on the left side of the colon (``:``). Those values are the
current user's credentials in the form of:
- role
- tenant
- owner
The left side of the colon can also contain any value that Python can
understand, e.g.,:
- ``True``
- ``False``
- ``"a string"``
- &c.
Role checks are going to continue to work exactly as they already do. If the
role defined in the check is one that the user holds, then that will pass,
e.g., ``role:admin``.
Using ``tenant`` and ``owner`` will only work with Images or actions that
interact with an image. Consider the following rule::
This will use the ``tenant`` value of the currently authenticated user. It
will also use ``owner`` from the image it is acting upon. If those two
values are equivalent the check will pass. All attributes on an image (as well
as extra image properties) are available for use on the right side of the
colon. The most useful are the following:
- ``owner``
- ``protected``
- ``is_public``
An operator, therefore, could construct a set of rules like the following::
"not_protected": "False:%(protected)s",
"is_owner": "tenant:%(owner)s",
"not_protected_and_is_owner": "rule:not_protected and rule:is_owner",
"delete_image": "rule:not_protected_and_is_owner"
Developer impact
Primary assignee:
Core reviewer(s):
Other reviewer(s):
Work Items
- Create appropriate target classes
- Use target classes to proxy targets to the policy enforcer
- Add tests demonstrating that generic checks now work
- Add better documentation surrounding policy rules to the existing
Functional tests will be added where specific ``policy.json`` files are loaded
to test access control of different targets.
Documentation Impact
There is no direct impact, but the existing ``policy.json`` documentation is
thin and only describes what each rule controls. It does not describe the
available target information or how to write rules.
Related bugs:
Initial work at an implementation:
Glance meeting discussion on 2015 January 15: