Merge "Add support for scoped tokens and default roles"

This commit is contained in:
Zuul 2021-03-10 02:55:08 +00:00 committed by Gerrit Code Review
commit 41ff3ee3f1
10 changed files with 499 additions and 59 deletions

View File

@ -2,6 +2,19 @@
Octavia Policies
================
.. _Keystone Default Roles: https://docs.openstack.org/keystone/latest/admin/service-api-protection.html
Octavia Advanced Role Based Access Control (RBAC)
-------------------------------------------------
Octavia adopted the "Advanced Role Based Access Control (RBAC)" default
policies in the Pike release of OpenStack. This provides a fine-grained default
access control policy for the Octavia service.
The Octavia Advanced RBAC goes beyond the OpenStack legacy RBAC policies of
allowing "owners and admins" full access to all services. It also provides a
more fine-grained RBAC policy than the newer `Keystone Default Roles`_ .
The default policy is to not allow access unless the auth_strategy is 'noauth'.
Users must be a member of one of the following roles to have access to
@ -23,11 +36,11 @@ the load-balancer API:
User is considered an admin for quota APIs only.
role:load-balancer_admin
User is considered an admin for all load-balnacer APIs including
User is considered an admin for all load-balancer APIs including
resources owned by others.
role:admin
User is admin to all APIs.
role:admin and system_scope:all
User is admin to all service APIs, including Octavia.
.. note::
@ -36,13 +49,136 @@ the load-balancer API:
It is equivalent to 'rule:context_is_admin or {auth_strategy == noauth}'
if that would be valid syntax.
Legacy Admin or Owner Policy
----------------------------
These roles are in addition to the `Keystone Default Roles`_:
* role:reader
* role:member
In addition, the Octavia API supports Keystone scoped tokens. When enabled
in Oslo Policy, users will need to present a token scoped to either the
"system" or a specific "project". See the section `Upgrade Considerations`_
for more information.
See the section `Managing Octavia User Roles`_ for examples and advice on how
to apply these RBAC policies in production.
Legacy Admin or Owner Policy Override File
------------------------------------------
An alternate policy file has been provided in octavia/etc/policy called
admin_or_owner-policy.yaml that removes the load-balancer RBAC role
requirement. Please see the README.rst in that directory for more information.
This will drop the role requirements to allow access to all with the "admin"
role or if the user is a member of the project that created the resource. All
users have access to the Octavia API to create and manage load balancers
under their project.
OpenStack Default Roles Policy Override File
--------------------------------------------
An alternate policy file has been provided in octavia/etc/policy called
keystone_default_roles-policy.yaml that removes the load-balancer RBAC role
requirement. Please see the README.rst in that directory for more information.
This policy will honor the following `Keystone Default Roles`_ in the Octavia
API:
* System scoped - Admin
* System scoped - Reader
* Project scoped - Reader
* Project scoped - Member
Managing Octavia User Roles
---------------------------
User and group roles are managed through the Keystone (identity) project.
A role can be added to a user with the following command::
openstack role add --project <project name or id> --user <user name or id> <role>
An example where user "jane", in the "engineering" project, gets a new role
"load-balancer_member"::
openstack role add --project engineering --user jane load-balancer_member
Keystone Group Roles
~~~~~~~~~~~~~~~~~~~~
Roles can also be assigned to `Keystone groups
<https://docs.openstack.org/keystone/latest/admin/identity-concepts.html>`_.
This can simplify the management of user roles greatly.
For example, your cloud may have a "users" group defined in Keystone. This
group is set up to have all of the regular users of your cloud as a member.
If you want all of your users to have access to the load balancing service
Octavia, you could add the "load-balancer_member" role to the "users" group::
openstack role add --domain default --group users load-balancer_member
Upgrade Considerations
----------------------
Starting with the Wallaby release of Octavia, Keystone token scopes and
default roles can be enforced. By default, in the Wallaby release, `Oslo Policy
<https://docs.openstack.org/oslo.policy/latest>`_
will not be enforcing these new roles and scopes. However, at some point in the
future they may become the default. You may want to enable them now to be ready
for the later transition. This section will describe those settings.
The Oslo Policy project defines two configuration settings, among others, that
can be set in the Octavia configuration file to influence how policies are
handled in the Octavia API. Those two settings are `enforce_scope
<https://docs.openstack.org/oslo.policy/latest/configuration/index.html#oslo_policy.enforce_scope>`_ and `enforce_new_defaults
<https://docs.openstack.org/oslo.policy/latest/configuration/index.html#oslo_policy.enforce_new_defaults>`_.
[oslo_policy] enforce_scope
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Keystone has introduced the concept of `token scopes
<https://docs.openstack.org/keystone/latest/admin/tokens-overview.html#authorization-scopes>`_.
Currently, Oslo Policy defaults to not enforce the scope validation of a
token for backward compatibility reasons.
The Octavia API supports enforcing the Keystone token scopes as of the Wallaby
release. If you are ready to start enforcing the Keystone token scope in the
Octavia API you can add the following setting to your Octavia API configuration
file::
[oslo_policy]
enforce_scope = True
Currently the primary effect of this setting is to allow a system scoped
admin token when performing administrative API calls to the Octavia API.
It will also allow system scoped reader tokens to have the equivalent of the
load-balancer_global_observer role.
The Octavia API already enforces the project scoping in Keystone tokens.
[oslo_policy] enforce_new_defaults
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Octavia Wallaby release added support for `Keystone Default Roles`_ in
the default policies. The previous Octavia Advanced RBAC policies have now
been deprecated in favor of the new policies requiring one of the new
`Keystone Default Roles`_.
Currently, Oslo Policy defaults to using the deprecated policies that do not
require the new `Keystone Default Roles`_ for backward compatibility.
The Octavia API supports requiring these new `Keystone Default Roles`_ as of
the Wallaby release. If you are ready to start requiring these roles you can
enable the new policies by adding the following setting to your Octavia API
configuration file::
[oslo_policy]
enforce_new_defaults = True
When the new default policies are enabled in the Octavia API, users with the
load-balancer:observer role will also require the Keystone default role of
"role:reader". Users with the load-balancer:member role will also require
the Keystone default role of "role:member".
Sample File Generation
----------------------
@ -76,7 +212,109 @@ in effective policy::
oslopolicy-list-redundant
--config-file etc/policy/octavia-policy-generator.conf
Default Octavia Policies
------------------------
Default Octavia Policies - API Effective Rules
----------------------------------------------
This section will list the RBAC rules the Octavia API will use followed by a
list of the roles that will be allowed access.
Without `enforce_scope
<https://docs.openstack.org/oslo.policy/latest/configuration/index.html#oslo_policy.enforce_scope>`_ and `enforce_new_defaults
<https://docs.openstack.org/oslo.policy/latest/configuration/index.html#oslo_policy.enforce_new_defaults>`_:
* load-balancer:read
* load-balancer_admin
* load-balancer_global_observer
* load-balancer_member and <project member>
* load-balancer_observer and <project member>
* role:admin
* load-balancer:read-global
* load-balancer_admin
* load-balancer_global_observer
* role:admin
* load-balancer:write
* load-balancer_admin
* load-balancer_member and <project member>
* role:admin
* load-balancer:read-quota
* load-balancer_admin
* load-balancer_global_observer
* load-balancer_member and <project member>
* load-balancer_observer and <project member>
* load-balancer_quota_admin
* role:admin
* load-balancer:read-quota-global
* load-balancer_admin
* load-balancer_global_observer
* load-balancer_quota_admin
* role:admin
* load-balancer:write-quota
* load-balancer_admin
* load-balancer_quota_admin
* role:admin
With `enforce_scope
<https://docs.openstack.org/oslo.policy/latest/configuration/index.html#oslo_policy.enforce_scope>`_ and `enforce_new_defaults
<https://docs.openstack.org/oslo.policy/latest/configuration/index.html#oslo_policy.enforce_new_defaults>`_:
* load-balancer:read
* load-balancer_admin
* load-balancer_global_observer
* load-balancer_member and <project member> and role:member
* load-balancer_observer and <project member> and role:reader
* role:admin and system_scope:all
* role:reader and system_scope:all
* load-balancer:read-global
* load-balancer_admin
* load-balancer_global_observer
* role:admin and system_scope:all
* role:reader and system_scope:all
* load-balancer:write
* load-balancer_admin
* load-balancer_member and <project member> and role:member
* role:admin and system_scope:all
* load-balancer:read-quota
* load-balancer_admin
* load-balancer_global_observer
* load-balancer_member and <project member> and role:member
* load-balancer_observer and <project member> and role:reader
* load-balancer_quota_admin
* role:admin and system_scope:all
* role:reader and system_scope:all
* load-balancer:read-quota-global
* load-balancer_admin
* load-balancer_global_observer
* load-balancer_quota_admin
* role:admin and system_scope:all
* role:reader and system_scope:all
* load-balancer:write-quota
* load-balancer_admin
* load-balancer_quota_admin
* role:admin and system_scope:all
Default Octavia Policies - Generated From The Octavia Code
----------------------------------------------------------
.. literalinclude:: _static/octavia.policy.yaml.sample

