Launchpad blueprint:
https://blueprints.launchpad.net/neutron/+spec/group-based-policy-abstraction
This blueprint proposes an extension to the Neutron API with a declarative policy driven connectivity model that presents simplified application-oriented interfaces to the user.
The current Neutron model of networks, ports, subnets, routers, and security groups provides the necessary building blocks to build a logical network topology for connectivity. However, it does not provide the right level of abstraction for an application administrator who understands the application's details (like application port numbers), but not the infrastructure details likes networks and routes. Not only that, the current abstraction puts the burden of maintaining the consistency of the network topology on the user. The lack of application developer/administrator focussed abstractions supported by a declarative model make it hard for those users to consume Neutron as a connectivity layer.
The policy framework described in this blueprint complements the current Neutron model with the notion of policies that can be applied between groups of endpoints. As users look beyond basic connectivity, richer network services with diverse implementations and network properties are naturally expressed as policies. Examples include service chaining, QoS, path properties, access control, etc.
This proposal suggests a model that allows application administrators to express their networking requirements using group and policy abstractions, with the specifics of policy enforcement and implementation left to the underlying policy driver. The main advantage of the extensions described in this blueprint is that they allow for an application-centric interface to Neutron that complements the existing network-centric interface.
More specifically the new abstractions will achieve the following:
The following new terminology is being introduced:
Endpoint (EP): An L2/L3 addressable entity.
Endpoint Group (EPG): A collection of endpoints.
Contract: It defines how the application services provided by an EPG can be accessed. In effect it specifies how an EPG communicates with other EPGs. A Contract consists of Policy Rules.
Policy Rule: These are individual rules used to define the communication criteria between EPGs. Each rule contains a Filter, Classifier, and Action.
Classifier: Characterizes the traffic that a particular Policy Rule acts on. Corresponding action is taken on traffic that satisfies this classification criteria.
Action: The action that is taken for a matching Policy Rule defined in a Contract.
Filter: Provides a way to tag a Policy Rule with Capability and Role labels.
Capability: It is a Policy Label that defines what part of a Contract a particular EPG provides.
Role: It is a Policy Label that defines what part of a Contract an EPG wants to consume.
Contract Scope: An EPG conveys its intent to provide or consume a Contract (or its part) by defining a Contract Scope which references the target Contract.
Selector: A Contract Scope can define additional constraints around choosing the matching provider or consumer EPGs for a Contract via a Selector.
Policy Labels: These are labels contained within a namespace hierarchy and used to define Capability and Role tags used in Filters.
Bridge Domain: Used to define a L2 boundary and impose additional constraints (such as no broadcast) within that L2 boundary.
Routing Domain: Used to define a non-overlapping IP address space.
Here is an example of how a three tier application would look like:
+–––––––––+ +–––––––+ +–––––––+ +–––––––+
| | | Web | | App | |DB |
| Outside | | EPG | | EPG | |EPG |
| Public | +––––––––+ +––+ | +––––––––+ +––+ | +––––––––+ +––+ |
| Network +–+Web | |VM| +–+App | |VM| +–+DB | |VM| |
| EPG | |Contract| +––+ | |Contract| +––+ | |Contract| +––+ |
| | +––––––––+ | +––––––––+ | +––––––––+ |
| | | +––+ | | +––+ | | +––+ |
| | | |VM| | | |VM| | | |VM| |
| | | +––+ | | +––+ | | +––+ |
+–––––––––+ +–––––––+ +–––––––+ +–––––––+
Example CLI:
(exmaple only shows access to the Web Server Tier from the Outside Network)
Create Classifier
neutron classifier-create Insecure-Web-Access --port 80 --protocol TCP
--direction IN
Create Contract using the Classifier
neutron contract-create Web-Server-Contract --classifier Insecure-Web-Access
--action ALLOW
Create EPG providing the Contract
neutron epg-create Web-Server-EPG --provides-contract Web-Server-Contract
Create Endpoint in EPG
neutron ep-create --epg Web-Server-EPG
Launch Web Server VM using Endpoint in EPG
nova boot --image cirros --flavor m1.nano --nic port-id=<EP-NAME> Web-Server
Specify connectivity of Outside world VMs to Web Server
neutron epg-create Outside-EPG --consumes-contract Web-Server-Contract
Note that the Contract Provider/Consuming Scopes are not explicitly shown in the above diagram but define each providing and consuming relation between an EPG and a Contract as shown below:
+––––––––––+
|Web |
|Contract |
|Consuming |
|Scope |
+–––+––––––+
+–––––––––+ | +––––––––––+ | | | | Web | | Outside | | | EPG | | Public | | +––––––––+ | +––+ | | Network +–+–+Web +––+–+ EP | | EPG | | | +––+ | | | +––––––––+ | | | | | | | +––+ | | | | | EP | | | | | +––+ | +–––––––––+ | | | | +––––––––––+ + +––––+–––––+ +––––––––––+
Since a new level of abstraction is being proposed here, a direct alternate does not exist in the current model.
New Database Objects to support Group Policy:
+–––––––––––––+ +–––––––––––––––+ +–––––––––––+
| | | Contract | |Contracts |
| Endpoint | | Providing/ | | |
| Groups +–––––+ Consuming +––––––+ |
| | | Scopes | +–––––+–––––+
+––––––+––––––+ +–––––––––––––––+ |
| +–––––+–––––+
| |Policy |
+––––––+––––––+ |Rules |
| | | |
| Endpoints | +–––––+––––––+––––+––––––––+
| | | | |
+–––––––––––––+ | | |
| | |
+–––––+––+ +––––––+–––––+ +–––––+––+
|Filters | |Classifiers | |Actions |
| | | | | |
+––––––––+ +––––––––––––+ +––––––––+
The way ip_pool and default_subnet_prefix_length work is as follows: When creating L3Policy a default ip_pool and default_subnet_prefix_length are created. If a user creates an EPG, a subnet will be pulled from ip_pool using default_subnet_prefix_length.
Objects to support Mapping to existing Neutron resources
Appropriate foreign key constraints will be added to maintain the referential integrity of the model.
Database migrations: New tables are being added to the schema, however the existing schema remains unchanged.
The following new resources are being introduced:
gp_supported_actions = [None, 'ALLOW', 'REDIRECT']
gp_supported_directions = [None, 'IN', 'OUT', 'BI']
gp_supported_protocols = [None, 'TCP', 'UDP', 'ICMP']
gp_supported_scopes = [None, 'GLOBAL', 'TENANT', 'EPG']
ENDPOINTS = 'endpoints'
ENDPOINT_GROUPS = 'endpoint_groups'
CONTRACTS = 'contracts'
CONTRACT_PROVIDING_SCOPES = 'contract_providing_scopes'
CONTRACT_CONSUMING_SCOPES = 'contract_consuming_scopes'
POLICY_RULES = 'policy_rules'
FILTERS = 'filters'
CLASSIFIERS = 'classifiers'
ACTIONS = 'actions'
SELECTORS = 'selectors'
POLICY_LABELS = 'policy_labels'
L2_POLICIES = 'l2_policies'
L3_POLICIES = 'l3_policies'
RESOURCE_ATTRIBUTE_MAP = {
ENDPOINTS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None}, 'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None}, 'default': '',
'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True, 'is_visible': True},
'endpointgroup_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid__or_none': None},
'required': True, 'is_visible': True},
},
ENDPOINT_GROUPS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None}, 'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True, 'is_visible': True},
'endpoints': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'default': None, 'is_visible': True},
'bridge_domain_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_or_none': None},
'default': None, 'is_visible': True},
'provided_contract_scopes': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_list': None},
'convert_to':
attr.convert_none_to_empty_list,
'default': None, 'is_visible': True},
'consumed_contract_scopes': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_list': None},
'convert_to':
attr.convert_none_to_empty_list,
'default': None, 'is_visible': True},
},
CONTRACTS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '',
'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True,
'is_visible': True},
'child_contracts': {'allow_post': True, 'allow_put': True,
'default': None,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'required': True, 'is_visible': True},
'policy_rules': {'allow_post': True, 'allow_put': True,
'default': None,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'required': True, 'is_visible': True},
},
CONTRACT_PROVIDING_SCOPES: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '',
'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True,
'is_visible': True},
'endpointgroup_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid': None},
'required': True, 'is_visible': True},
'contract_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid': None},
'required': True, 'is_visible': True},
'selector_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_or_none': None},
'required': True, 'is_visible': True},
'capabilities': {'allow_post': True, 'allow_put': True,
'default': None,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'required': True, 'is_visible': True},
},
CONTRACT_CONSUMING_SCOPES: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True, 'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '',
'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True,
'is_visible': True},
'endpointgroup_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid': None},
'required': True, 'is_visible': True},
'contract_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid': None},
'required': True, 'is_visible': True},
'selector_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_or_none': None},
'required': True, 'is_visible': True},
'roles': {'allow_post': True, 'allow_put': True,
'default': None,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'required': True, 'is_visible': True},
},
POLICY_RULES: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True, 'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True,
'is_visible': True},
'enabled': {'allow_post': True, 'allow_put': True,
'default': True, 'convert_to': attr.convert_to_boolean,
'is_visible': True},
'filter_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_or_none': None},
'required': True, 'is_visible': True},
'classifier_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid': None},
'required': True, 'is_visible': True},
'actions': {'allow_post': True, 'allow_put': True,
'default': None,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'required': True, 'is_visible': True},
},
FILTERS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True, 'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True,
'is_visible': True},
'provider_capabilities': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_list': None},
'convert_to':
attr.convert_none_to_empty_list,
'required': True, 'is_visible': True},
'consumer_roles': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'required': True, 'is_visible': True},
},
CLASSIFIERS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True, 'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True,
'is_visible': True},
'protocol': {'allow_post': True, 'allow_put': True,
'is_visible': True, 'default': None,
'convert_to': convert_protocol,
'validate': {'type:values': gp_supported_protocols}},
'port_range': {'allow_post': True, 'allow_put': True,
'validate': {'type:port_range': None},
'convert_to': convert_port_to_string,
'default': None, 'is_visible': True},
'direction': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': gp_supported_directions},
'default': None, 'is_visible': True},
},
ACTIONS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True,
'is_visible': True},
'action_type': {'allow_post': True, 'allow_put': True,
'convert_to': convert_action_to_case_insensitive,
'validate': {'type:values': gp_supported_actions},
'is_visible': True, 'default': 'allow'},
'action_value': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_or_none': None},
'is_visible': True},
},
SELECTORS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True,
'is_visible': True},
'scope': {'allow_post': True, 'allow_put': True,
'convert_to': convert_scope_to_case_insensitive,
'validate': {'type:values': gp_supported_scopes},
'is_visible': True, 'default': 'tenant'},
'value': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_or_none': None},
'is_visible': True},
},
POLICY_LABELS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True,
'is_visible': True},
'namespace': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'required': True},
},
L2_POLICIES: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None}, 'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True, 'is_visible': True},
'endpoint_groups': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'default': None, 'is_visible': True},
'routing_domain_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_or_none': None},
'default': None, 'is_visible': True,
'required': True},
},
L3_POLICIES: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None}, 'is_visible': True,
'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True, 'is_visible': True},
'ip_version': {'allow_post': True, 'allow_put': False,
'convert_to': attr.convert_to_int,
'validate': {'type:values': [4, 6]},
'is_visible': True},
'ip_pool': {'allow_post': True, 'allow_put': False,
'validate': {'type:subnet': None},
'default': '10.0.0.0/8', 'is_visible': True},
'default_subnet_prefix_length': {'allow_post': True, 'allow_put': True,
'convert_to': attr.convert_to_int,
'validate': {
# ipv4 specific validation is
# performed in the plugin code.
'type:values': range(1, 127)},
'default': 24, 'is_visible': True},
'l2_policies': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'default': None, 'is_visible': True},
},
}
The following defines the mapping to classical (existing) Neutron resources using attribute extension:
EXTENDED_ATTRIBUTES_2_0 = {
gpolicy.ENDPOINTS: {
'neutron_port_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:uuid_or_none': None},
'is_visible': True, 'default': None},
},
gpolicy.ENDPOINT_GROUPS: {
'neutron_subnets': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'default': None, 'is_visible': True},
},
gpolicy.L2_POLICIES: {
'neutron_network_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:uuid_or_none': None},
'is_visible': True, 'default': None},
},
gpolicy.L3_POLICIES: {
'neutron_routers': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'default': None, 'is_visible': True},
},
}
The connectivity model used here is consistent with OpenStack/Neutron's current white list model - that is, there is no connectivity outside an EPG unless explicitly allowed.
The rendering of the proposed new abstractions happens via existing Security Groups and Firewall as a Service constructs. As such, no new constructs or implementation that will directly affect the current security framework are being introduced.
Does this change touch sensitive data such as tokens, keys, or user data?
No
Does this change alter the API in a way that may impact security, such as a new way to access sensitive information or a new way to login?
No
Does this change involve cryptography or hashing?
No
Does this change require the use of sudo or any elevated privileges?
No
Does this change involve using or parsing user-provided data? This could be directly at the API level or indirectly such as changes to a cache layer.
No
Can this change enable a resource exhaustion attack, such as allowing a single API interaction to consume significant server resources? Some examples of this include launching subprocesses for each connection, or entity expansion attacks in XML.
The exposed risk here is no different from the existing APIs and would largely depend on the Policy Driver implementation.
None
Integration with following projects will be required:
A new layer of abstraction is being introduced. All performance considerations that are relevant to existing Neutron will apply and be taken into consideration during the implementation. It should be noted though that the use of this new layer of abstraction/extensions is optional, and as such will not affect the performance of the existing implementation if the former is not used.
This will be a new API, and will not affect existing API.
Sumit Naiksatam (snaiksat) - Launchpad blueprint assignee
Robert Kukura (rkukura)
Mandeep Dhami (mandeep-dhami)
Mohammad Banikazemi (banix)
Stephen Wong (s3wong)
Prasad Vellanki (prasad-vellanki)
Hemanth Ravi (hemanth-ravi)
Subrahmanyam Ongole (osms69)
Ronak Shah (ronak-malav-shah)
Rudra Rugge (rudrarugge)
Kanzhe Jiang (kanzhe-jiang)
Kevin Benton (kevinbenton)
Policy Manager Policy Driver
None
Both, functional and, system tests will be added.
Both, API and, Admin guide will be updated.