As most OpenStack projects, Neutron leverages oslo_policy [1]. However, since Neutron loves to be special and complicate every developer’s life, it also “augments” oslo_policy capabilities by:
This document discusses Neutron-specific aspects of policy enforcement, and in particular how the enforcement logic is wired into API processing. For any other information please refer to the developer documentation for oslo_policy [2].
The Neutron API controllers perform policy checks in two phases during the processing of an API request:
POST
, PUT
, and DELETE
, and immediately after
returning from the plugin layer for GET
requests;The aim of this step is to authorize processing for a request or reject it
with an error status code.
This step uses the neutron.policy.enforce
routine. This routine raises
oslo_policy.PolicyNotAuthorized
when policy enforcement fails. The Neutron
REST API controllers catch this exception and return:
POST
request or an PUT
request for an
object owned by the project submitting the request;add_router_interface
;DELETE
, GET
and all other PUT
requests.For DELETE
operations the resource must first be fetched. This is done
invoking the same _item
[3] method used for processing GET
requests.
This is also true for PUT
operations, since the Neutron API implements
PATCH
semantics for PUTs
.
The criteria to evaluate are built in the _build_match_rule
[4] routine.
This routine takes in input the following parameters:
<operation>_<resource>
form,
e.g.: create_network
POST
operations this could
be a partial specification of the object, whereas it is always a full
specification for GET
, PUT
, and DELETE
requests, as resource
data are retrieved before dispatching the call to the plugin layer.The _build_match_rule
routine returns a oslo_policy.RuleCheck
instance
built in the following way:
policy.json
;GET
operations; more detailed checks will be performed anyway
when building the response;<operation>_<resource>:<attribute>
rule, and link it with the
previous rule with an ‘And’ relationship (using oslo_policy.AndCheck
);
this step will be performed only if the enforce_policy
flag is set to
True
in the resource attribute descriptor (usually found in a data
structure called RESOURCE_ATTRIBUTE_MAP
);<operation>_<resource>:<attribute>:<sub_attribute>
.
An ‘And’ relationship will be used in this case too.As all the rules to verify are linked by ‘And’ relationships, all the policy
checks should succeed in order for a request to be authorized. Rule
verification is performed by oslo_policy
with no “customization” from the
Neutron side.
Some Neutron extensions, like the provider networks one, add some attribute to resources which are however not meant to be consumed by all clients. This might be because these attributes contain implementation details, or are meant only to be used when exchanging information between services, such as Nova and Neutron;
For this reason the policy engine is invoked again when building API
responses. This is achieved by the _exclude_attributes_by_policy
[5]
method in neutron.api.v2.base.Controller
;
This method, for each attribute in the response returned by the plugin layer,
first checks if the is_visible
flag is True. In that case it proceeds to
checking policies for the attribute; if the policy check fails the attribute
is added to a list of attributes that should be removed from the response
before returning it to the API client.
The neutron.policy
module exposes a simple API whose main goal if to allow the
REST API controllers to implement the authorization workflow discussed in this
document. It is a bad practice to call the policy engine from within the plugin
layer, as this would make request authorization dependent on configured
plugins, and therefore make API behaviour dependent on the plugin itself, which
defies Neutron tenet of being backend agnostic.
The neutron.policy API exposes the following routines:
init
Initializes the policy engine loading rules from the json policy (files).
This method can safely be called several times.reset
Clears all the rules currently configured in the policy engine. It is
called in unit tests and at the end of the initialization of core API
router [6] in order to ensure rules are loaded after all the extensions
are loaded.refresh
Combines init and reset. Called when a SIGHUP signal is sent to an API
worker.set_rules
Explicitly set policy engine’s rules. Used only in unit tests.check
Perform a check using the policy engine. Builds match rules as described
in this document, and then evaluates the resulting rule using oslo_policy’s
policy engine. Returns True if the checks succeeds, false otherwise.enforce
Operates like the check routine but raises if the check in oslo_policy
fails.check_is_admin
Enforce the predefined context_is_admin rule; used to determine the is_admin
property for a neutron context.check_is_advsvc
Enforce the predefined context_is_advsvc rule; used to determine the
is_advsvc property for a neutron context.Neutron provides two additional policy rule classes in order to support the
“augmented” authorization capabilities it provides. They both extend
oslo_policy.RuleCheck
and are registered using the
oslo_policy.register
decorator.
This class is registered for rules matching the tenant_id
keyword and
overrides the generic check performed by oslo_policy in this case.
It uses for those cases where neutron needs to check whether the project
submitting a request for a new resource owns the parent resource of the one
being created. Current usages of OwnerCheck
include, for instance,
creating and updating a subnet. This class supports the extension parent
resources owner check which the parent resource introduced by
service plugins. Such as router and floatingip owner check for router
service plugin. Developers can register the extension resource name and service
plugin name which were registered in neutron-lib into
EXT_PARENT_RESOURCE_MAPPING
which is located in
neutron.common.constants
.
The check, performed in the __call__
method, works as follows:
oslo_policy.GenericCheck
would do. This is also the most frequent case
as the target field is usually tenant_id
;networks:tenant_id
identifies the tenant_id
attribute of the
network
resource. For extension parent resource case,
ext_parent:tenant_id
identifies the tenant_id
attribute of the
registered extension resource in EXT_PARENT_RESOURCE_MAPPING
;PolicyCheckError
exception;_RESOURCE_FOREIGN_KEYS
data
structure in neutron.policy
. This foreign key is simply the
attribute acting as a primary key in the parent resource. A
PolicyCheckError
exception will be raised if such ‘parent foreign key’
cannot be retrieved;tenant_id
of the port data structure matches the
tenant_id
of the network where this port is being created.This class is registered with the policy engine for rules matching the ‘field’ keyword, and provides a way to perform fine grained checks on resource attributes. For instance, using this class of rules it is possible to specify a rule for granting every project read access to shared resources.
In policy.json, a FieldCheck rules is specified in the following way:
> field: <resource>:<field>=<value>
This will result in the initialization of a FieldCheck that will check for
<field>
in the target resource data, and return True
if it is equal
to <value>
or return False
is the <field>
either is not equal to
<value>
or does not exist at all.
When developing REST APIs for Neutron it is important to be aware of how the policy engine will authorize these requests. This is true both for APIs served by Neutron “core” and for the APIs served by the various Neutron “stadium” services.
enforce_policy
attribute should be set to True
. While
setting this flag to True
for each attribute is a viable strategy,
it is worth noting that this will require a call to the policy engine
for each attribute, thus consistently increasing the time required to
complete policy checks for a resource. This could result in a scalability
issue, especially in the case of list operations retrieving a large
number of resources;tenant_id
attribute. For these attributes the
required_by_policy
attribute should always set to True
. This will
ensure that the attribute is included in the resource data sent to the
policy engine for evaluation;tenant_id
attribute is a fundamental one in Neutron API request
authorization. The default policy, admin_or_owner
, uses it to validate
if a project owns the resource it is trying to operate on. To this aim,
if a resource without a tenant_id is created, it is important to ensure
that ad-hoc authZ policies are specified for this resource.PUT
and DELETE
requests a 404 error is returned on request
authorization failures rather than a 403, unless the project submitting the
request own the resource to update or delete. This is to avoid conditions
in which an API client might try and find out other projects’ resource
identifiers by sending out PUT
and DELETE
requests for random
resource identifiers.OR
relationship between two
attributes of a given resource (eg.: port.name == 'meh' or
port.status == 'DOWN'
), unless the rule with the or condition is explicitly
added to the policy.json file.OwnerCheck
performs a plugin access; this will likely require a database
access, but since the behaviour is implementation specific it might also
imply a round-trip to the backend. This class of checks, when involving
retrieving attributes for ‘parent’ resources should be used very sparingly.OwnerCheck
rules to work, parent resources should have an
entry in neutron.policy._RESOURCE_FOREIGN_KEYS
; moreover the
resource must be managed by the ‘core’ plugin (ie: the one defined in the
core_plugin configuration variable)The following is the guideline of policy definitions.
Ideally we should define all available policies, but in the neutron policy enforcement it is not practical to define all policies because we check all attributes of a target resource in the Response Filtering. Considering this, we have the special guidelines for “get” operation.
<action>_<resource>
must be defined
for all types of operations.
Valid actions are create
, update
, delete
and get
.get_<resourceS>
(get plural) is unnecessary.
The neutron API layer use a single form policy get_<resource>
when listing resources [7] [8].add_router_interface
of router
resource.<action>_<resource>:<attribute>(:<sub_attribute>)
policy is
required for attributes with enforce_policy
in the API definitions.
Note that it is recommended to define even if a rule is same as for
<action>_<resource>
from the documentation perspective.get_<resource>:<attribute>(:<sub_attribute>)
,
the following guideline is applied:get_<resource>
(without attributes).get_<resource>
, there is
no need to define it explicitly.
This is for simplicity. We check all attributes of a target resource
in the process of Response Filtering so it leads to a long long
policy definitions for “get” actions in our documentation.
It is not happy for operators either.enforce_policy
, it is recommended to
define the corresponding policy with the attribute.
This is for clarification. If an attribute is marked as enforce_policy
in the API definitions, for example, the neutron API limits to set such
attribute only to admin users but allows to retrieve a value for regular
users. If policies for the attribute are different across the types of
operations, it is better to define all of them explicitly.[1] | Oslo policy module |
[2] | Oslo policy developer |
[3] | API controller item method |
[4] | Policy engine’s build_match_rule method |
[5] | exclude_attributes_by_policy method |
[6] | Policy reset in neutron.api.v2.router |
[7] | https://github.com/openstack/neutron/blob/051b6b40f3921b9db4f152a54f402c402cbf138c/neutron/pecan_wsgi/hooks/policy_enforcement.py#L173 |
[8] | https://github.com/openstack/neutron/blob/051b6b40f3921b9db4f152a54f402c402cbf138c/neutron/pecan_wsgi/hooks/policy_enforcement.py#L143 |
[9] | https://docs.openstack.org/oslo.policy/latest/user/usage.html#sample-file-generation |
[10] | https://docs.openstack.org/oslo.policy/latest/cli/index.html#oslopolicy-sample-generator |
Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. See all OpenStack Legal Documents.