NSX|P: Handle port security

Change-Id: I4e8a64f5730f359f5b5b4a6c93da13e123ac6321
This commit is contained in:
Anna Khmelnitsky 2018-10-25 21:18:46 -07:00 committed by Adit Sarfaty
parent ff9d30f938
commit db43ace22f
5 changed files with 176 additions and 112 deletions

View File

@ -23,7 +23,6 @@ from neutron.db import l3_attrs_db
from neutron.db import l3_db
from neutron.db import models_v2
from neutron_lib.api.definitions import address_scope as ext_address_scope
from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef
from neutron_lib.api.definitions import availability_zone as az_def
from neutron_lib.api.definitions import external_net as extnet_apidef
from neutron_lib.api.definitions import network as net_def
@ -39,7 +38,6 @@ from neutron_lib import constants
from neutron_lib import context as n_context
from neutron_lib.db import api as db_api
from neutron_lib import exceptions as n_exc
from neutron_lib.exceptions import allowedaddresspairs as addr_exc
from neutron_lib.plugins import directory
from neutron_lib.services.qos import constants as qos_consts
from neutron_lib.utils import net
@ -48,7 +46,6 @@ from vmware_nsx._i18n import _
from vmware_nsx.common import exceptions as nsx_exc
from vmware_nsx.common import utils
from vmware_nsx.extensions import maclearning as mac_ext
from vmware_nsx.extensions import secgroup_rule_local_ip_prefix as sg_prefix
from vmware_nsx.services.qos.common import utils as qos_com_utils
from vmware_nsx.services.vpnaas.nsxv3 import ipsec_utils
@ -600,59 +597,12 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
self.set_extra_attr_value(context, router_db,
extra_attr, r[extra_attr])
def _get_interface_network(self, context, interface_info):
is_port, is_sub = self._validate_interface_info(interface_info)
if is_port:
net_id = self.get_port(context,
interface_info['port_id'])['network_id']
elif is_sub:
net_id = self.get_subnet(context,
interface_info['subnet_id'])['network_id']
return net_id
def _fix_sg_rule_dict_ips(self, sg_rule):
# 0.0.0.0/# is not a valid entry for local and remote so we need
# to change this to None
if (sg_rule.get('remote_ip_prefix') and
sg_rule['remote_ip_prefix'].startswith('0.0.0.0/')):
sg_rule['remote_ip_prefix'] = None
if (sg_rule.get(sg_prefix.LOCAL_IP_PREFIX) and
validators.is_attr_set(sg_rule[sg_prefix.LOCAL_IP_PREFIX]) and
sg_rule[sg_prefix.LOCAL_IP_PREFIX].startswith('0.0.0.0/')):
sg_rule[sg_prefix.LOCAL_IP_PREFIX] = None
def _validate_interface_address_scope(self, context,
router_db, interface_info):
gw_network_id = (router_db.gw_port.network_id if router_db.gw_port
else None)
subnet = self.get_subnet(context, interface_info['subnet_ids'][0])
if not router_db.enable_snat and gw_network_id:
self._validate_address_scope_for_router_interface(
context.elevated(), router_db.id, gw_network_id, subnet['id'])
def _validate_ipv4_address_pairs(self, address_pairs):
for pair in address_pairs:
ip = pair.get('ip_address')
if not utils.is_ipv4_ip_address(ip):
raise nsx_exc.InvalidIPAddress(ip_address=ip)
# NSXv3 and Policy only
def _create_port_address_pairs(self, context, port_data):
(port_security, has_ip) = self._determine_port_security_and_has_ip(
context, port_data)
address_pairs = port_data.get(addr_apidef.ADDRESS_PAIRS)
if validators.is_attr_set(address_pairs):
if not port_security:
raise addr_exc.AddressPairAndPortSecurityRequired()
else:
self._validate_ipv4_address_pairs(address_pairs)
self._process_create_allowed_address_pairs(context, port_data,
address_pairs)
else:
port_data[addr_apidef.ADDRESS_PAIRS] = []
def get_housekeeper(self, context, name, fields=None):
# run the job in readonly mode and get the results
self.housekeeper.run(context, name, readonly=True)

View File

View File

