NSX|V: FWaaS-V2 driver
This patch adds a driver for FWaaS V2 support in the NSX-V plugin. It supports setting firewall rules per router interface port on the router edge firewall. In addition, the FWaaS TVD driver will now support NSX-V as well. The driver code is a combination of the NSX-V3 FWaas-V2 code, and the old NSX-V FWaaS-V1 code that is being deleted. Change-Id: Iacc7eaff0c70b68156516008cf0277c154edd76b
This commit is contained in:
parent
85c9ae8071
commit
a36a1dba74
@ -41,6 +41,25 @@ Optional: Update the nsx qos_peak_bw_multiplier in nsx.ini (default value is 2.0
|
||||
[NSX]
|
||||
qos_peak_bw_multiplier = <i.e 10.0>
|
||||
|
||||
FWaaS (V2) Driver
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Add neutron-fwaas repo as an external repository and configure following flags in ``local.conf``::
|
||||
|
||||
[[local|localrc]]
|
||||
enable_service q-fwaas-v2
|
||||
Q_SERVICE_PLUGIN_CLASSES+=,firewall_v2
|
||||
|
||||
[[post-config|$NEUTRON_CONF]]
|
||||
[fwaas]
|
||||
enabled = True
|
||||
driver = vmware_nsxv_edge_v2
|
||||
|
||||
[service_providers]
|
||||
service_provider = FIREWALL_V2:fwaas_db:neutron_fwaas.services.firewall.service_drivers.agents.agents.FirewallAgentDriver:default
|
||||
|
||||
Note - if devstack fails due to ml2_conf.ini being missing, please copy neutron/plugins/ml2/ml2_conf.ini.sample to /etc/neutron/plugins/ml2/ml2_conf.ini and stack again.
|
||||
|
||||
L2GW Driver
|
||||
~~~~~~~~~~~
|
||||
|
||||
@ -188,8 +207,8 @@ FWaaS (V2) Driver
|
||||
Add neutron-fwaas repo as an external repository and configure following flags in ``local.conf``::
|
||||
|
||||
[[local|localrc]]
|
||||
ENABLED_SERVICES+=,q-fwaas-v2
|
||||
Q_SERVICE_PLUGIN_CLASSES+=,neutron_fwaas.services.firewall.fwaas_plugin_v2.FirewallPluginV2
|
||||
enable_service q-fwaas-v2
|
||||
Q_SERVICE_PLUGIN_CLASSES+=,firewall_v2
|
||||
|
||||
[[post-config|$NEUTRON_CONF]]
|
||||
[fwaas]
|
||||
@ -199,6 +218,8 @@ Add neutron-fwaas repo as an external repository and configure following flags i
|
||||
[service_providers]
|
||||
service_provider = FIREWALL_V2:fwaas_db:neutron_fwaas.services.firewall.service_drivers.agents.agents.FirewallAgentDriver:default
|
||||
|
||||
Note - if devstack fails due to ml2_conf.ini being missing, please copy neutron/plugins/ml2/ml2_conf.ini.sample to /etc/neutron/plugins/ml2/ml2_conf.ini and stack again.
|
||||
|
||||
LBaaS v2 Driver
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
@ -297,14 +318,13 @@ Configure the service provider::
|
||||
[DEFAULT]
|
||||
api_extensions_path = $DEST/neutron-lbaas/neutron_lbaas/extensions
|
||||
|
||||
|
||||
FWaaS (V2) Driver
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Add neutron-fwaas repo as an external repository and configure following flags in ``local.conf``::
|
||||
|
||||
[[local|localrc]]
|
||||
ENABLED_SERVICES+=,q-fwaas-v2
|
||||
enable_service q-fwaas-v2
|
||||
Q_SERVICE_PLUGIN_CLASSES+=,vmware_nsxtvd_fwaasv2
|
||||
|
||||
[[post-config|$NEUTRON_CONF]]
|
||||
@ -317,6 +337,8 @@ Add neutron-fwaas repo as an external repository and configure following flags i
|
||||
[service_providers]
|
||||
service_provider = FIREWALL_V2:fwaas_db:neutron_fwaas.services.firewall.service_drivers.agents.agents.FirewallAgentDriver:default
|
||||
|
||||
Note - if devstack fails due to ml2_conf.ini being missing, please copy neutron/plugins/ml2/ml2_conf.ini.sample to /etc/neutron/plugins/ml2/ml2_conf.ini and stack again.
|
||||
|
||||
L2GW Driver
|
||||
~~~~~~~~~~~
|
||||
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
prelude: >
|
||||
The NSX-V plugin can suppport FWaaS-V2 for setting router edges firewall rules.
|
||||
features:
|
||||
- |
|
||||
The NSX-V plugin can suppport FWaaS-V2 for setting router edges firewall rules.
|
@ -36,7 +36,7 @@ neutron.core_plugins =
|
||||
vmware_dvs = vmware_nsx.plugin:NsxDvsPlugin
|
||||
vmware_nsxtvd = vmware_nsx.plugin:NsxTVDPlugin
|
||||
firewall_drivers =
|
||||
vmware_nsxv3_edge = vmware_nsx.services.fwaas.nsx_v3.edge_fwaas_driver_v1:EdgeFwaasV3DriverV1
|
||||
vmware_nsxv_edge_v2 = vmware_nsx.services.fwaas.nsx_v.edge_fwaas_driver_v2:EdgeFwaasVDriverV2
|
||||
vmware_nsxv3_edge_v2 = vmware_nsx.services.fwaas.nsx_v3.edge_fwaas_driver_v2:EdgeFwaasV3DriverV2
|
||||
vmware_nsxtvd_edge_v2 = vmware_nsx.services.fwaas.nsx_tv.edge_fwaas_driver_v2:EdgeFwaasTVDriverV2
|
||||
neutron.service_plugins =
|
||||
|
@ -144,6 +144,8 @@ from vmware_nsx.plugins.nsx_v.vshield import edge_utils
|
||||
from vmware_nsx.plugins.nsx_v.vshield import securitygroup_utils
|
||||
from vmware_nsx.plugins.nsx_v.vshield import vcns_driver
|
||||
from vmware_nsx.services.flowclassifier.nsx_v import utils as fc_utils
|
||||
from vmware_nsx.services.fwaas.common import utils as fwaas_utils
|
||||
from vmware_nsx.services.fwaas.nsx_v import fwaas_callbacks_v2
|
||||
from vmware_nsx.services.lbaas.nsx_v.implementation import healthmon_mgr
|
||||
from vmware_nsx.services.lbaas.nsx_v.implementation import l7policy_mgr
|
||||
from vmware_nsx.services.lbaas.nsx_v.implementation import l7rule_mgr
|
||||
@ -329,9 +331,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
# Make sure starting rpc listeners (for QoS and other agents)
|
||||
# will happen only once
|
||||
self.start_rpc_listeners_called = False
|
||||
|
||||
# Init the FWaaS support
|
||||
self._init_fwaas()
|
||||
self.fwaas_callbacks = None
|
||||
|
||||
# Service insertion driver register
|
||||
self._si_handler = fc_utils.NsxvServiceInsertionHandler(self)
|
||||
@ -412,6 +412,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
self.octavia_listener = octavia_listener.NSXOctaviaListener(
|
||||
**octavia_objects)
|
||||
|
||||
# Init the FWaaS support
|
||||
self._init_fwaas()
|
||||
|
||||
self.init_is_complete = True
|
||||
|
||||
def _get_octavia_objects(self):
|
||||
@ -497,8 +500,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
|
||||
def _init_fwaas(self):
|
||||
# Bind FWaaS callbacks to the driver
|
||||
#TODO(asarfaty): waiting for FWaaS v2 support
|
||||
self.fwaas_callbacks = None
|
||||
if fwaas_utils.is_fwaas_v2_plugin_enabled():
|
||||
LOG.info("NSXv FWaaS v2 plugin enabled")
|
||||
self.fwaas_callbacks = fwaas_callbacks_v2.NsxvFwaasCallbacksV2()
|
||||
|
||||
def _create_security_group_container(self):
|
||||
name = "OpenStack Security Group container"
|
||||
@ -3913,9 +3917,29 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
context, router_id, interface_info)
|
||||
|
||||
def remove_router_interface(self, context, router_id, interface_info):
|
||||
# Get the router interface port id
|
||||
if self.fwaas_callbacks:
|
||||
port_id = interface_info.get('port_id')
|
||||
if not port_id:
|
||||
subnet_id = interface_info['subnet_id']
|
||||
subnet = self._get_subnet(context, subnet_id)
|
||||
rport_qry = context.session.query(models_v2.Port)
|
||||
ports = rport_qry.filter_by(
|
||||
device_id=router_id,
|
||||
device_owner=l3_db.DEVICE_OWNER_ROUTER_INTF,
|
||||
network_id=subnet['network_id'])
|
||||
for p in ports:
|
||||
if p['fixed_ips'][0]['subnet_id'] == subnet_id:
|
||||
port_id = p['id']
|
||||
break
|
||||
|
||||
router_driver = self._find_router_driver(context, router_id)
|
||||
return router_driver.remove_router_interface(
|
||||
result = router_driver.remove_router_interface(
|
||||
context, router_id, interface_info)
|
||||
# inform the FWaaS that interface port was removed
|
||||
if self.fwaas_callbacks and port_id:
|
||||
self.fwaas_callbacks.delete_port(context, port_id)
|
||||
return result
|
||||
|
||||
def _get_floatingips_by_router(self, context, router_id):
|
||||
fip_qry = context.session.query(l3_db_models.FloatingIP)
|
||||
@ -4015,35 +4039,22 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
router_db is the neutron router structure
|
||||
router_id is the id of the actual router that will be updated on
|
||||
the NSX (in case of distributed router it can be plr or tlr)
|
||||
|
||||
This is just a wrapper of update_router_firewall
|
||||
"""
|
||||
if not router_id:
|
||||
router_id = router_db['id']
|
||||
|
||||
# Add fw rules if FWaaS is enabled
|
||||
# in case of a distributed-router:
|
||||
# router['id'] is the id of the neutron router (=tlr)
|
||||
# and router_id is the plr/tlr (the one that is being updated)
|
||||
fwaas_rules = None
|
||||
if (self.fwaas_callbacks and
|
||||
self.fwaas_callbacks.should_apply_firewall_to_router(
|
||||
context, router_db, router_id)):
|
||||
fwaas_rules = self.fwaas_callbacks.get_fwaas_rules_for_router(
|
||||
context, router_db['id'])
|
||||
self.update_router_firewall(context, router_id, router_db)
|
||||
|
||||
self.update_router_firewall(context, router_id, router_db,
|
||||
fwaas_rules=fwaas_rules)
|
||||
|
||||
def update_router_firewall(self, context, router_id, router_db,
|
||||
fwaas_rules=None):
|
||||
def update_router_firewall(self, context, router_id, router_db):
|
||||
"""Recreate all rules in the router edge firewall
|
||||
|
||||
router_db is the neutron router structure
|
||||
router_id is the id of the actual router that will be updated on
|
||||
the NSX (in case of distributed router it can be plr or tlr)
|
||||
if fwaas_rules is not none - this router is attached to a firewall
|
||||
"""
|
||||
fw_rules = []
|
||||
router_with_firewall = True if fwaas_rules is not None else False
|
||||
edge_id = self._get_edge_id_by_rtr_id(context, router_id)
|
||||
|
||||
# Add FW rule/s to open subnets firewall flows and static routes
|
||||
@ -4056,33 +4067,41 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
if self.metadata_proxy_handler:
|
||||
fw_rules += nsx_v_md_proxy.get_router_fw_rules()
|
||||
|
||||
# Add FWaaS rules
|
||||
if router_with_firewall and fwaas_rules:
|
||||
fw_rules += fwaas_rules
|
||||
# Add FWaaS rules if FWaaS is enabled
|
||||
if (self.fwaas_callbacks and
|
||||
self.fwaas_callbacks.should_apply_firewall_to_router(
|
||||
context, router_db, router_id)):
|
||||
fwaas_rules = self.fwaas_callbacks.get_fwaas_rules_for_router(
|
||||
context, router_db['id'], edge_id)
|
||||
if fwaas_rules:
|
||||
fw_rules += fwaas_rules
|
||||
|
||||
if not router_with_firewall:
|
||||
dnat_rule = self._get_dnat_fw_rule(context, router_db)
|
||||
if dnat_rule:
|
||||
fw_rules.append(dnat_rule)
|
||||
# The rules added from here forward are relevant only for interface
|
||||
# ports without fwaas firewall group
|
||||
# To allow this traffic on interfaces with firewall group, the user
|
||||
# should add specific rules.
|
||||
dnat_rule = self._get_dnat_fw_rule(context, router_db)
|
||||
if dnat_rule:
|
||||
fw_rules.append(dnat_rule)
|
||||
|
||||
# Add rule for not NAT-ed allocation pools
|
||||
alloc_pool_rule = self._get_allocation_pools_fw_rule(
|
||||
context, router_db)
|
||||
if alloc_pool_rule:
|
||||
fw_rules.append(alloc_pool_rule)
|
||||
# Add rule for not NAT-ed allocation pools
|
||||
alloc_pool_rule = self._get_allocation_pools_fw_rule(
|
||||
context, router_db)
|
||||
if alloc_pool_rule:
|
||||
fw_rules.append(alloc_pool_rule)
|
||||
|
||||
# Add no-snat rules
|
||||
nosnat_fw_rules = self._get_nosnat_subnets_fw_rules(
|
||||
context, router_db)
|
||||
fw_rules.extend(nosnat_fw_rules)
|
||||
# Add no-snat rules
|
||||
nosnat_fw_rules = self._get_nosnat_subnets_fw_rules(
|
||||
context, router_db)
|
||||
fw_rules.extend(nosnat_fw_rules)
|
||||
|
||||
vpn_plugin = directory.get_plugin(plugin_const.VPN)
|
||||
if vpn_plugin:
|
||||
vpn_driver = vpn_plugin.drivers[vpn_plugin.default_provider]
|
||||
vpn_rules = (
|
||||
vpn_driver._generate_ipsecvpn_firewall_rules(
|
||||
self.plugin_type(), context, edge_id=edge_id))
|
||||
fw_rules.extend(vpn_rules)
|
||||
vpn_plugin = directory.get_plugin(plugin_const.VPN)
|
||||
if vpn_plugin:
|
||||
vpn_driver = vpn_plugin.drivers[vpn_plugin.default_provider]
|
||||
vpn_rules = (
|
||||
vpn_driver._generate_ipsecvpn_firewall_rules(
|
||||
self.plugin_type(), context, edge_id=edge_id))
|
||||
fw_rules.extend(vpn_rules)
|
||||
|
||||
# Get the load balancer rules in case they are refreshed
|
||||
# (relevant only for older LB that are still on the router edge)
|
||||
@ -4102,11 +4121,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
|
||||
|
||||
fw = {'firewall_rule_list': fw_rules}
|
||||
try:
|
||||
# If we have a firewall we shouldn't add the default
|
||||
# allow-external rule
|
||||
allow_external = False if router_with_firewall else True
|
||||
edge_utils.update_firewall(self.nsx_v, context, router_id, fw,
|
||||
allow_external=allow_external)
|
||||
edge_utils.update_firewall(self.nsx_v, context, router_id, fw)
|
||||
except vsh_exc.ResourceNotFound:
|
||||
LOG.error("Failed to update firewall for router %s",
|
||||
router_id)
|
||||
|
@ -2577,7 +2577,8 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
|
||||
relay_target = []
|
||||
if self.fwaas_callbacks:
|
||||
relay_target = (self.fwaas_callbacks.fwaas_driver.
|
||||
translate_addresses_to_target(set(relay_servers)))
|
||||
translate_addresses_to_target(set(relay_servers),
|
||||
self.plugin_type()))
|
||||
|
||||
dhcp_services = self._get_port_relay_services()
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright 2017 VMware, Inc.
|
||||
# Copyright 2018 VMware, Inc.
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@ -48,22 +48,20 @@ class NsxFwaasCallbacksV2(firewall_l3_agent_v2.L3WithFWaaS):
|
||||
neutron_conf.agent_mode = 'nsx'
|
||||
super(NsxFwaasCallbacksV2, self).__init__(conf=neutron_conf)
|
||||
self.agent_api = DummyAgentApi()
|
||||
self._core_plugin = None
|
||||
self.core_plugin = self._get_core_plugin()
|
||||
|
||||
@property
|
||||
def plugin_type(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def core_plugin(self):
|
||||
"""Get the NSX-V3 core plugin"""
|
||||
if not self._core_plugin:
|
||||
self._core_plugin = directory.get_plugin()
|
||||
if self._core_plugin.is_tvd_plugin():
|
||||
# get the plugin that match this driver
|
||||
self._core_plugin = self._core_plugin.get_plugin_by_type(
|
||||
self.plugin_type)
|
||||
return self._core_plugin
|
||||
def _get_core_plugin(self):
|
||||
"""Get the NSX core plugin"""
|
||||
core_plugin = directory.get_plugin()
|
||||
if core_plugin.is_tvd_plugin():
|
||||
# get the plugin that match this driver
|
||||
core_plugin = core_plugin.get_plugin_by_type(
|
||||
self.plugin_type)
|
||||
return core_plugin
|
||||
|
||||
# Override functions using the agent_api that is not used by our plugin
|
||||
def _get_firewall_group_ports(self, context, firewall_group,
|
||||
@ -203,3 +201,33 @@ class NsxFwaasCallbacksV2(firewall_l3_agent_v2.L3WithFWaaS):
|
||||
port_id=port_id).first()
|
||||
if entry:
|
||||
return entry.firewall_group_id
|
||||
|
||||
def should_apply_firewall_to_router(self, context, router_id):
|
||||
"""Return True if there are FWaaS rules that are attached to an
|
||||
interface of the given router.
|
||||
"""
|
||||
if not self.fwaas_enabled:
|
||||
return False
|
||||
|
||||
ctx = context.elevated()
|
||||
router_interfaces = self.core_plugin._get_router_interfaces(
|
||||
ctx, router_id)
|
||||
for port in router_interfaces:
|
||||
fwg_id = self._get_port_firewall_group_id(ctx, port['id'])
|
||||
if fwg_id:
|
||||
# check the state of this firewall group
|
||||
fwg = self._get_fw_group_from_plugin(ctx, fwg_id)
|
||||
if fwg is not None:
|
||||
if fwg.get('status') not in (nl_constants.ERROR,
|
||||
nl_constants.PENDING_DELETE):
|
||||
# Found a router interface port with rules
|
||||
return True
|
||||
return False
|
||||
|
||||
def delete_port(self, context, port_id):
|
||||
# Mark the FW group as inactive if this is the last port
|
||||
fwg = self.get_port_fwg(context, port_id)
|
||||
if (fwg and fwg.get('status') == nl_constants.ACTIVE and
|
||||
len(fwg.get('ports', [])) <= 1):
|
||||
self.fwplugin_rpc.set_firewall_group_status(
|
||||
context, fwg['id'], nl_constants.INACTIVE)
|
||||
|
93
vmware_nsx/services/fwaas/common/fwaas_driver_base.py
Normal file
93
vmware_nsx/services/fwaas/common/fwaas_driver_base.py
Normal file
@ -0,0 +1,93 @@
|
||||
# Copyright 2017 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.
|
||||
|
||||
import abc
|
||||
|
||||
from oslo_log import helpers as log_helpers
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron_lib.exceptions import firewall_v2 as exceptions
|
||||
|
||||
try:
|
||||
from neutron_fwaas.services.firewall.service_drivers.agents.drivers \
|
||||
import fwaas_base
|
||||
except ImportError:
|
||||
# FWaaS project no found
|
||||
from vmware_nsx.services.fwaas.common import fwaas_mocks \
|
||||
as fwaas_base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EdgeFwaasDriverBaseV2(fwaas_base.FwaasDriverBase):
|
||||
"""NSX Base driver for Firewall As A Service - V2."""
|
||||
|
||||
def __init__(self, driver_name):
|
||||
super(EdgeFwaasDriverBaseV2, self).__init__()
|
||||
self.driver_name = driver_name
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def create_firewall_group(self, agent_mode, apply_list, firewall_group):
|
||||
"""Create the Firewall with a given policy. """
|
||||
self._validate_firewall_group(firewall_group)
|
||||
self._update_backend_routers(apply_list, firewall_group['id'])
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def update_firewall_group(self, agent_mode, apply_list, firewall_group):
|
||||
"""Remove previous policy and apply the new policy."""
|
||||
self._validate_firewall_group(firewall_group)
|
||||
self._update_backend_routers(apply_list, firewall_group['id'])
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def delete_firewall_group(self, agent_mode, apply_list, firewall_group):
|
||||
"""Delete firewall.
|
||||
|
||||
Removes rules created by this instance from the backend firewall
|
||||
And add the default allow rule.
|
||||
"""
|
||||
self._update_backend_routers(apply_list, firewall_group['id'])
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def apply_default_policy(self, agent_mode, apply_list, firewall_group):
|
||||
"""Apply the default policy (deny all).
|
||||
|
||||
The backend firewall always has this policy (=deny all) as default,
|
||||
so we only need to delete the current rules.
|
||||
"""
|
||||
self._update_backend_routers(apply_list, firewall_group['id'])
|
||||
|
||||
@abc.abstractmethod
|
||||
def _update_backend_routers(self, apply_list, fwg_id):
|
||||
"""Update all the affected router on the backend"""
|
||||
pass
|
||||
|
||||
def _validate_firewall_group(self, firewall_group):
|
||||
"""Validate the rules in the firewall group"""
|
||||
for rule in firewall_group['egress_rule_list']:
|
||||
if rule.get('source_ip_address'):
|
||||
# this rule cannot be used as egress rule
|
||||
LOG.error("Rule %(id)s cannot be used in an egress "
|
||||
"policy because it has a source",
|
||||
{'id': rule['id']})
|
||||
raise exceptions.FirewallInternalDriverError(
|
||||
driver=self.driver_name)
|
||||
for rule in firewall_group['ingress_rule_list']:
|
||||
if rule.get('destination_ip_address'):
|
||||
# this rule cannot be used as ingress rule
|
||||
LOG.error("Rule %(id)s cannot be used in an ingress "
|
||||
"policy because it has a destination",
|
||||
{'id': rule['id']})
|
||||
raise exceptions.FirewallInternalDriverError(
|
||||
driver=self.driver_name)
|
@ -20,6 +20,7 @@ from neutron_lib.exceptions import firewall_v2 as exceptions
|
||||
|
||||
from vmware_nsx.extensions import projectpluginmap
|
||||
from vmware_nsx.plugins.nsx import utils as tvd_utils
|
||||
from vmware_nsx.services.fwaas.nsx_v import edge_fwaas_driver_v2 as v_driver
|
||||
from vmware_nsx.services.fwaas.nsx_v3 import edge_fwaas_driver_v2 as t_driver
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -36,15 +37,13 @@ except ImportError:
|
||||
|
||||
class EdgeFwaasTVDriverV2(fwaas_base_v2.FwaasDriverBase):
|
||||
"""NSX-TV driver for Firewall As A Service - V2.
|
||||
|
||||
This driver is just a wrapper calling the relevant nsx-v3 driver
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super(EdgeFwaasTVDriverV2, self).__init__()
|
||||
self.driver_name = FWAAS_DRIVER_NAME
|
||||
|
||||
# supported drivers (Only NSX-T):
|
||||
# supported drivers:
|
||||
self.drivers = {}
|
||||
try:
|
||||
self.drivers[projectpluginmap.NsxPlugins.NSX_T] = (
|
||||
@ -53,10 +52,20 @@ class EdgeFwaasTVDriverV2(fwaas_base_v2.FwaasDriverBase):
|
||||
LOG.warning("EdgeFwaasTVDriverV2 failed to initialize the NSX-T "
|
||||
"driver")
|
||||
self.drivers[projectpluginmap.NsxPlugins.NSX_T] = None
|
||||
try:
|
||||
self.drivers[projectpluginmap.NsxPlugins.NSX_V] = (
|
||||
v_driver.EdgeFwaasVDriverV2())
|
||||
except Exception:
|
||||
LOG.warning("EdgeFwaasTVDriverV2 failed to initialize the NSX-V "
|
||||
"driver")
|
||||
self.drivers[projectpluginmap.NsxPlugins.NSX_V] = None
|
||||
|
||||
def get_T_driver(self):
|
||||
return self.drivers[projectpluginmap.NsxPlugins.NSX_T]
|
||||
|
||||
def get_V_driver(self):
|
||||
return self.drivers[projectpluginmap.NsxPlugins.NSX_V]
|
||||
|
||||
def _get_driver_for_project(self, project):
|
||||
plugin_type = tvd_utils.get_tvd_plugin_type_for_project(project)
|
||||
if not self.drivers.get(plugin_type):
|
||||
@ -87,8 +96,12 @@ class EdgeFwaasTVDriverV2(fwaas_base_v2.FwaasDriverBase):
|
||||
d = self._get_driver_for_project(firewall_group['tenant_id'])
|
||||
return d.apply_default_policy(agent_mode, apply_list, firewall_group)
|
||||
|
||||
def translate_addresses_to_target(self, cidrs, fwaas_rule_id=None):
|
||||
def translate_addresses_to_target(self, cidrs, plugin_type,
|
||||
fwaas_rule_id=None):
|
||||
# This api is called directly from the core plugin
|
||||
# Assuming nsx-T as it is the only one supported now.
|
||||
return self.get_T_driver().translate_addresses_to_target(
|
||||
cidrs, fwaas_rule_id=fwaas_rule_id)
|
||||
if not self.drivers.get(plugin_type):
|
||||
LOG.error("%s has no support for plugin %s",
|
||||
self.driver_name, plugin_type)
|
||||
else:
|
||||
return self.drivers[plugin_type].translate_addresses_to_target(
|
||||
cidrs, fwaas_rule_id=fwaas_rule_id)
|
||||
|
142
vmware_nsx/services/fwaas/nsx_v/edge_fwaas_driver_v2.py
Normal file
142
vmware_nsx/services/fwaas/nsx_v/edge_fwaas_driver_v2.py
Normal file
@ -0,0 +1,142 @@
|
||||
# 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 neutron_lib import context as n_context
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron_lib.exceptions import firewall_v2 as exceptions
|
||||
from neutron_lib.plugins import directory
|
||||
|
||||
from vmware_nsx.common import locking
|
||||
from vmware_nsx.extensions import projectpluginmap
|
||||
from vmware_nsx.plugins.nsx_v.vshield import edge_utils
|
||||
from vmware_nsx.services.fwaas.common import fwaas_driver_base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
FWAAS_DRIVER_NAME = 'Fwaas V2 NSX-V driver'
|
||||
|
||||
|
||||
class EdgeFwaasVDriverV2(fwaas_driver_base.EdgeFwaasDriverBaseV2):
|
||||
"""NSX-V driver for Firewall As A Service - V2."""
|
||||
|
||||
def __init__(self):
|
||||
super(EdgeFwaasVDriverV2, self).__init__(FWAAS_DRIVER_NAME)
|
||||
self._core_plugin = None
|
||||
|
||||
@property
|
||||
def core_plugin(self):
|
||||
"""Get the NSX-V core plugin"""
|
||||
if not self._core_plugin:
|
||||
self._core_plugin = directory.get_plugin()
|
||||
if self._core_plugin.is_tvd_plugin():
|
||||
self._core_plugin = self._core_plugin.get_plugin_by_type(
|
||||
projectpluginmap.NsxPlugins.NSX_V)
|
||||
if not self._core_plugin:
|
||||
# The NSX-V plugin was not initialized
|
||||
return
|
||||
# make sure plugin init was completed
|
||||
if not self._core_plugin.init_is_complete:
|
||||
self._core_plugin.init_complete(None, None, {})
|
||||
return self._core_plugin
|
||||
|
||||
def should_apply_firewall_to_router(self, router_data,
|
||||
raise_exception=True):
|
||||
"""Return True if the firewall rules allowed to be added the router
|
||||
|
||||
Return False in those cases:
|
||||
- router without an external gateway (rule may be added later when
|
||||
there is a gateway)
|
||||
|
||||
Raise an exception if the router is unsupported
|
||||
(and raise_exception is True):
|
||||
- shared router (not supported)
|
||||
- md proxy router (not supported)
|
||||
|
||||
"""
|
||||
if (not router_data.get('distributed') and
|
||||
router_data.get('router_type') == 'shared'):
|
||||
LOG.error("Cannot apply firewall to shared router %s",
|
||||
router_data['id'])
|
||||
if raise_exception:
|
||||
raise exceptions.FirewallInternalDriverError(
|
||||
driver=self.driver_name)
|
||||
return False
|
||||
|
||||
if router_data.get('name', '').startswith('metadata_proxy_router'):
|
||||
LOG.error("Cannot apply firewall to the metadata proxy router %s",
|
||||
router_data['id'])
|
||||
if raise_exception:
|
||||
raise exceptions.FirewallInternalDriverError(
|
||||
driver=self.driver_name)
|
||||
return False
|
||||
|
||||
if not router_data.get('external_gateway_info'):
|
||||
LOG.info("Cannot apply firewall to router %s with no gateway",
|
||||
router_data['id'])
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _update_backend_routers(self, apply_list, fwg_id):
|
||||
"""Update all the affected routers on the backend"""
|
||||
LOG.info("Updating routers firewall for firewall group %s", fwg_id)
|
||||
context = n_context.get_admin_context()
|
||||
routers = set()
|
||||
routers_mapping = {}
|
||||
# the apply_list is a list of tuples: routerInfo, port-id
|
||||
for router_info, port_id in apply_list:
|
||||
# Skip dummy entries that were added only to avoid errors
|
||||
if isinstance(router_info, str):
|
||||
continue
|
||||
# Skip unsupported routers
|
||||
if not self.should_apply_firewall_to_router(router_info.router):
|
||||
continue
|
||||
|
||||
lookup_id = None
|
||||
router_id = router_info.router_id
|
||||
if router_info.router.get('distributed'):
|
||||
# Distributed router (need to update the plr edge)
|
||||
lookup_id = self.core_plugin.edge_manager.get_plr_by_tlr_id(
|
||||
context, router_id)
|
||||
else:
|
||||
# Exclusive router
|
||||
lookup_id = router_id
|
||||
if lookup_id:
|
||||
# look for the edge id in the DB
|
||||
edge_id = edge_utils.get_router_edge_id(context, lookup_id)
|
||||
if edge_id:
|
||||
routers_mapping[router_id] = {'edge_id': edge_id,
|
||||
'lookup_id': lookup_id}
|
||||
routers.add(router_id)
|
||||
|
||||
# update each router once using the core plugin
|
||||
for router_id in routers:
|
||||
router_db = self.core_plugin._get_router(context, router_id)
|
||||
edge_id = routers_mapping[router_id]['edge_id']
|
||||
LOG.info("Updating FWaaS rules for router %s on edge %s",
|
||||
router_id, edge_id)
|
||||
router_lookup_id = routers_mapping[router_id]['lookup_id']
|
||||
try:
|
||||
with locking.LockManager.get_lock(str(edge_id)):
|
||||
self.core_plugin.update_router_firewall(
|
||||
context, router_lookup_id, router_db)
|
||||
except Exception as e:
|
||||
# catch known library exceptions and raise Fwaas generic
|
||||
# exception
|
||||
LOG.error("Failed to update firewall rules on edge "
|
||||
"%(edge_id)s for router %(rtr)s: %(e)s",
|
||||
{'e': e, 'rtr': router_id, 'edge_id': edge_id})
|
||||
raise exceptions.FirewallInternalDriverError(
|
||||
driver=self.driver_name)
|
167
vmware_nsx/services/fwaas/nsx_v/fwaas_callbacks_v2.py
Normal file
167
vmware_nsx/services/fwaas/nsx_v/fwaas_callbacks_v2.py
Normal file
@ -0,0 +1,167 @@
|
||||
# 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 vmware_nsx.db import nsxv_db
|
||||
from vmware_nsx.extensions import projectpluginmap
|
||||
from vmware_nsx.plugins.nsx_v.vshield import edge_firewall_driver
|
||||
from vmware_nsx.services.fwaas.common import fwaas_callbacks_v2 as \
|
||||
com_callbacks
|
||||
from vmware_nsx.services.fwaas.nsx_tv import edge_fwaas_driver_v2 as tv_driver
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
RULE_NAME_PREFIX = 'Fwaas-'
|
||||
|
||||
|
||||
class NsxvFwaasCallbacksV2(com_callbacks.NsxFwaasCallbacksV2):
|
||||
"""NSX-V RPC callbacks for Firewall As A Service - V2."""
|
||||
|
||||
def __init__(self):
|
||||
super(NsxvFwaasCallbacksV2, self).__init__()
|
||||
# update the fwaas driver in case of TV plugin
|
||||
self.internal_driver = None
|
||||
if self.fwaas_enabled:
|
||||
if self.fwaas_driver.driver_name == tv_driver.FWAAS_DRIVER_NAME:
|
||||
self.internal_driver = self.fwaas_driver.get_V_driver()
|
||||
else:
|
||||
self.internal_driver = self.fwaas_driver
|
||||
|
||||
@property
|
||||
def plugin_type(self):
|
||||
return projectpluginmap.NsxPlugins.NSX_V
|
||||
|
||||
def should_apply_firewall_to_router(self, context, router, router_id):
|
||||
"""Return True if the FWaaS rules should be added to this router."""
|
||||
# in case of a distributed-router:
|
||||
# router['id'] is the id of the neutron router (=tlr)
|
||||
# and router_id is the plr/tlr (the one that is being updated)
|
||||
|
||||
# First check if there are rules attached to this router
|
||||
if not super(NsxvFwaasCallbacksV2,
|
||||
self).should_apply_firewall_to_router(
|
||||
context, router['id']):
|
||||
return False
|
||||
|
||||
# get all the relevant router info
|
||||
# ("router" does not have all the fields)
|
||||
ctx_elevated = context.elevated()
|
||||
router_data = self.core_plugin.get_router(ctx_elevated, router['id'])
|
||||
if not router_data:
|
||||
LOG.error("Couldn't read router %s data", router['id'])
|
||||
return False
|
||||
|
||||
if router_data.get('distributed'):
|
||||
if router_id == router['id']:
|
||||
# Do not add firewall rules on the tlr router.
|
||||
return False
|
||||
|
||||
# Check if the FWaaS driver supports this router
|
||||
if not self.internal_driver.should_apply_firewall_to_router(
|
||||
router_data, raise_exception=False):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def get_fwaas_rules_for_router(self, context, router_id, edge_id):
|
||||
"""Return the list of (translated) FWaaS rules for this router."""
|
||||
ctx_elevated = context.elevated()
|
||||
router_interfaces = self.core_plugin._get_router_interfaces(
|
||||
ctx_elevated, router_id)
|
||||
|
||||
fw_rules = []
|
||||
# Add firewall rules per port attached to a firewall group
|
||||
for port in router_interfaces:
|
||||
fwg = self.get_port_fwg(ctx_elevated, port['id'])
|
||||
if fwg:
|
||||
# get the interface vnic
|
||||
edge_vnic_bind = nsxv_db.get_edge_vnic_binding(
|
||||
context.session, edge_id, port['network_id'])
|
||||
vnic_id = 'vnic-index-%s' % edge_vnic_bind.vnic_index
|
||||
# Add the FWaaS rules for this port
|
||||
fw_rules.extend(
|
||||
self.get_port_translated_rules(vnic_id, fwg))
|
||||
|
||||
return fw_rules
|
||||
|
||||
def get_port_translated_rules(self, vnic_id, firewall_group):
|
||||
"""Return the list of translated rules per port
|
||||
|
||||
Ingress/Egress firewall rules + default ingress/egress drop
|
||||
"""
|
||||
port_rules = []
|
||||
logged = False
|
||||
# Add the firewall group ingress/egress rules only if the fw is up
|
||||
if firewall_group['admin_state_up']:
|
||||
port_rules.extend(self.translate_rules(
|
||||
firewall_group['ingress_rule_list'],
|
||||
replace_dest=vnic_id,
|
||||
logged=logged,
|
||||
is_ingress=True))
|
||||
port_rules.extend(self.translate_rules(
|
||||
firewall_group['egress_rule_list'],
|
||||
replace_src=vnic_id,
|
||||
logged=logged,
|
||||
is_ingress=False))
|
||||
|
||||
# Add ingress/egress block rules for this port
|
||||
port_rules.extend([
|
||||
{'name': "Block port ingress",
|
||||
'action': edge_firewall_driver.FWAAS_DENY,
|
||||
'destination_vnic_groups': [vnic_id],
|
||||
'logged': logged},
|
||||
{'name': "Block port egress",
|
||||
'action': edge_firewall_driver.FWAAS_DENY,
|
||||
'source_vnic_groups': [vnic_id],
|
||||
'logged': logged}])
|
||||
|
||||
return port_rules
|
||||
|
||||
def translate_rules(self, fwaas_rules, replace_dest=None, replace_src=None,
|
||||
logged=False, is_ingress=True):
|
||||
translated_rules = []
|
||||
for rule in fwaas_rules:
|
||||
if not rule['enabled']:
|
||||
# skip disabled rules
|
||||
continue
|
||||
# Make sure the rule has a name, and it starts with the prefix
|
||||
# (backend max name length is 30)
|
||||
if rule.get('name'):
|
||||
rule['name'] = RULE_NAME_PREFIX + rule['name']
|
||||
else:
|
||||
rule['name'] = RULE_NAME_PREFIX + rule['id']
|
||||
rule['name'] = rule['name'][:30]
|
||||
if rule.get('id'):
|
||||
# update rules ID to prevent DB duplications in
|
||||
# NsxvEdgeFirewallRuleBinding
|
||||
if is_ingress:
|
||||
rule['id'] = ('ingress-%s' % rule['id'])[:36]
|
||||
else:
|
||||
rule['id'] = ('egress-%s' % rule['id'])[:36]
|
||||
# source & destination should be lists
|
||||
if replace_dest:
|
||||
rule['destination_vnic_groups'] = [replace_dest]
|
||||
elif rule.get('destination_ip_address'):
|
||||
rule['destination_ip_address'] = [
|
||||
rule['destination_ip_address']]
|
||||
if replace_src:
|
||||
rule['source_vnic_groups'] = [replace_src]
|
||||
elif rule.get('source_ip_address'):
|
||||
rule['source_ip_address'] = [rule['source_ip_address']]
|
||||
if logged:
|
||||
rule['logged'] = True
|
||||
translated_rules.append(rule)
|
||||
|
||||
return translated_rules
|
@ -23,27 +23,21 @@ from neutron_lib.plugins import directory
|
||||
from oslo_log import log as logging
|
||||
|
||||
from vmware_nsx.extensions import projectpluginmap
|
||||
from vmware_nsx.services.fwaas.common import fwaas_driver_base
|
||||
from vmware_nsxlib.v3 import nsx_constants as consts
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
RULE_NAME_PREFIX = 'Fwaas-'
|
||||
DEFAULT_RULE_NAME = 'Default LR Layer3 Rule'
|
||||
|
||||
try:
|
||||
from neutron_fwaas.services.firewall.service_drivers.agents.drivers \
|
||||
import fwaas_base
|
||||
except ImportError:
|
||||
# FWaaS project no found
|
||||
from vmware_nsx.services.fwaas.common import fwaas_mocks \
|
||||
as fwaas_base
|
||||
|
||||
|
||||
class CommonEdgeFwaasV3Driver(fwaas_base.FwaasDriverBase):
|
||||
#TODO(asarfaty): this base class now serves only 1 driver and can be merged
|
||||
# with it
|
||||
class CommonEdgeFwaasV3Driver(fwaas_driver_base.EdgeFwaasDriverBaseV2):
|
||||
"""Base class for NSX-V3 driver for Firewall As A Service - V1 & V2."""
|
||||
|
||||
def __init__(self, driver_exception, driver_name):
|
||||
super(CommonEdgeFwaasV3Driver, self).__init__()
|
||||
self.driver_name = driver_name
|
||||
super(CommonEdgeFwaasV3Driver, self).__init__(driver_name)
|
||||
self.backend_support = True
|
||||
self.driver_exception = driver_exception
|
||||
registry.subscribe(
|
||||
@ -139,7 +133,8 @@ class CommonEdgeFwaasV3Driver(fwaas_base.FwaasDriverBase):
|
||||
cidr,
|
||||
consts.IPV6 if net.version == 6 else consts.IPV4)
|
||||
|
||||
def translate_addresses_to_target(self, cidrs, fwaas_rule_id=None):
|
||||
def translate_addresses_to_target(self, cidrs, plugin_type,
|
||||
fwaas_rule_id=None):
|
||||
translated_cidrs = []
|
||||
for ip in cidrs:
|
||||
res = self._translate_cidr(ip, fwaas_rule_id)
|
||||
|
@ -14,7 +14,6 @@
|
||||
# under the License.
|
||||
|
||||
from neutron_lib import context as n_context
|
||||
from oslo_log import helpers as log_helpers
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron_lib.exceptions import firewall_v2 as exceptions
|
||||
@ -35,36 +34,6 @@ class EdgeFwaasV3DriverV2(base_driver.CommonEdgeFwaasV3Driver):
|
||||
super(EdgeFwaasV3DriverV2, self).__init__(exception_cls,
|
||||
FWAAS_DRIVER_NAME)
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def create_firewall_group(self, agent_mode, apply_list, firewall_group):
|
||||
"""Create the Firewall with a given policy. """
|
||||
self._validate_firewall_group(firewall_group)
|
||||
self._update_backend_routers(apply_list, firewall_group['id'])
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def update_firewall_group(self, agent_mode, apply_list, firewall_group):
|
||||
"""Remove previous policy and apply the new policy."""
|
||||
self._validate_firewall_group(firewall_group)
|
||||
self._update_backend_routers(apply_list, firewall_group['id'])
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def delete_firewall_group(self, agent_mode, apply_list, firewall_group):
|
||||
"""Delete firewall.
|
||||
|
||||
Removes rules created by this instance from the backend firewall
|
||||
And add the default allow rule.
|
||||
"""
|
||||
self._update_backend_routers(apply_list, firewall_group['id'])
|
||||
|
||||
@log_helpers.log_method_call
|
||||
def apply_default_policy(self, agent_mode, apply_list, firewall_group):
|
||||
"""Apply the default policy (deny all).
|
||||
|
||||
The backend firewall always has this policy (=deny all) as default,
|
||||
so we only need to delete the current rules.
|
||||
"""
|
||||
self._update_backend_routers(apply_list, firewall_group['id'])
|
||||
|
||||
def _update_backend_routers(self, apply_list, fwg_id):
|
||||
"""Update all the affected router on the backend"""
|
||||
self.validate_backend_version()
|
||||
@ -121,22 +90,3 @@ class EdgeFwaasV3DriverV2(base_driver.CommonEdgeFwaasV3Driver):
|
||||
'direction': 'OUT'}])
|
||||
|
||||
return port_rules
|
||||
|
||||
def _validate_firewall_group(self, firewall_group):
|
||||
"""Validate the rules in the firewall group"""
|
||||
for rule in firewall_group['egress_rule_list']:
|
||||
if rule.get('source_ip_address'):
|
||||
# this rule cannot be used as egress rule
|
||||
LOG.error("Rule %(id)s cannot be used in an egress "
|
||||
"policy because it has a source",
|
||||
{'id': rule['id']})
|
||||
raise exceptions.FirewallInternalDriverError(
|
||||
driver=FWAAS_DRIVER_NAME)
|
||||
for rule in firewall_group['ingress_rule_list']:
|
||||
if rule.get('destination_ip_address'):
|
||||
# this rule cannot be used as ingress rule
|
||||
LOG.error("Rule %(id)s cannot be used in an ingress "
|
||||
"policy because it has a destination",
|
||||
{'id': rule['id']})
|
||||
raise exceptions.FirewallInternalDriverError(
|
||||
driver=FWAAS_DRIVER_NAME)
|
||||
|
288
vmware_nsx/tests/unit/nsx_v/test_fwaas_v2_driver.py
Normal file
288
vmware_nsx/tests/unit/nsx_v/test_fwaas_v2_driver.py
Normal file
@ -0,0 +1,288 @@
|
||||
# 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.
|
||||
|
||||
import copy
|
||||
|
||||
import mock
|
||||
|
||||
from neutron_lib.exceptions import firewall_v2 as exceptions
|
||||
from neutron_lib.plugins import directory
|
||||
|
||||
from vmware_nsx.db import nsxv_models
|
||||
from vmware_nsx.plugins.nsx_v.vshield import edge_firewall_driver
|
||||
from vmware_nsx.plugins.nsx_v.vshield import edge_utils
|
||||
from vmware_nsx.services.fwaas.nsx_v import edge_fwaas_driver_v2
|
||||
from vmware_nsx.services.fwaas.nsx_v import fwaas_callbacks_v2
|
||||
from vmware_nsx.tests.unit.nsx_v import test_plugin as test_v_plugin
|
||||
|
||||
FAKE_FW_ID = 'fake_fw_uuid'
|
||||
FAKE_ROUTER_ID = 'fake_rtr_uuid'
|
||||
FAKE_PORT_ID = 'fake_port_uuid'
|
||||
FAKE_NET_ID = 'fake_net_uuid'
|
||||
FAKE_DB_OBJ = nsxv_models.NsxvEdgeVnicBinding(vnic_index='1')
|
||||
|
||||
|
||||
class NsxvFwaasTestCase(test_v_plugin.NsxVPluginV2TestCase):
|
||||
def setUp(self):
|
||||
super(NsxvFwaasTestCase, self).setUp()
|
||||
self.firewall = edge_fwaas_driver_v2.EdgeFwaasVDriverV2()
|
||||
|
||||
self.plugin = directory.get_plugin()
|
||||
self.plugin.fwaas_callbacks = fwaas_callbacks_v2.\
|
||||
NsxvFwaasCallbacksV2()
|
||||
self.plugin.fwaas_callbacks.fwaas_enabled = True
|
||||
self.plugin.fwaas_callbacks.fwaas_driver = self.firewall
|
||||
self.plugin.fwaas_callbacks.internal_driver = self.firewall
|
||||
self.plugin.init_is_complete = True
|
||||
self.plugin.metadata_proxy_handler = None
|
||||
|
||||
# Start some mocks
|
||||
self.router = {'id': FAKE_ROUTER_ID,
|
||||
'external_gateway_info': {'network_id': 'external'}}
|
||||
mock.patch.object(self.plugin, '_get_router',
|
||||
return_value=self.router).start()
|
||||
mock.patch.object(self.plugin, 'get_router',
|
||||
return_value=self.router).start()
|
||||
self.port = {'id': FAKE_PORT_ID, 'network_id': FAKE_NET_ID}
|
||||
mock.patch.object(self.plugin, '_get_router_interfaces',
|
||||
return_value=[self.port]).start()
|
||||
mock.patch.object(self.plugin, 'get_port',
|
||||
return_value=self.port).start()
|
||||
mock.patch.object(self.plugin, '_get_subnet_fw_rules',
|
||||
return_value=[]).start()
|
||||
mock.patch.object(self.plugin, '_get_dnat_fw_rule',
|
||||
return_value=[]).start()
|
||||
mock.patch.object(self.plugin, '_get_allocation_pools_fw_rule',
|
||||
return_value=[]).start()
|
||||
mock.patch.object(self.plugin, '_get_nosnat_subnets_fw_rules',
|
||||
return_value=[]).start()
|
||||
|
||||
def _fake_rules_v4(self, is_ingress=True, cidr='10.24.4.0/24'):
|
||||
rule1 = {'enabled': True,
|
||||
'action': 'allow',
|
||||
'ip_version': 4,
|
||||
'protocol': 'tcp',
|
||||
'destination_port': '80',
|
||||
'id': 'fake-fw-rule1',
|
||||
'description': 'first rule',
|
||||
'position': '0'}
|
||||
rule2 = {'enabled': True,
|
||||
'action': 'reject',
|
||||
'ip_version': 4,
|
||||
'protocol': 'tcp',
|
||||
'destination_port': '22:24',
|
||||
'source_port': '1:65535',
|
||||
'id': 'fake-fw-rule2',
|
||||
'position': '1'}
|
||||
rule3 = {'enabled': True,
|
||||
'action': 'deny',
|
||||
'ip_version': 4,
|
||||
'protocol': 'icmp',
|
||||
'id': 'fake-fw-rule3',
|
||||
'position': '2'}
|
||||
rule4 = {'enabled': True,
|
||||
'action': 'deny',
|
||||
'ip_version': 4,
|
||||
'id': 'fake-fw-rule4',
|
||||
'position': '3'}
|
||||
if is_ingress:
|
||||
# source ips are allowed
|
||||
rule1['source_ip_address'] = cidr
|
||||
else:
|
||||
# dest ips are allowed for egress rules
|
||||
rule1['destination_ip_address'] = cidr
|
||||
|
||||
return [rule1, rule2, rule3, rule4]
|
||||
|
||||
def _fake_translated_rules(self, rules_list,
|
||||
nsx_port_id,
|
||||
is_ingress=True,
|
||||
logged=False):
|
||||
translated_rules = copy.copy(rules_list)
|
||||
for rule in translated_rules:
|
||||
if logged:
|
||||
rule['logged'] = True
|
||||
if is_ingress:
|
||||
rule['destination_vnic_groups'] = ['vnic-index-1']
|
||||
else:
|
||||
rule['source_vnic_groups'] = ['vnic-index-1']
|
||||
if rule.get('destination_ip_address'):
|
||||
rule['destination_ip_address'] = [
|
||||
rule['destination_ip_address']]
|
||||
if rule.get('source_ip_address'):
|
||||
rule['source_ip_address'] = [
|
||||
rule['source_ip_address']]
|
||||
rule['name'] = (fwaas_callbacks_v2.RULE_NAME_PREFIX +
|
||||
(rule.get('name') or rule['id']))[:30]
|
||||
if rule.get('id'):
|
||||
if is_ingress:
|
||||
rule['id'] = ('ingress-%s' % rule['id'])[:36]
|
||||
else:
|
||||
rule['id'] = ('egress-%s' % rule['id'])[:36]
|
||||
|
||||
return translated_rules
|
||||
|
||||
def _fake_empty_firewall_group(self):
|
||||
fw_inst = {'id': FAKE_FW_ID,
|
||||
'admin_state_up': True,
|
||||
'tenant_id': 'tenant-uuid',
|
||||
'ingress_rule_list': [],
|
||||
'egress_rule_list': []}
|
||||
return fw_inst
|
||||
|
||||
def _fake_firewall_group(self, rule_list, is_ingress=True,
|
||||
admin_state_up=True):
|
||||
_rule_list = copy.deepcopy(rule_list)
|
||||
for rule in _rule_list:
|
||||
rule['position'] = str(_rule_list.index(rule))
|
||||
fw_inst = {'id': FAKE_FW_ID,
|
||||
'admin_state_up': admin_state_up,
|
||||
'tenant_id': 'tenant-uuid',
|
||||
'ingress_rule_list': [],
|
||||
'egress_rule_list': []}
|
||||
if is_ingress:
|
||||
fw_inst['ingress_rule_list'] = _rule_list
|
||||
else:
|
||||
fw_inst['egress_rule_list'] = _rule_list
|
||||
return fw_inst
|
||||
|
||||
def _fake_firewall_group_with_admin_down(self, rule_list,
|
||||
is_ingress=True):
|
||||
return self._fake_firewall_group(
|
||||
rule_list, is_ingress=is_ingress, admin_state_up=False)
|
||||
|
||||
def _fake_apply_list(self):
|
||||
router_inst = self.router
|
||||
router_info_inst = mock.Mock()
|
||||
router_info_inst.router = router_inst
|
||||
router_info_inst.router_id = FAKE_ROUTER_ID
|
||||
apply_list = [(router_info_inst, FAKE_PORT_ID)]
|
||||
return apply_list
|
||||
|
||||
def test_create_firewall_no_rules(self):
|
||||
apply_list = self._fake_apply_list()
|
||||
firewall = self._fake_empty_firewall_group()
|
||||
with mock.patch.object(self.plugin.fwaas_callbacks, 'get_port_fwg',
|
||||
return_value=firewall),\
|
||||
mock.patch.object(self.plugin.fwaas_callbacks,
|
||||
'_get_port_firewall_group_id',
|
||||
return_value=FAKE_FW_ID),\
|
||||
mock.patch.object(self.plugin.fwaas_callbacks,
|
||||
'_get_fw_group_from_plugin',
|
||||
return_value=firewall),\
|
||||
mock.patch("vmware_nsx.db.nsxv_db.get_edge_vnic_binding",
|
||||
return_value=FAKE_DB_OBJ),\
|
||||
mock.patch.object(edge_utils, "update_firewall") as update_fw,\
|
||||
mock.patch.object(edge_utils, 'get_router_edge_id',
|
||||
return_value='edge-1'):
|
||||
self.firewall.create_firewall_group('nsx', apply_list, firewall)
|
||||
# expecting 2 block rules for the logical port (egress & ingress)
|
||||
# and last default allow all rule
|
||||
expected_rules = [
|
||||
{'name': "Block port ingress",
|
||||
'action': edge_firewall_driver.FWAAS_DENY,
|
||||
'destination_vnic_groups': ['vnic-index-1'],
|
||||
'logged': False},
|
||||
{'name': "Block port egress",
|
||||
'action': edge_firewall_driver.FWAAS_DENY,
|
||||
'source_vnic_groups': ['vnic-index-1'],
|
||||
'logged': False}]
|
||||
update_fw.assert_called_once_with(
|
||||
self.plugin.nsx_v, mock.ANY, FAKE_ROUTER_ID,
|
||||
{'firewall_rule_list': expected_rules})
|
||||
|
||||
def _setup_firewall_with_rules(self, func, is_ingress=True):
|
||||
apply_list = self._fake_apply_list()
|
||||
rule_list = self._fake_rules_v4(is_ingress=is_ingress)
|
||||
firewall = self._fake_firewall_group(rule_list, is_ingress=is_ingress)
|
||||
with mock.patch.object(self.plugin.fwaas_callbacks, 'get_port_fwg',
|
||||
return_value=firewall),\
|
||||
mock.patch.object(self.plugin.fwaas_callbacks,
|
||||
'_get_port_firewall_group_id',
|
||||
return_value=FAKE_FW_ID),\
|
||||
mock.patch.object(self.plugin.fwaas_callbacks,
|
||||
'_get_fw_group_from_plugin',
|
||||
return_value=firewall),\
|
||||
mock.patch("vmware_nsx.db.nsxv_db.get_edge_vnic_binding",
|
||||
return_value=FAKE_DB_OBJ),\
|
||||
mock.patch.object(edge_utils, "update_firewall") as update_fw,\
|
||||
mock.patch.object(edge_utils, 'get_router_edge_id',
|
||||
return_value='edge-1'):
|
||||
func('nsx', apply_list, firewall)
|
||||
expected_rules = self._fake_translated_rules(
|
||||
rule_list,
|
||||
'vnic-index-1', is_ingress=is_ingress) + [
|
||||
{'name': "Block port ingress",
|
||||
'action': edge_firewall_driver.FWAAS_DENY,
|
||||
'destination_vnic_groups': ['vnic-index-1'],
|
||||
'logged': False},
|
||||
{'name': "Block port egress",
|
||||
'action': edge_firewall_driver.FWAAS_DENY,
|
||||
'source_vnic_groups': ['vnic-index-1'],
|
||||
'logged': False}]
|
||||
|
||||
update_fw.assert_called_once_with(
|
||||
self.plugin.nsx_v, mock.ANY, FAKE_ROUTER_ID,
|
||||
{'firewall_rule_list': expected_rules})
|
||||
|
||||
def test_create_firewall_with_ingress_rules(self):
|
||||
self._setup_firewall_with_rules(self.firewall.create_firewall_group)
|
||||
|
||||
def test_update_firewall_with_ingress_rules(self):
|
||||
self._setup_firewall_with_rules(self.firewall.update_firewall_group)
|
||||
|
||||
def test_create_firewall_with_egress_rules(self):
|
||||
self._setup_firewall_with_rules(self.firewall.create_firewall_group,
|
||||
is_ingress=False)
|
||||
|
||||
def test_update_firewall_with_egress_rules(self):
|
||||
self._setup_firewall_with_rules(self.firewall.update_firewall_group,
|
||||
is_ingress=False)
|
||||
|
||||
def test_create_firewall_with_illegal_rules(self):
|
||||
"""Use ingress rules as the egress list and verify failure"""
|
||||
apply_list = self._fake_apply_list()
|
||||
rule_list = self._fake_rules_v4(is_ingress=True)
|
||||
firewall = self._fake_firewall_group(rule_list, is_ingress=False)
|
||||
self.assertRaises(exceptions.FirewallInternalDriverError,
|
||||
self.firewall.create_firewall_group, 'nsx',
|
||||
apply_list, firewall)
|
||||
|
||||
def test_delete_firewall(self):
|
||||
apply_list = self._fake_apply_list()
|
||||
firewall = self._fake_empty_firewall_group()
|
||||
with mock.patch.object(self.plugin.fwaas_callbacks, 'get_port_fwg',
|
||||
return_value=None),\
|
||||
mock.patch("vmware_nsx.db.db.get_nsx_switch_and_port_id",
|
||||
return_value=('vnic-index-1', 0)),\
|
||||
mock.patch.object(edge_utils, "update_firewall") as update_fw,\
|
||||
mock.patch.object(edge_utils, 'get_router_edge_id',
|
||||
return_value='edge-1'):
|
||||
self.firewall.delete_firewall_group('nsx', apply_list, firewall)
|
||||
update_fw.assert_called_once_with(
|
||||
self.plugin.nsx_v, mock.ANY, FAKE_ROUTER_ID,
|
||||
{'firewall_rule_list': []})
|
||||
|
||||
def test_create_firewall_with_admin_down(self):
|
||||
apply_list = self._fake_apply_list()
|
||||
rule_list = self._fake_rules_v4()
|
||||
firewall = self._fake_firewall_group_with_admin_down(rule_list)
|
||||
with mock.patch.object(edge_utils, "update_firewall") as update_fw,\
|
||||
mock.patch.object(edge_utils, 'get_router_edge_id',
|
||||
return_value='edge-1'):
|
||||
self.firewall.create_firewall_group('nsx', apply_list, firewall)
|
||||
update_fw.assert_called_once_with(
|
||||
self.plugin.nsx_v, mock.ANY, FAKE_ROUTER_ID,
|
||||
{'firewall_rule_list': []})
|
Loading…
Reference in New Issue
Block a user