NSX-V FWaaS(V1) support

The nsx-v FWaaS driver will add the configured firewall rules to
the router edges.
Currently there is not support for shared routers.
The rules will be edded after the current rules (NAT, LBaaS, external traffic)
for exclusive routers edges and distributed routers PLR edged.

Change-Id: I82ba90070ef4e739a0b5c4463ef03a807e26adfb
This commit is contained in:
Adit Sarfaty 2017-03-21 11:15:32 +02:00
parent 7cdd70e681
commit f10dcfe82d
13 changed files with 568 additions and 74 deletions

View File

@ -89,6 +89,24 @@ lines to the policy.json file::
"delete_flow_classifier": "rule:admin_only", "delete_flow_classifier": "rule:admin_only",
"get_flow_classifier": "rule:admin_only" "get_flow_classifier": "rule:admin_only"
FWAAS (V1) Driver:
~~~~~~~~~~~~~
Add neutron-fwaas repo as an external repository and configure following flags in ``local.conf``::
[[local|localrc]]
enable_plugin neutron-fwaas https://git.openstack.org/openstack/neutron-fwaas
ENABLED_SERVICES+=,q-fwaas
[[post-config|$NEUTRON_CONF]]
[DEFAULT]
service_plugins = neutron_fwaas.services.firewall.fwaas_plugin.FirewallPlugin
[fwaas]
enabled = True
driver = vmware_nsx.services.fwaas.nsx_v.edge_fwaas_driver.EdgeFwaasDriver
NSXv3 NSXv3
----- -----

View File

@ -0,0 +1,6 @@
---
prelude: >
The NSX-V plugin can suppport FWaaS-V1 for setting router edges firewall rules.
features:
- |
The NSX-V plugin can suppport FWaaS-V1 for setting router edges firewall rules.

View File

@ -8,6 +8,7 @@ ${DIR}/tox_install_project.sh networking-l2gw networking_l2gw $*
${DIR}/tox_install_project.sh networking-sfc networking_sfc $* ${DIR}/tox_install_project.sh networking-sfc networking_sfc $*
${DIR}/tox_install_project.sh neutron-lbaas neutron_lbaas $* ${DIR}/tox_install_project.sh neutron-lbaas neutron_lbaas $*
${DIR}/tox_install_project.sh vmware-nsxlib vmware_nsxlib $* ${DIR}/tox_install_project.sh vmware-nsxlib vmware_nsxlib $*
${DIR}/tox_install_project.sh neutron-fwaas neutron_fwaas $*
CONSTRAINTS_FILE=$1 CONSTRAINTS_FILE=$1
shift shift

View File

@ -317,7 +317,7 @@ class RouterSharedDriver(router_driver.RouterBaseDriver):
fake_fw_rules += ( fake_fw_rules += (
nsx_v_md_proxy.get_router_fw_rules()) nsx_v_md_proxy.get_router_fw_rules())
# TODO(berlin): Add fw rules if fw service is supported # TODO(asarfaty): Add fwaas rules when fwaas supports shared routers
fake_fw = {'firewall_rule_list': fake_fw_rules} fake_fw = {'firewall_rule_list': fake_fw_rules}
edge_utils.update_firewall(self.nsx_v, context, target_router_id, edge_utils.update_firewall(self.nsx_v, context, target_router_id,
fake_fw, allow_external=allow_external) fake_fw, allow_external=allow_external)

View File