View File

@ -5,6 +5,8 @@ Octavia Sample Policy Files
The sample policy.yaml files described here can be copied into
/etc/octavia/policy.yaml to override the default RBAC policy for Octavia.
See the `Octavia Policy Guide <https://docs.openstack.org/octavia/latest/configuration/policy.html>`_ for more information about these policy override files.
admin_or_owner-policy.yaml
--------------------------
This policy file disables the requirement for load-balancer service users to
@ -12,3 +14,15 @@ have one of the load-balancer:* roles. It provides a similar policy to
legacy OpenStack policies where any user or admin has access to load-balancer
resources that they own. Users with the admin role has access to all
load-balancer resources, whether they own them or not.
keystone_default_roles-policy.yaml
----------------------------------
This policy file disables the requirement for load-balancer service users to
have one of the load-balancer:* roles.
This policy will honor the following Keystone default roles in the Octavia API:
* System scoped - Admin
* System scoped - Reader
* Project scoped - Reader
* Project scoped - Member

View File

@ -0,0 +1,37 @@
# This policy YAML file will revert the Octavia API to follow the keystone
# "default role" RBAC policies.
#
# The [oslo_policy] enforce_scope and enforce_new_defaults must be True.
#
# Users will not be required to be a member of the load-balancer_* roles
# to take action on Octavia resources.
# Keystone token scoping and "default roles"/personas will still be enforced.
# Role Rules
"system_admin": "role:admin and system_scope:all"
"system_reader": "role:reader and system_scope:all"
"project_reader": "role:reader and project_id:%(project_id)s"
"project_member": "role:member and project_id:%(project_id)s"
"context_is_admin": "role:admin and system_scope:all"
# API Rules
"load-balancer:admin": "is_admin:True or
rule:system_admin or
role:load-balancer_admin"
"load-balancer:read": "is_admin:True or
rule:system_reader or
rule:project_reader"
"load-balancer:read-global": "is_admin:True or rule:system_reader"
"load-balancer:write": "is_admin:True or rule:project_member"
"load-balancer:read-quota": "is_admin:True or
rule:system_reader or
rule:project_reader"
"load-balancer:read-quota-global": "is_admin:True or rule:system_reader"
"load-balancer:write-quota": "is_admin:True"