@ -0,0 +1,129 @@
# Copyright 2018 VMware, Inc.
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log as logging
from neutron.extensions import securitygroup as ext_sg
from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef
from neutron_lib.api.definitions import port_security as psec
from neutron_lib.api import validators
from neutron_lib.exceptions import allowedaddresspairs as addr_exc
from neutron_lib.exceptions import port_security as psec_exc
from vmware_nsx.common import exceptions as nsx_exc
from vmware_nsx.common import utils
from vmware_nsx.db import extended_security_group as extended_sec
from vmware_nsx.extensions import providersecuritygroup as provider_sg
from vmware_nsx.extensions import secgroup_rule_local_ip_prefix as sg_prefix
from vmware_nsx.plugins.common import plugin
LOG = logging.getLogger(__name__)
class NsxPluginV3Base(plugin.NsxPluginBase,
extended_sec.ExtendedSecurityGroupPropertiesMixin):
"""Common methods for NSX-V3 plugins"""
def _get_interface_network(self, context, interface_info):
is_port, is_sub = self._validate_interface_info(interface_info)
if is_port:
net_id = self.get_port(context,
interface_info['port_id'])['network_id']
elif is_sub:
net_id = self.get_subnet(context,
interface_info['subnet_id'])['network_id']
return net_id
def _fix_sg_rule_dict_ips(self, sg_rule):
# 0.0.0.0/# is not a valid entry for local and remote so we need
# to change this to None
if (sg_rule.get('remote_ip_prefix') and
sg_rule['remote_ip_prefix'].startswith('0.0.0.0/')):
sg_rule['remote_ip_prefix'] = None
if (sg_rule.get(sg_prefix.LOCAL_IP_PREFIX) and
validators.is_attr_set(sg_rule[sg_prefix.LOCAL_IP_PREFIX]) and
sg_rule[sg_prefix.LOCAL_IP_PREFIX].startswith('0.0.0.0/')):
sg_rule[sg_prefix.LOCAL_IP_PREFIX] = None
def _validate_interface_address_scope(self, context,
router_db, interface_info):
gw_network_id = (router_db.gw_port.network_id if router_db.gw_port
else None)
subnet = self.get_subnet(context, interface_info['subnet_ids'][0])
if not router_db.enable_snat and gw_network_id:
self._validate_address_scope_for_router_interface(
context.elevated(), router_db.id, gw_network_id, subnet['id'])
def _validate_ipv4_address_pairs(self, address_pairs):
for pair in address_pairs:
ip = pair.get('ip_address')
if not utils.is_ipv4_ip_address(ip):
raise nsx_exc.InvalidIPAddress(ip_address=ip)
def _create_port_address_pairs(self, context, port_data):
(port_security, has_ip) = self._determine_port_security_and_has_ip(
context, port_data)
address_pairs = port_data.get(addr_apidef.ADDRESS_PAIRS)
if validators.is_attr_set(address_pairs):
if not port_security:
raise addr_exc.AddressPairAndPortSecurityRequired()
else:
self._validate_ipv4_address_pairs(address_pairs)
self._process_create_allowed_address_pairs(context, port_data,
address_pairs)
else:
port_data[addr_apidef.ADDRESS_PAIRS] = []
def _provider_sgs_specified(self, port_data):
# checks if security groups were updated adding/modifying
# security groups, port security is set and port has ip
provider_sgs_specified = (validators.is_attr_set(
port_data.get(provider_sg.PROVIDER_SECURITYGROUPS)) and
port_data.get(provider_sg.PROVIDER_SECURITYGROUPS) != [])
return provider_sgs_specified
def _create_port_preprocess_security(
self, context, port, port_data, neutron_db, is_ens_tz_port):
(port_security, has_ip) = self._determine_port_security_and_has_ip(
context, port_data)
port_data[psec.PORTSECURITY] = port_security
# No port security is allowed if the port belongs to an ENS TZ
if (port_security and is_ens_tz_port and
not self._ens_psec_supported()):
raise nsx_exc.NsxENSPortSecurity()
self._process_port_port_security_create(
context, port_data, neutron_db)
# allowed address pair checks
self._create_port_address_pairs(context, port_data)
if port_security and has_ip:
self._ensure_default_security_group_on_port(context, port)
(sgids, psgids) = self._get_port_security_groups_lists(
context, port)
elif (self._check_update_has_security_groups({'port': port_data}) or
self._provider_sgs_specified(port_data) or
self._get_provider_security_groups_on_port(context, port)):
LOG.error("Port has conflicting port security status and "
"security groups")
raise psec_exc.PortSecurityAndIPRequiredForSecurityGroups()
else:
sgids = psgids = []
port_data[ext_sg.SECURITYGROUPS] = (
self._get_security_groups_on_port(context, port))
return port_security, has_ip, sgids, psgids

View File