@ -127,6 +127,7 @@ 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 securitygroup_utils
from vmware_nsx.plugins.nsx_v.vshield import vcns_driver 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.flowclassifier.nsx_v import utils as fc_utils
from vmware_nsx.services.fwaas.nsx_v import fwaas_callbacks
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
PORTGROUP_PREFIX = 'dvportgroup' PORTGROUP_PREFIX = 'dvportgroup'
@ -272,6 +273,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
# will happen only once # will happen only once
self.start_rpc_listeners_called = False self.start_rpc_listeners_called = False
# Init the FWaaS support
self._init_fwaas()
# Service insertion driver register # Service insertion driver register
self._si_handler = fc_utils.NsxvServiceInsertionHandler(self) self._si_handler = fc_utils.NsxvServiceInsertionHandler(self)
registry.subscribe(self.add_vms_to_service_insertion, registry.subscribe(self.add_vms_to_service_insertion,
@ -377,6 +381,10 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
self.start_rpc_listeners_called = True self.start_rpc_listeners_called = True
return self.conn.consume_in_threads() return self.conn.consume_in_threads()
def _init_fwaas(self):
# Bind FWaaS callbacks to the driver
self.fwaas_callbacks = fwaas_callbacks.NsxvFwaasCallbacks()
def _ext_extend_network_dict(self, result, netdb): def _ext_extend_network_dict(self, result, netdb):
ctx = n_context.get_admin_context() ctx = n_context.get_admin_context()
with db_api.context_manager.writer.using(ctx): with db_api.context_manager.writer.using(ctx):
@ -3459,7 +3467,7 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
def _update_subnets_and_dnat_firewall(self, context, router, def _update_subnets_and_dnat_firewall(self, context, router,
router_id=None, allow_external=True): router_id=None, allow_external=True):
fake_fw_rules = [] fw_rules = []
if not router_id: if not router_id:
router_id = router['id'] router_id = router['id']
subnet_cidrs = self._find_router_subnets_cidrs(context, router['id']) subnet_cidrs = self._find_router_subnets_cidrs(context, router['id'])
@ -3473,12 +3481,12 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
'enabled': True, 'enabled': True,
'source_ip_address': subnet_cidrs, 'source_ip_address': subnet_cidrs,
'destination_ip_address': subnet_cidrs} 'destination_ip_address': subnet_cidrs}
fake_fw_rules.append(fake_subnet_fw_rule) fw_rules.append(fake_subnet_fw_rule)
_, dnat_rules = self._get_nat_rules(context, router) _, dnat_rules = self._get_nat_rules(context, router)
# If metadata service is enabled, block access to inter-edge network # If metadata service is enabled, block access to inter-edge network
if self.metadata_proxy_handler: if self.metadata_proxy_handler:
fake_fw_rules += nsx_v_md_proxy.get_router_fw_rules() fw_rules += nsx_v_md_proxy.get_router_fw_rules()
dnat_cidrs = [rule['dst'] for rule in dnat_rules] dnat_cidrs = [rule['dst'] for rule in dnat_rules]
if dnat_cidrs: if dnat_cidrs:
@ -3487,10 +3495,10 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
'action': 'allow', 'action': 'allow',
'enabled': True, 'enabled': True,
'destination_ip_address': dnat_cidrs} 'destination_ip_address': dnat_cidrs}
fake_fw_rules.append(fake_dnat_fw_rule) fw_rules.append(fake_dnat_fw_rule)
nosnat_fw_rules = self._get_nosnat_subnets_fw_rules( nosnat_fw_rules = self._get_nosnat_subnets_fw_rules(
context, router) context, router)
fake_fw_rules.extend(nosnat_fw_rules) fw_rules.extend(nosnat_fw_rules)
# Get the load balancer rules in case they are refreshed # Get the load balancer rules in case they are refreshed
edge_id = self._get_edge_id_by_rtr_id(context, router_id) edge_id = self._get_edge_id_by_rtr_id(context, router_id)
@ -3506,10 +3514,13 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin,
'name': vsm_rule['name'], 'name': vsm_rule['name'],
'ruleId': vsm_rule['ruleId'] 'ruleId': vsm_rule['ruleId']
} }
fake_fw_rules.append(lb_fw_rule) fw_rules.append(lb_fw_rule)
# TODO(berlin): Add fw rules if fw service is supported # Add fw rules if FWaaS is enabled
fake_fw = {'firewall_rule_list': fake_fw_rules} fw_rules.extend(self.fwaas_callbacks.get_fwaas_rules_for_router(
context, router, router_id))
fake_fw = {'firewall_rule_list': fw_rules}
try: try:
edge_utils.update_firewall(self.nsx_v, context, router_id, fake_fw, edge_utils.update_firewall(self.nsx_v, context, router_id, fake_fw,
allow_external=allow_external) allow_external=allow_external)

View File