View File

@ -79,14 +79,14 @@ os-service-types==1.2.0
osc-lib==1.10.0
oslo.cache==1.29.0
oslo.concurrency==3.26.0
oslo.config==5.2.0
oslo.config==6.8.0
oslo.context==2.22.0
oslo.db==8.4.0
oslo.i18n==3.20.0
oslo.log==4.3.0
oslo.messaging==12.4.0
oslo.middleware==4.0.1
oslo.policy==2.1.0
oslo.policy==3.6.2
oslo.reports==1.18.0
oslo.serialization==2.28.1
oslo.service==1.30.0
@ -130,14 +130,14 @@ python-neutronclient==6.7.0
python-novaclient==9.1.0
python-subunit==1.0.0
pytz==2018.3
PyYAML==3.12
PyYAML==5.1
redis==2.10.0
repoze.lru==0.7
requests==2.14.2
requests==2.18.4
requests-mock==1.2.0
requestsexceptions==1.4.0
restructuredtext-lint==1.1.3
rfc3986==0.3.1
rfc3986==1.2.0
Routes==2.4.1
setproctitle==1.1.10
simplegeneric==0.8.1

View File

@ -731,6 +731,7 @@ DEFAULT_PAGE_SIZE = 1000
# RBAC
LOADBALANCER_API = 'os_load-balancer_api'
RULE_API_ADMIN = 'rule:load-balancer:admin'
RULE_API_READ = 'rule:load-balancer:read'
RULE_API_READ_GLOBAL = 'rule:load-balancer:read-global'
@ -738,6 +739,7 @@ RULE_API_WRITE = 'rule:load-balancer:write'
RULE_API_READ_QUOTA = 'rule:load-balancer:read-quota'
RULE_API_READ_QUOTA_GLOBAL = 'rule:load-balancer:read-quota-global'
RULE_API_WRITE_QUOTA = 'rule:load-balancer:write-quota'
RBAC_LOADBALANCER = '{}:loadbalancer:'.format(LOADBALANCER_API)
RBAC_LISTENER = '{}:listener:'.format(LOADBALANCER_API)
RBAC_POOL = '{}:pool:'.format(LOADBALANCER_API)
@ -756,6 +758,7 @@ RBAC_FLAVOR_PROFILE = '{}:flavor-profile:'.format(LOADBALANCER_API)
RBAC_AVAILABILITY_ZONE = '{}:availability-zone:'.format(LOADBALANCER_API)
RBAC_AVAILABILITY_ZONE_PROFILE = '{}:availability-zone-profile:'.format(
LOADBALANCER_API)
RBAC_POST = 'post'
RBAC_PUT = 'put'
RBAC_PUT_CONFIG = 'put_config'
@ -768,6 +771,16 @@ RBAC_GET_DEFAULTS = 'get_defaults'
RBAC_GET_STATS = 'get_stats'
RBAC_GET_STATUS = 'get_status'
RBAC_SCOPE_PROJECT = 'project'
RBAC_SCOPE_SYSTEM = 'system'
RBAC_ROLES_DEPRECATED_REASON = (
'The Octavia API now requires the OpenStack default roles and scoped '
'tokens. '
'See https://docs.openstack.org/octavia/latest/configuration/policy.html '
'and https://docs.openstack.org/keystone/latest/contributor/'
'services.html#reusable-default-roles for more information.')
# PROVIDERS
OCTAVIA = 'octavia'
AMPHORAV2 = 'amphorav2'