@ -39,6 +39,7 @@ from neutron.db import portsecurity_db
from neutron.db import securitygroups_db
from neutron.db import vlantransparent_db
from neutron.extensions import providernet
from neutron.extensions import securitygroup as ext_sg
from neutron.quota import resource_registry
from neutron_lib.api.definitions import allowedaddresspairs as addr_apidef
from neutron_lib.api.definitions import external_net
@ -62,14 +63,13 @@ from vmware_nsx.common import locking
from vmware_nsx.common import managers
from vmware_nsx.common import utils
from vmware_nsx.db import db as nsx_db
from vmware_nsx.db import extended_security_group as extend_sg
from vmware_nsx.db import extended_security_group_rule as extend_sg_rule
from vmware_nsx.db import maclearning as mac_db
from vmware_nsx.extensions import projectpluginmap
from vmware_nsx.extensions import providersecuritygroup as provider_sg
from vmware_nsx.extensions import secgroup_rule_local_ip_prefix as sg_prefix
from vmware_nsx.extensions import securitygrouplogging as sg_logging
from vmware_nsx.plugins.common import plugin as nsx_plugin_common
from vmware_nsx.plugins.common_v3 import plugin as nsx_plugin_common
from vmware_nsx.plugins.nsx_v3 import utils as v3_utils
from vmware_nsxlib.v3 import exceptions as nsx_lib_exc
@ -93,9 +93,8 @@ NSX_P_PROVIDER_SECTION_CATEGORY = policy_constants.CATEGORY_INFRASTRUCTURE
@resource_extend.has_resource_extenders
class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
addr_pair_db.AllowedAddressPairsMixin,
nsx_plugin_common.NsxPluginBase,
nsx_plugin_common.NsxPluginV3Base,
extend_sg_rule.ExtendedSecurityGroupRuleMixin,
extend_sg.ExtendedSecurityGroupPropertiesMixin,
securitygroups_db.SecurityGroupDbMixin,
external_net_db.External_net_db_mixin,
extraroute_db.ExtraRoute_db_mixin,
@ -253,7 +252,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
webob.exc.HTTPBadRequest,
})
def _create_network_at_the_backend(self, context, net_data):
def _create_network_on_backend(self, context, net_data):
# TODO(annak): provider network
net_data['id'] = net_data.get('id') or uuidutils.generate_uuid()
@ -306,7 +305,7 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# Create the backend NSX network
if not is_external_net:
try:
self._create_network_at_the_backend(context, created_net)
self._create_network_on_backend(context, created_net)
except Exception as e:
LOG.exception("Failed to create NSX network network: %s", e)
with excutils.save_and_reraise_exception():
@ -402,32 +401,47 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
return updated_subnet
def _build_address_bindings(self, port):
def _build_port_address_bindings(self, context, port_data):
psec_on, has_ip = self._determine_port_security_and_has_ip(context,
port_data)
if not psec_on:
return None
address_bindings = []
for fixed_ip in port['fixed_ips']:
for fixed_ip in port_data['fixed_ips']:
if netaddr.IPNetwork(fixed_ip['ip_address']).version != 4:
#TODO(annak): enable when IPv6 is supported
continue
binding = self.nsxpolicy.segment_port.build_address_binding(
fixed_ip['ip_address'], port['mac_address'])
fixed_ip['ip_address'], port_data['mac_address'])
address_bindings.append(binding)
for pair in port.get(addr_apidef.ADDRESS_PAIRS):
for pair in port_data.get(addr_apidef.ADDRESS_PAIRS):
binding = self.nsxpolicy.segment_port.build_address_binding(
pair['ip_address'], pair['mac_address'])
address_bindings.append(binding)
return address_bindings
def _create_port_at_the_backend(self, context, port_data):
def _build_port_tags(self, port_data):
sec_groups = port_data.get(ext_sg.SECURITYGROUPS, [])
sec_groups += port_data.get(provider_sg.PROVIDER_SECURITYGROUPS, [])
tags = []
for sg in sec_groups:
tags = nsxlib_utils.add_v3_tag(tags,
NSX_P_SECURITY_GROUP_TAG,
sg)
return tags
def _create_port_on_backend(self, context, port_data):
# TODO(annak): admin_state not supported by policy
# TODO(annak): handle exclude list
# TODO(annak): switching profiles when supported
name = self._build_port_name(context, port_data)
psec, has_ip = self._determine_port_security_and_has_ip(context,
port_data)
address_bindings = (self._build_address_bindings(port_data)
if psec else None)
address_bindings = self._build_port_address_bindings(
context, port_data)
device_owner = port_data.get('device_owner')
vif_id = None
if device_owner and device_owner != l3_db.DEVICE_OWNER_ROUTER_INTF:
@ -435,6 +449,10 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
tags = self.nsxpolicy.build_v3_api_version_project_tag(
context.tenant_name)
tags = self._build_port_tags(port_data)
tags.append(self.nsxpolicy.build_v3_api_version_project_tag(
context.tenant_name))
self.nsxpolicy.segment_port.create_or_overwrite(
name,
port_data['network_id'],
@ -471,11 +489,18 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
neutron_db = self.base_create_port(context, port)
port["port"].update(neutron_db)
self._create_port_address_pairs(context, port_data)
(is_psec_on, has_ip, sgids, psgids) = (
self._create_port_preprocess_security(context, port,
port_data, neutron_db,
False))
self._process_port_create_security_group(context, port_data, sgids)
self._process_port_create_provider_security_group(
context, port_data, psgids)
if not is_external_net:
try:
self._create_port_at_the_backend(context, port_data)
self._create_port_on_backend(context, port_data)
except Exception as e:
with excutils.save_and_reraise_exception():
LOG.error('Failed to create port %(id)s on NSX '
@ -512,8 +537,9 @@ class NsxPolicyPlugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
def _update_port_on_backend(self, context, lport_id,
original_port, updated_port):
#TODO(asarfaty): implement
pass
# For now port create and update are the same
# Update might evolve with more features
return self._create_port_on_backend(context, updated_port)
def update_port(self, context, id, port):
with db_api.CONTEXT_WRITER.using(context):

View File

@ -97,7 +97,6 @@ from vmware_nsx.common import managers
from vmware_nsx.common import nsx_constants
from vmware_nsx.common import utils
from vmware_nsx.db import db as nsx_db
from vmware_nsx.db import extended_security_group
from vmware_nsx.db import extended_security_group_rule as extend_sg_rule
from vmware_nsx.db import maclearning as mac_db
from vmware_nsx.db import nsx_portbindings_db as pbin_db
@ -109,7 +108,7 @@ from vmware_nsx.extensions import projectpluginmap
from vmware_nsx.extensions import providersecuritygroup as provider_sg
from vmware_nsx.extensions import securitygrouplogging as sg_logging
from vmware_nsx.plugins.common.housekeeper import housekeeper
from vmware_nsx.plugins.common import plugin as nsx_plugin_common
from vmware_nsx.plugins.common_v3 import plugin as nsx_plugin_common
from vmware_nsx.plugins.nsx import utils as tvd_utils
from vmware_nsx.plugins.nsx_v3 import availability_zones as nsx_az
from vmware_nsx.plugins.nsx_v3 import utils as v3_utils
@ -161,9 +160,8 @@ NSX_V3_OS_DFW_UUID = '00000000-def0-0000-0fed-000000000000'
# the classes into a new class to handle the order correctly.
@resource_extend.has_resource_extenders
class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
extended_security_group.ExtendedSecurityGroupPropertiesMixin,
addr_pair_db.AllowedAddressPairsMixin,
nsx_plugin_common.NsxPluginBase,
nsx_plugin_common.NsxPluginV3Base,
extend_sg_rule.ExtendedSecurityGroupRuleMixin,
securitygroups_db.SecurityGroupDbMixin,
external_net_db.External_net_db_mixin,
@ -2411,14 +2409,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
return result
def _provider_sgs_specified(self, port_data):
# checks if security groups were updated adding/modifying
# security groups, port security is set and port has ip
provider_sgs_specified = (validators.is_attr_set(
port_data.get(provider_sg.PROVIDER_SECURITYGROUPS)) and
port_data.get(provider_sg.PROVIDER_SECURITYGROUPS) != [])
return provider_sgs_specified
def _get_net_tz(self, context, net_id):
mappings = nsx_db.get_nsx_switch_ids(context.session, net_id)
if mappings:
@ -2443,37 +2433,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# Check the host-switch-mode of the TZ connected to the ports network
return self._is_ens_tz_net(context, port_data['network_id'])
def _create_port_preprocess_security(
self, context, port, port_data, neutron_db, is_ens_tz_port):
(port_security, has_ip) = self._determine_port_security_and_has_ip(
context, port_data)
port_data[psec.PORTSECURITY] = port_security
# No port security is allowed if the port belongs to an ENS TZ
if (port_security and is_ens_tz_port and
not self._ens_psec_supported()):
raise nsx_exc.NsxENSPortSecurity()
self._process_port_port_security_create(
context, port_data, neutron_db)
# allowed address pair checks
self._create_port_address_pairs(context, port_data)
if port_security and has_ip:
self._ensure_default_security_group_on_port(context, port)
(sgids, psgids) = self._get_port_security_groups_lists(
context, port)
elif (self._check_update_has_security_groups({'port': port_data}) or
self._provider_sgs_specified(port_data) or
self._get_provider_security_groups_on_port(context, port)):
LOG.error("Port has conflicting port security status and "
"security groups")
raise psec_exc.PortSecurityAndIPRequiredForSecurityGroups()
else:
sgids = psgids = []
port_data[ext_sg.SECURITYGROUPS] = (
self._get_security_groups_on_port(context, port))
return port_security, has_ip, sgids, psgids
def _assert_on_dhcp_relay_without_router(self, context, port_data,
original_port=None):
# Prevent creating/updating port with device owner prefix 'compute'