@ -25,9 +25,11 @@ LOG = logging.getLogger(__name__)
VSE_FWAAS_ALLOW = "accept" VSE_FWAAS_ALLOW = "accept"
VSE_FWAAS_DENY = "deny" VSE_FWAAS_DENY = "deny"
VSE_FWAAS_REJECT = "reject"
FWAAS_ALLOW = "allow" FWAAS_ALLOW = "allow"
FWAAS_DENY = "deny" FWAAS_DENY = "deny"
FWAAS_REJECT = "reject"
class EdgeFirewallDriver(object): class EdgeFirewallDriver(object):
@ -43,6 +45,8 @@ class EdgeFirewallDriver(object):
return VSE_FWAAS_ALLOW return VSE_FWAAS_ALLOW
elif action == FWAAS_DENY: elif action == FWAAS_DENY:
return VSE_FWAAS_DENY return VSE_FWAAS_DENY
elif action == FWAAS_REJECT:
return VSE_FWAAS_REJECT
else: else:
msg = _("Invalid action value %s in a firewall rule") % action msg = _("Invalid action value %s in a firewall rule") % action
raise vcns_exc.VcnsBadRequest(resource='firewall_rule', msg=msg) raise vcns_exc.VcnsBadRequest(resource='firewall_rule', msg=msg)
@ -52,12 +56,14 @@ class EdgeFirewallDriver(object):
return FWAAS_ALLOW return FWAAS_ALLOW
elif action == VSE_FWAAS_DENY: elif action == VSE_FWAAS_DENY:
return FWAAS_DENY return FWAAS_DENY
elif action == VSE_FWAAS_REJECT:
return FWAAS_REJECT
else: else:
msg = (_("Invalid action value %s in " msg = (_("Invalid action value %s in "
"a vshield firewall rule") % action) "a vshield firewall rule") % action)
raise vcns_exc.VcnsBadRequest(resource='firewall_rule', msg=msg) raise vcns_exc.VcnsBadRequest(resource='firewall_rule', msg=msg)
def _get_port_range_from_min_max_ports(self, min_port, max_port): def _get_port_range(self, min_port, max_port):
if not min_port: if not min_port:
return None return None
if min_port == max_port: if min_port == max_port:
@ -74,7 +80,7 @@ class EdgeFirewallDriver(object):
"num1:num2" - a range "num1:num2" - a range
"num1,num2,num3" - a list "num1,num2,num3" - a list
""" """
if not port_str: if not port_str or port_str == 'any':
return [] return []
if ':' in port_str: if ':' in port_str:
min_port, sep, max_port = port_str.partition(":") min_port, sep, max_port = port_str.partition(":")
@ -91,7 +97,7 @@ class EdgeFirewallDriver(object):
else: else:
return [int(port_str.strip())] return [int(port_str.strip())]
def _convert_firewall_rule(self, context, rule, index=None): def _convert_firewall_rule(self, rule, index=None):
vcns_rule = { vcns_rule = {
"action": self._convert_firewall_action(rule['action']), "action": self._convert_firewall_action(rule['action']),
"enabled": rule.get('enabled', True)} "enabled": rule.get('enabled', True)}
@ -141,47 +147,59 @@ class EdgeFirewallDriver(object):
vcns_rule['ruleTag'] = index vcns_rule['ruleTag'] = index
return vcns_rule return vcns_rule
def _restore_firewall_rule(self, context, edge_id, response): def _restore_firewall_rule(self, context, edge_id, rule):
rule = response fw_rule = {}
rule_binding = nsxv_db.get_nsxv_edge_firewallrule_binding_by_vseid( try:
context.session, edge_id, rule['ruleId']) rule_binding = (
nsxv_db.get_nsxv_edge_firewallrule_binding_by_vseid(
context.session, edge_id, rule['ruleId']))
except nsx_exc.NsxPluginException:
rule_binding = None
fw_rule['id'] = (rule_binding['rule_id']
if rule_binding else rule['ruleId'])
fw_rule['ruleId'] = rule['ruleId']
if rule.get('source'):
src = rule['source']
fw_rule['source_ip_address'] = src['ipAddress']
fw_rule['source_vnic_groups'] = src['vnicGroupId']
if rule.get('destination'):
dest = rule['destination']
fw_rule['destination_ip_address'] = dest['ipAddress']
fw_rule['destination_vnic_groups'] = dest['vnicGroupId']
if 'application' in rule and 'service' in rule['application']:
service = rule['application']['service'][0] service = rule['application']['service'][0]
src_port_range = self._get_port_range_from_min_max_ports( fw_rule['protocol'] = service['protocol']
fw_rule['source_port'] = self._get_port_range(
service['sourcePort'][0], service['sourcePort'][-1]) service['sourcePort'][0], service['sourcePort'][-1])
dst_port_range = self._get_port_range_from_min_max_ports( fw_rule['destination_port'] = self._get_port_range(
service['port'][0], service['port'][-1]) service['port'][0], service['port'][-1])
fw_rule = {
'firewall_rule': { fw_rule['action'] = self._restore_firewall_action(rule['action'])
'id': rule_binding['rule_id'], fw_rule['enabled'] = rule['enabled']
'source_ip_address': rule['source']['ipAddress'],
'destination_ip_address': rule['destination']['ipAddress'],
'protocol': service['protocol'],
'destination_port': dst_port_range,
'source_port': src_port_range,
'action': self._restore_firewall_action(rule['action']),
'enabled': rule['enabled']}}
if rule.get('name'): if rule.get('name'):
fw_rule['firewall_rule']['name'] = rule['name'] fw_rule['name'] = rule['name']
if rule.get('description'): if rule.get('description'):
fw_rule['firewall_rule']['description'] = rule['description'] fw_rule['description'] = rule['description']
return fw_rule return fw_rule
def _convert_firewall(self, context, firewall, allow_external=False): def _convert_firewall(self, firewall, allow_external=False):
#bulk configuration on firewall and rescheduling the rule binding #bulk configuration on firewall and rescheduling the rule binding
ruleTag = 1 ruleTag = 1
vcns_rules = [] vcns_rules = []
for rule in firewall['firewall_rule_list']:
tag = rule.get('ruleTag', ruleTag)
vcns_rule = self._convert_firewall_rule(context, rule, tag)
vcns_rules.append(vcns_rule)
if not rule.get('ruleTag'):
ruleTag += 1
if allow_external: if allow_external:
vcns_rules.append( vcns_rules.append(
{'action': "accept", {'action': "accept",
'enabled': True, 'enabled': True,
'destination': {'vnicGroupId': ["external"]}, 'destination': {'vnicGroupId': ["external"]},
'ruleTag': ruleTag}) 'ruleTag': ruleTag})
for rule in firewall['firewall_rule_list']:
tag = rule.get('ruleTag', ruleTag)
vcns_rule = self._convert_firewall_rule(rule, tag)
vcns_rules.append(vcns_rule)
if not rule.get('ruleTag'):
ruleTag += 1
return { return {
'featureType': "firewall_4.0", 'featureType': "firewall_4.0",
'globalConfig': {'tcpTimeoutEstablished': 7200}, 'globalConfig': {'tcpTimeoutEstablished': 7200},
@ -192,32 +210,10 @@ class EdgeFirewallDriver(object):
res = {} res = {}
res['firewall_rule_list'] = [] res['firewall_rule_list'] = []
for rule in response['firewallRules']['firewallRules']: for rule in response['firewallRules']['firewallRules']:
rule_binding = ( if rule.get('ruleType') == 'default_policy':
nsxv_db.get_nsxv_edge_firewallrule_binding_by_vseid(
context.session, edge_id, rule['ruleId']))
if rule_binding is None:
continue continue
service = rule['application']['service'][0] firewall_rule = self._restore_firewall_rule(context, edge_id, rule)
src_port_range = self._get_port_range_from_min_max_ports( res['firewall_rule_list'].append({'firewall_rule': firewall_rule})
service['sourcePort'][0], service['sourcePort'][-1])
dst_port_range = self._get_port_range_from_min_max_ports(
service['port'][0], service['port'][-1])
item = {
'firewall_rule': {
'id': rule_binding['rule_id'],
'source_ip_address': rule['source']['ipAddress'],
'destination_ip_address': rule[
'destination']['ipAddress'],
'protocol': service['protocol'],
'destination_port': dst_port_range,
'source_port': src_port_range,
'action': self._restore_firewall_action(rule['action']),
'enabled': rule['enabled']}}
if rule.get('name'):
item['firewall_rule']['name'] = rule['name']
if rule.get('description'):
item['firewall_rule']['description'] = rule['description']
res['firewall_rule_list'].append(item)
return res return res
def _get_firewall(self, edge_id): def _get_firewall(self, edge_id):
@ -277,7 +273,7 @@ class EdgeFirewallDriver(object):
rule_map = nsxv_db.get_nsxv_edge_firewallrule_binding( rule_map = nsxv_db.get_nsxv_edge_firewallrule_binding(
context.session, id, edge_id) context.session, id, edge_id)
vcns_rule_id = rule_map.rule_vseid vcns_rule_id = rule_map.rule_vseid
fwr_req = self._convert_firewall_rule(context, firewall_rule) fwr_req = self._convert_firewall_rule(firewall_rule)
try: try:
self.vcns.update_firewall_rule(edge_id, vcns_rule_id, fwr_req) self.vcns.update_firewall_rule(edge_id, vcns_rule_id, fwr_req)
except vcns_exc.VcnsApiException: except vcns_exc.VcnsApiException:
@ -308,7 +304,7 @@ class EdgeFirewallDriver(object):
rule_map = nsxv_db.get_nsxv_edge_firewallrule_binding( rule_map = nsxv_db.get_nsxv_edge_firewallrule_binding(
context.session, ref_rule_id, edge_id) context.session, ref_rule_id, edge_id)
ref_vcns_rule_id = rule_map.rule_vseid ref_vcns_rule_id = rule_map.rule_vseid
fwr_req = self._convert_firewall_rule(context, firewall_rule) fwr_req = self._convert_firewall_rule(firewall_rule)
try: try:
header = self.vcns.add_firewall_rule_above( header = self.vcns.add_firewall_rule_above(
edge_id, ref_vcns_rule_id, fwr_req)[0] edge_id, ref_vcns_rule_id, fwr_req)[0]
@ -334,7 +330,7 @@ class EdgeFirewallDriver(object):
ref_vcns_rule_id = rule_map.rule_vseid ref_vcns_rule_id = rule_map.rule_vseid
fwr_vse_next = self._get_firewall_rule_next( fwr_vse_next = self._get_firewall_rule_next(
context, edge_id, ref_vcns_rule_id) context, edge_id, ref_vcns_rule_id)
fwr_req = self._convert_firewall_rule(context, firewall_rule) fwr_req = self._convert_firewall_rule(firewall_rule)
if fwr_vse_next: if fwr_vse_next:
ref_vcns_rule_id = fwr_vse_next['ruleId'] ref_vcns_rule_id = fwr_vse_next['ruleId']
try: try:
@ -379,7 +375,7 @@ class EdgeFirewallDriver(object):
raise vcns_exc.VcnsBadRequest(resource='firewall_rule', msg=msg) raise vcns_exc.VcnsBadRequest(resource='firewall_rule', msg=msg)
def update_firewall(self, edge_id, firewall, context, allow_external=True): def update_firewall(self, edge_id, firewall, context, allow_external=True):
config = self._convert_firewall(None, firewall, config = self._convert_firewall(firewall,
allow_external=allow_external) allow_external=allow_external)
try: try:

View File

View File

@ -0,0 +1,202 @@
# 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.
from neutron_lib import context as n_context
from neutron_lib.plugins import directory
from oslo_log import helpers as log_helpers
from oslo_log import log as logging
from neutron_fwaas.extensions import firewall as fw_ext
from neutron_fwaas.services.firewall.drivers import fwaas_base
from vmware_nsx.common import locking
from vmware_nsx.plugins.nsx_v.vshield.common import (
exceptions as vcns_exc)
from vmware_nsx.plugins.nsx_v.vshield import edge_utils
from vmware_nsx.plugins.nsx_v.vshield import vcns_driver
LOG = logging.getLogger(__name__)
FWAAS_DRIVER_NAME = 'Fwaas NSX-V driver'
RULE_NAME_PREFIX = 'Fwaas-'
class EdgeFwaasDriver(fwaas_base.FwaasDriverBase):
"""NSX-V driver for Firewall As A Service - V1."""
@property
def edge_manager(self):
return directory.get_plugin().edge_manager
def __init__(self):
LOG.debug("Loading FWaaS NsxVDriver.")
super(EdgeFwaasDriver, self).__init__()
self._nsxv = vcns_driver.VcnsDriver(None)
def _get_routers_edges(self, context, apply_list):
# Get edges for all the routers in the apply list.
# note that shared routers are currently not supported
edge_manager = self.edge_manager
edges = []
for router_info in apply_list:
lookup_id = None
router_id = router_info.router_id
if router_info.router.get('distributed'):
# we need the plr edge id
lookup_id = edge_manager.get_plr_by_tlr_id(
context, router_id)
if router_info.router.get('router_type') == 'shared':
LOG.info("Cannot apply firewall to shared router %s",
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:
edges.append(edge_id)
return edges
def _translate_rules(self, fwaas_rules):
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]
# source & destination should be lists
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']]
translated_rules.append(rule)
return translated_rules
def _get_other_backend_rules(self, context, edge_id):
"""Get a list of current backend rules from other applications
Those rules should stay on the backend firewall, when updating the
FWaaS rules.
"""
try:
backend_fw = self._nsxv.get_firewall(context, edge_id)
backend_rules = backend_fw['firewall_rule_list']
except vcns_exc.VcnsApiException:
# Need to create a new one
backend_rules = []
# remove old FWaaS rules from the rules list
relevant_rules = []
for rule_item in backend_rules:
rule = rule_item['firewall_rule']
if not rule.get('name', '').startswith(RULE_NAME_PREFIX):
relevant_rules.append(rule)
return relevant_rules
def _set_rules_on_edge(self, context, edge_id, fw_id, translated_rules):
"""delete old FWaaS rules from the Edge, and add new ones
Note that the edge might have other FW rules like NAT or LBaas
that should remain there.
"""
# Get the existing backend rules which do not belong to FWaaS
backend_rules = self._get_other_backend_rules(context, edge_id)
# add new FWaaS rules at the end by their original order
backend_rules.extend(translated_rules)
# update the backend
# allow_external is False because it was already added
try:
with locking.LockManager.get_lock(str(edge_id)):
self._nsxv.update_firewall(
edge_id,
{'firewall_rule_list': backend_rules},
context,
allow_external=False)
except Exception as e:
# catch known library exceptions and raise Fwaas generic exception
LOG.error("Failed to update backend firewall %(fw)s: "
"%(e)s", {'e': e, 'fw': fw_id})
raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME)
def _create_or_update_firewall(self, agent_mode, apply_list, firewall):
# admin state down means default block rule firewall
if not firewall['admin_state_up']:
self.apply_default_policy(agent_mode, apply_list, firewall)
return
context = n_context.get_admin_context()
# Find out the relevant edges
router_edges = self._get_routers_edges(context, apply_list)
if not router_edges:
LOG.warning("Cannot apply the firewall to any of the routers %s",
apply_list)
return
rules = self._translate_rules(firewall['firewall_rule_list'])
# update each edge
for edge_id in router_edges:
self._set_rules_on_edge(
context, edge_id, firewall['id'], rules)
@log_helpers.log_method_call
def create_firewall(self, agent_mode, apply_list, firewall):
"""Create the Firewall with a given policy. """
self._create_or_update_firewall(agent_mode, apply_list, firewall)
@log_helpers.log_method_call
def update_firewall(self, agent_mode, apply_list, firewall):
"""Remove previous policy and apply the new policy."""
self._create_or_update_firewall(agent_mode, apply_list, firewall)
def _delete_firewall_or_set_default_policy(self, apply_list, firewall):
context = n_context.get_admin_context()
router_edges = self._get_routers_edges(context, apply_list)
if router_edges:
for edge_id in router_edges:
self._set_rules_on_edge(context, edge_id, firewall['id'], [])
@log_helpers.log_method_call
def delete_firewall(self, agent_mode, apply_list, firewall):
"""Delete firewall.
Removes rules created by this instance from the backend firewall.
"""
self._delete_firewall_or_set_default_policy(apply_list, firewall)
@log_helpers.log_method_call
def apply_default_policy(self, agent_mode, apply_list, firewall):
"""Apply the default policy (deny all).
The backend firewall always has this policy as default, so we only
need to delete the current rules.
"""
self._delete_firewall_or_set_default_policy(apply_list, firewall)
def get_firewall_translated_rules(self, firewall):
if firewall['admin_state_up']:
return self._translate_rules(firewall['firewall_rule_list'])
return []

View File

@ -0,0 +1,115 @@
# 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.
from oslo_config import cfg
from neutron.agent.l3 import router_info
from neutron.common import config as neutron_config # noqa
from neutron_fwaas.db.firewall import firewall_db # noqa
from neutron_fwaas.db.firewall import firewall_router_insertion_db \
as fw_r_ins_db
from neutron_fwaas.services.firewall.agents.l3reference \
import firewall_l3_agent
from neutron_lib import context as n_context
from neutron_lib.plugins import directory
class NsxvFwaasCallbacks(firewall_l3_agent.L3WithFWaaS):
"""NSX-V RPC callbacks for Firewall As A Service - V1."""
def __init__(self):
# The super code needs a configuration object with the neutron host
# and an agent_mode, hich our driver doesn't use.
neutron_conf = cfg.CONF
neutron_conf.agent_mode = 'nsx'
super(NsxvFwaasCallbacks, self).__init__(conf=neutron_conf)
@property
def core_plugin(self):
return directory.get_plugin()
# Override functions using the agent_api that is not used by our plugin
def _get_router_ids_for_fw(self, context, fw, to_delete=False):
"""Return the router_ids either from fw dict or tenant routers."""
if self._has_router_insertion_fields(fw):
# it is a new version of plugin
return (fw['del-router-ids'] if to_delete
else fw['add-router-ids'])
else:
return [router['id'] for router in
self._get_routers_in_project(context, fw['tenant_id'])]
def _get_routers_in_project(self, context, project_id):
return self.core_plugin.get_routers(
context,
filters={'tenant_id': [project_id]})
def _router_dict_to_obj(self, r):
# The callbacks expect a router-info object
return router_info.RouterInfo(
None, r['id'], router=r,
agent_conf=None,
interface_driver=None,
use_ipv6=False)
def _get_router_info_list_for_tenant(self, router_ids, tenant_id):
"""Returns the list of router info objects on which to apply the fw."""
context = n_context.get_admin_context()
tenant_routers = self._get_routers_in_project(context, tenant_id)
return [self._router_dict_to_obj(ri) for ri in tenant_routers
if ri['id'] in router_ids]
def get_fwaas_rules_for_router(self, context, router, router_id):
"""Return the list of (translated) fwaas rules for this router."""
if not self.fwaas_enabled:
return []
ctx_elevated = context.elevated()
# get all the relevant router info
# ("router" does not have all the fields)
router_data = self.core_plugin.get_router(ctx_elevated, router['id'])
if not router_data:
return []
if router_data.get('distributed'):
# in case of distributed router router['id'] is the id of the
# neutron router
# and router_id is the plr/tlr (the one that is being updated)
if router_id == router['id']:
# Do not add firewall rules on the tlr router.
return []
if router_data.get('router_type') == 'shared':
# Currently there is no FWaaS support for shared routers
return []
# Exclusive router or PLR
fw_id = self._get_router_firewall_id(ctx_elevated, router['id'])
if fw_id:
return self._get_fw_applicable_rules(ctx_elevated, fw_id)
return []
# TODO(asarfaty): add this api to fwaas firewall-router-insertion-db
def _get_router_firewall_id(self, context, router_id):
entry = context.session.query(
fw_r_ins_db.FirewallRouterAssociation).filter_by(
router_id=router_id).first()
if entry:
return entry.fw_id
def _get_fw_applicable_rules(self, context, fw_id):
fw_list = self.fwplugin_rpc.get_firewalls_for_tenant(context)
for fw in fw_list:
if fw['id'] == fw_id:
return self.fwaas_driver.get_firewall_translated_rules(fw)
return []

View File

@ -86,10 +86,15 @@ TAAS_TABLES = {
'tap_id_associations', 'tap_id_associations',
} }
FWAAS_TABLES = {
'firewall_router_associations',
'cisco_firewall_associations',
}
# EXTERNAL_TABLES should contain all names of tables that are not related to # EXTERNAL_TABLES should contain all names of tables that are not related to
# current repo. # current repo.
EXTERNAL_TABLES = (set(external.TABLES) | LBAAS_TABLES | EXTERNAL_TABLES = (set(external.TABLES) | LBAAS_TABLES |
L2GW_TABLES | SFC_TABLES | TAAS_TABLES) L2GW_TABLES | SFC_TABLES | TAAS_TABLES | FWAAS_TABLES)
class _TestModelsMigrationsFoo(test_migrations._TestModelsMigrations): class _TestModelsMigrationsFoo(test_migrations._TestModelsMigrations):