View File

@ -125,7 +125,16 @@ class Policy(oslo_policy.Enforcer):
"""
credentials = context.to_dict()
return self.enforce('context_is_admin', credentials, credentials)
result = False
try:
result = self.enforce('context_is_admin', credentials, credentials)
except oslo_policy.InvalidScope as e:
# This will happen if the token being used is not system scoped
# which is required for the admin roles when scope checking is
# enabled.
LOG.warning(str(e))
return False
return result
def get_rules(self):
return self.rules

View File

@ -10,9 +10,61 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import versionutils
from oslo_policy import policy
from octavia.common import constants
deprecated_context_is_admin = policy.DeprecatedRule(
name='context_is_admin',
check_str='role:admin or '
'role:load-balancer_admin'
)
deprecated_observer_and_owner = policy.DeprecatedRule(
name='load-balancer:observer_and_owner',
check_str='role:load-balancer_observer and '
'rule:load-balancer:owner'
)
deprecated_member_and_owner = policy.DeprecatedRule(
name='load-balancer:member_and_owner',
check_str='role:load-balancer_member and '
'rule:load-balancer:owner'
)
rules = [
# OpenStack wide scoped rules
# System scoped Administrator
policy.RuleDefault(
name='system-admin',
check_str='role:admin and '
'system_scope:all',
scope_types=[constants.RBAC_SCOPE_SYSTEM]),
# System scoped Reader
policy.RuleDefault(
name='system-reader',
check_str='role:reader and '
'system_scope:all',
scope_types=[constants.RBAC_SCOPE_SYSTEM]),
# Project scoped Member
policy.RuleDefault(
name='project-member',
check_str='role:member and '
'project_id:%(project_id)s',
scope_types=[constants.RBAC_SCOPE_PROJECT]),
# Project scoped Reader
policy.RuleDefault(
name='project-reader',
check_str='role:reader and '
'project_id:%(project_id)s',
scope_types=[constants.RBAC_SCOPE_PROJECT]),
# Octavia specific Advanced RBAC rules
# The default is to not allow access unless the auth_strategy is 'noauth'.
# Users must be a member of one of the following roles to have access to
# the load-balancer API:
@ -25,66 +77,107 @@ rules = [
# role:load-balancer_member
# User has access to load-balancer read and write APIs
# role:load-balancer_admin
# User is considered an admin for all load-balnacer APIs including
# User is considered an admin for all load-balancer APIs including
# resources owned by others.
# role:admin
# User is admin to all APIs
# role:admin and system_scope:all
# User is admin to all service APIs, including Octavia.
policy.RuleDefault('context_is_admin',
'role:admin or role:load-balancer_admin'),
policy.RuleDefault(
name='context_is_admin',
check_str='role:load-balancer_admin or '
'rule:system-admin',
deprecated_rule=deprecated_context_is_admin,
deprecated_reason=constants.RBAC_ROLES_DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY,
scope_types=[constants.RBAC_SCOPE_SYSTEM]),
# Note: 'is_admin:True' is a policy rule that takes into account the
# auth_strategy == noauth configuration setting.
# It is equivalent to 'rule:context_is_admin or {auth_strategy == noauth}'
policy.RuleDefault('load-balancer:owner', 'project_id:%(project_id)s'),
policy.RuleDefault(
name='load-balancer:owner',
check_str='project_id:%(project_id)s',
scope_types=[constants.RBAC_SCOPE_PROJECT]),
# API access roles
policy.RuleDefault('load-balancer:admin', 'is_admin:True or '
'role:admin or '
'role:load-balancer_admin'),
policy.RuleDefault(
name='load-balancer:observer_and_owner',
check_str='role:load-balancer_observer and '
'rule:project-reader',
deprecated_rule=deprecated_observer_and_owner,
deprecated_reason=constants.RBAC_ROLES_DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY,
scope_types=[constants.RBAC_SCOPE_PROJECT]),
policy.RuleDefault('load-balancer:observer_and_owner',
'role:load-balancer_observer and '
'rule:load-balancer:owner'),
policy.RuleDefault(
name='load-balancer:global_observer',
check_str='role:load-balancer_global_observer or '
'rule:system-reader',
scope_types=[constants.RBAC_SCOPE_SYSTEM]),
policy.RuleDefault('load-balancer:global_observer',
'role:load-balancer_global_observer'),
policy.RuleDefault('load-balancer:member_and_owner',
'role:load-balancer_member and '
'rule:load-balancer:owner'),
policy.RuleDefault(
name='load-balancer:member_and_owner',
check_str='role:load-balancer_member and '
'rule:project-member',
deprecated_rule=deprecated_member_and_owner,
deprecated_reason=constants.RBAC_ROLES_DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY,
scope_types=[constants.RBAC_SCOPE_PROJECT]),
# API access methods
policy.RuleDefault('load-balancer:read',
'rule:load-balancer:observer_and_owner or '
'rule:load-balancer:global_observer or '
'rule:load-balancer:member_and_owner or '
'rule:load-balancer:admin'),
policy.RuleDefault('load-balancer:read-global',
'rule:load-balancer:global_observer or '
'rule:load-balancer:admin'),
policy.RuleDefault(
name='load-balancer:admin',
check_str='is_admin:True or '
'role:load-balancer_admin or '
'rule:system-admin',
scope_types=[constants.RBAC_SCOPE_SYSTEM]),
policy.RuleDefault('load-balancer:write',
'rule:load-balancer:member_and_owner or '
'rule:load-balancer:admin'),
policy.RuleDefault(
name='load-balancer:read',
check_str='rule:load-balancer:observer_and_owner or '
'rule:load-balancer:global_observer or '
'rule:load-balancer:member_and_owner or '
'rule:load-balancer:admin',
scope_types=[constants.RBAC_SCOPE_PROJECT,
constants.RBAC_SCOPE_SYSTEM]),
policy.RuleDefault('load-balancer:read-quota',
'rule:load-balancer:observer_and_owner or '
'rule:load-balancer:global_observer or '
'rule:load-balancer:member_and_owner or '
'role:load-balancer_quota_admin or '
'rule:load-balancer:admin'),
policy.RuleDefault(
name='load-balancer:read-global',
check_str='rule:load-balancer:global_observer or '
'rule:load-balancer:admin',
scope_types=[constants.RBAC_SCOPE_SYSTEM]),
policy.RuleDefault('load-balancer:read-quota-global',
'rule:load-balancer:global_observer or '
'role:load-balancer_quota_admin or '
'rule:load-balancer:admin'),
policy.RuleDefault(
name='load-balancer:write',
check_str='rule:load-balancer:member_and_owner or '
'rule:load-balancer:admin',
scope_types=[constants.RBAC_SCOPE_PROJECT,
constants.RBAC_SCOPE_SYSTEM]),
policy.RuleDefault('load-balancer:write-quota',
'role:load-balancer_quota_admin or '
'rule:load-balancer:admin'),
policy.RuleDefault(
name='load-balancer:read-quota',
check_str='rule:load-balancer:observer_and_owner or '
'rule:load-balancer:global_observer or '
'rule:load-balancer:member_and_owner or '
'role:load-balancer_quota_admin or '
'rule:load-balancer:admin',
scope_types=[constants.RBAC_SCOPE_PROJECT,
constants.RBAC_SCOPE_SYSTEM]),
policy.RuleDefault(
name='load-balancer:read-quota-global',
check_str='rule:load-balancer:global_observer or '
'role:load-balancer_quota_admin or '
'rule:load-balancer:admin',
scope_types=[constants.RBAC_SCOPE_SYSTEM]),
policy.RuleDefault(
name='load-balancer:write-quota',
check_str='role:load-balancer_quota_admin or '
'rule:load-balancer:admin',
scope_types=[constants.RBAC_SCOPE_SYSTEM]),
]

View File

@ -162,11 +162,23 @@ class PolicyTestCase(base.TestCase):
def test_check_is_admin_fail(self):
self.assertFalse(policy.get_enforcer().check_is_admin(self.context))
# TODO(johnsom) When oslo.policy changes "enforce_new_defaults" to True
# this test will fail as "system_scope:all" will be required.
# This test and the conditional in common/policy.py can then
# be removed in favor of test_check_is_admin_new_defaults().
def test_check_is_admin(self):
self.context = context.Context('admin', 'fake', roles=['AdMiN'])
self.assertTrue(policy.get_enforcer().check_is_admin(self.context))
def test_check_is_admin_new_defaults(self):
conf = oslo_fixture.Config(config.cfg.CONF)
conf.config(group="oslo_policy", enforce_new_defaults=True)
self.context = context.Context('admin', 'fake', roles=['AdMiN'],
system_scope='all')
self.assertTrue(policy.get_enforcer().check_is_admin(self.context))
def test_get_enforcer(self):
self.assertTrue(isinstance(policy.get_no_context_enforcer(),
oslo_policy.Enforcer))

View File

@ -0,0 +1,24 @@
---
features:
- |
Added support for keystone default roles and system token scopes.
upgrade:
- |
Legacy Octavia Advanced RBAC policies will continue to function as before
as long as the [oslo_policy] enforce_scope = False and
enforce_new_defaults = False settings are present (this is the current
oslo.policy default). However, we highly recommend you update your
user roles to follow the new keystone default roles and start using scoped
tokens as appropriate.
See the `Octavia Policies
<https://docs.openstack.org/octavia/latest/configuration/policy.html>`_
administration guide for more information.
deprecations:
- |
Legacy Octavia Advanced RBAC policies without the keystone default roles
and/or token scoping are deprecated as of the Wallaby release.
The oslo.policy project may change the default settings requiring the
keystone default roles and scoped tokens in a future release. Please see
the upgrade section in these release notes and the `Octavia Policies
<https://docs.openstack.org/octavia/latest/configuration/policy.html>`_
administration guide for more information.

View File

@ -8,21 +8,21 @@ pbr>=3.1.1 # Apache-2.0
SQLAlchemy>=1.2.19 # MIT
SQLAlchemy-Utils>=0.30.11
futurist>=1.2.0 # Apache-2.0
requests>=2.14.2 # Apache-2.0
rfc3986>=0.3.1 # Apache-2.0
requests>=2.18.4 # Apache-2.0
rfc3986>=1.2.0 # Apache-2.0
keystoneauth1>=3.4.0 # Apache-2.0
keystonemiddleware>=4.17.0 # Apache-2.0
python-neutronclient>=6.7.0 # Apache-2.0
WebOb>=1.8.2 # MIT
stevedore>=1.20.0 # Apache-2.0
oslo.config>=5.2.0 # Apache-2.0
oslo.config>=6.8.0 # Apache-2.0
oslo.context>=2.22.0 # Apache-2.0
oslo.db[mysql]>=8.4.0 # Apache-2.0
oslo.i18n>=3.20.0 # Apache-2.0
oslo.log>=4.3.0 # Apache-2.0
oslo.messaging>=12.4.0 # Apache-2.0
oslo.middleware>=4.0.1 # Apache-2.0
oslo.policy>=2.1.0 # Apache-2.0
oslo.policy>=3.6.2 # Apache-2.0
oslo.reports>=1.18.0 # Apache-2.0
oslo.serialization>=2.28.1 # Apache-2.0
oslo.upgradecheck>=0.1.0 # Apache-2.0