45ab0ca064
Commit I817d9434715d7bd3cba266575321d4c89bf173e4 added the vnic-id to the rule so it will be unique. But for distributed router this is not good enough as the vnix is not part of the rule. Instead the driver will add each rule only once per firewall-group, and add the firewall group id instead of the vnic for distributed routers Change-Id: If775cc7aeb9e3edb64462484bb1b2714a7d30073
198 lines
8.2 KiB
Python
198 lines
8.2 KiB
Python
# 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, with_rpc):
|
|
super(NsxvFwaasCallbacksV2, self).__init__(with_rpc)
|
|
# 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, router_db,
|
|
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 = []
|
|
fwg_ids = []
|
|
router_dict = {}
|
|
# 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:
|
|
if not router_dict:
|
|
self.core_plugin._extend_nsx_router_dict(
|
|
router_dict, router_db)
|
|
if router_dict['distributed']:
|
|
# The vnic_id is ignored for distributed routers, so
|
|
# each rule will be applied to all the interfaces.
|
|
vnic_id = None
|
|
# if rules for this fwg where already added skip it
|
|
if fwg['id'] in fwg_ids:
|
|
continue
|
|
else:
|
|
# 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))
|
|
fwg_ids.append(fwg['id'])
|
|
|
|
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,
|
|
fwg_id=firewall_group['id']))
|
|
port_rules.extend(self.translate_rules(
|
|
firewall_group['egress_rule_list'],
|
|
replace_src=vnic_id,
|
|
logged=logged,
|
|
is_ingress=False,
|
|
fwg_id=firewall_group['id']))
|
|
|
|
# Add ingress/egress block rules for this port
|
|
default_ingress = {'name': "Block port ingress",
|
|
'action': edge_firewall_driver.FWAAS_DENY,
|
|
'logged': logged}
|
|
default_egress = {'name': "Block port egress",
|
|
'action': edge_firewall_driver.FWAAS_DENY,
|
|
'logged': logged}
|
|
if vnic_id:
|
|
default_ingress['destination_vnic_groups'] = [vnic_id]
|
|
default_egress['source_vnic_groups'] = [vnic_id]
|
|
port_rules.extend([default_ingress, default_egress])
|
|
|
|
return port_rules
|
|
|
|
def translate_rules(self, fwaas_rules, replace_dest=None, replace_src=None,
|
|
logged=False, is_ingress=True, fwg_id=None):
|
|
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-%s' % (replace_dest or
|
|
fwg_id[:15],
|
|
rule['id']))[:36]
|
|
else:
|
|
rule['id'] = ('egress-%s-%s' % (replace_src or
|
|
fwg_id[:15],
|
|
rule['id']))[:36]
|
|
# source & destination should be lists
|
|
if (rule.get('destination_ip_address') and
|
|
not rule['destination_ip_address'].startswith('0.0.0.0')):
|
|
rule['destination_ip_address'] = [
|
|
rule['destination_ip_address']]
|
|
else:
|
|
if replace_dest:
|
|
rule['destination_vnic_groups'] = [replace_dest]
|
|
if 'destination_ip_address' in rule:
|
|
del rule['destination_ip_address']
|
|
if (rule.get('source_ip_address') and
|
|
not rule['source_ip_address'].startswith('0.0.0.0')):
|
|
rule['source_ip_address'] = [rule['source_ip_address']]
|
|
else:
|
|
if replace_src:
|
|
rule['source_vnic_groups'] = [replace_src]
|
|
if 'source_ip_address' in rule:
|
|
del rule['source_ip_address']
|
|
if logged:
|
|
rule['logged'] = True
|
|
translated_rules.append(rule)
|
|
|
|
return translated_rules
|