View File

@ -0,0 +1,142 @@
# 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 copy
import mock
from vmware_nsx.services.fwaas.nsx_v import edge_fwaas_driver
from vmware_nsx.tests.unit.nsx_v import test_plugin as test_v_plugin
FAKE_FW_ID = 'fake_fw_uuid'
class NsxvFwaasTestCase(test_v_plugin.NsxVPluginV2TestCase):
def setUp(self):
super(NsxvFwaasTestCase, self).setUp()
self.firewall = edge_fwaas_driver.EdgeFwaasDriver()
self.firewall._get_routers_edges = mock.Mock()
self.firewall._get_routers_edges.return_value = ['edge-1']
def _fake_rules_v4(self):
rule1 = {'enabled': True,
'action': 'allow',
'ip_version': 4,
'protocol': 'tcp',
'destination_port': '80',
'source_ip_address': '10.24.4.2',
'id': 'fake-fw-rule1'}
rule2 = {'enabled': True,
'action': 'deny',
'ip_version': 4,
'protocol': 'tcp',
'destination_port': '22',
'id': 'fake-fw-rule2'}
rule3 = {'enabled': True,
'action': 'reject',
'ip_version': 4,
'protocol': 'tcp',
'destination_port': '23',
'id': 'fake-fw-rule3'}
return [rule1, rule2, rule3]
def _fake_firewall_no_rule(self):
rule_list = []
fw_inst = {'id': FAKE_FW_ID,
'admin_state_up': True,
'tenant_id': 'tenant-uuid',
'firewall_rule_list': rule_list}
return fw_inst
def _fake_firewall(self, rule_list):
_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': True,
'tenant_id': 'tenant-uuid',
'firewall_rule_list': _rule_list}
return fw_inst
def _fake_firewall_with_admin_down(self, rule_list):
fw_inst = {'id': FAKE_FW_ID,
'admin_state_up': False,
'tenant_id': 'tenant-uuid',
'firewall_rule_list': rule_list}
return fw_inst
def _fake_apply_list(self, router_count=1):
apply_list = []
while router_count > 0:
router_inst = {}
router_info_inst = mock.Mock()
router_info_inst.router = router_inst
apply_list.append(router_info_inst)
router_count -= 1
return apply_list
def _setup_firewall_with_rules(self, func, router_count=1):
apply_list = self._fake_apply_list(router_count=router_count)
rule_list = self._fake_rules_v4()
firewall = self._fake_firewall(rule_list)
edges = ['edge-1'] * router_count
with mock.patch.object(self.firewall._nsxv,
"update_firewall") as update_fw,\
mock.patch.object(self.firewall,
"_get_routers_edges", return_value=edges):
func('nsx', apply_list, firewall)
self.assertEqual(router_count, update_fw.call_count)
bakend_rules = update_fw.call_args[0][1]['firewall_rule_list']
self.assertEqual(len(rule_list), len(bakend_rules))
def test_create_firewall_no_rules(self):
apply_list = self._fake_apply_list()
firewall = self._fake_firewall_no_rule()
with mock.patch.object(self.firewall._nsxv,
"update_firewall") as update_fw:
self.firewall.create_firewall('nsx', apply_list, firewall)
self.assertEqual(1, update_fw.call_count)
bakend_rules = update_fw.call_args[0][1]['firewall_rule_list']
self.assertEqual(0, len(bakend_rules))
def test_create_firewall_with_rules(self):
self._setup_firewall_with_rules(self.firewall.create_firewall)
def test_create_firewall_with_rules_two_routers(self):
self._setup_firewall_with_rules(self.firewall.create_firewall,
router_count=2)
def test_update_firewall_with_rules(self):
self._setup_firewall_with_rules(self.firewall.update_firewall)
def test_delete_firewall(self):
apply_list = self._fake_apply_list()
firewall = self._fake_firewall_no_rule()
with mock.patch.object(self.firewall._nsxv,
"update_firewall") as update_fw:
self.firewall.delete_firewall('nsx', apply_list, firewall)
self.assertEqual(1, update_fw.call_count)
bakend_rules = update_fw.call_args[0][1]['firewall_rule_list']
self.assertEqual(0, len(bakend_rules))
def test_create_firewall_with_admin_down(self):
apply_list = self._fake_apply_list()
rule_list = self._fake_rules_v4()
firewall = self._fake_firewall_with_admin_down(rule_list)
with mock.patch.object(self.firewall._nsxv,
"update_firewall") as update_fw:
self.firewall.create_firewall('nsx', apply_list, firewall)
self.assertEqual(1, update_fw.call_count)
bakend_rules = update_fw.call_args[0][1]['firewall_rule_list']
self.assertEqual(0, len(bakend_rules))

View File

@ -3097,8 +3097,7 @@ class TestExclusiveRouterTestCase(L3NatTest, L3NatTestCaseBase,
# Also test the md_srvip conversion: # Also test the md_srvip conversion:
drv = edge_firewall_driver.EdgeFirewallDriver() drv = edge_firewall_driver.EdgeFirewallDriver()
rule = drv._convert_firewall_rule( rule = drv._convert_firewall_rule(md_srvip)
context.get_admin_context(), md_srvip)
exp_service = {'service': [{'port': [80, 443, 8775], exp_service = {'service': [{'port': [80, 443, 8775],
'protocol': 'tcp'}]} 'protocol': 'tcp'}]}
exp_rule = {'action': 'accept', exp_rule = {'action': 'accept',
@ -3156,8 +3155,7 @@ class TestExclusiveRouterTestCase(L3NatTest, L3NatTestCaseBase,
# Also test the rule conversion # Also test the rule conversion
# Ports should be sorted & unique, and ignore non numeric values # Ports should be sorted & unique, and ignore non numeric values
drv = edge_firewall_driver.EdgeFirewallDriver() drv = edge_firewall_driver.EdgeFirewallDriver()
rule = drv._convert_firewall_rule( rule = drv._convert_firewall_rule(md_srvip)
context.get_admin_context(), md_srvip)
exp_service = {'service': [{'port': [55, 66, 80, 443, 8775], exp_service = {'service': [{'port': [55, 66, 80, 443, 8775],
'protocol': 'tcp'}]} 'protocol': 'tcp'}]}
exp_rule = {'action': 'accept', exp_rule = {'action': 'accept',