![Adit Sarfaty](/assets/img/avatar_default.png)
Fail the router create/update with a driver error in case the added router belongs to a diffeeret project. For example - the metadata-proxy router. Theneutron_fwaas code allows it and ignores this router, but in our case it is better to fail and set the router in error state, just like we do for other unsupported routers. In addition - If the router is in error state, or not supported by the driver, do not add it's rules to the backend, but also do not fail. Change-Id: Ia6a8cccf5d90d19c31e961901441007d8484c73e
244 lines
9.7 KiB
Python
244 lines
9.7 KiB
Python
# 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 exceptions
|
|
from neutron_fwaas.services.firewall.drivers import fwaas_base
|
|
|
|
from vmware_nsx.common import locking
|
|
from vmware_nsx.plugins.nsx_v.vshield import edge_utils
|
|
|
|
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 core_plugin(self):
|
|
return directory.get_plugin()
|
|
|
|
@property
|
|
def edge_manager(self):
|
|
return self.core_plugin.edge_manager
|
|
|
|
def __init__(self):
|
|
LOG.debug("Loading FWaaS NsxVDriver.")
|
|
super(EdgeFwaasDriver, self).__init__()
|
|
self.driver_name = FWAAS_DRIVER_NAME
|
|
|
|
def should_apply_firewall_to_router(self, router_data,
|
|
raise_exception=True):
|
|
"""Return True if the firewall rules should 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 _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_map = {}
|
|
for router_info in apply_list:
|
|
|
|
# No FWaaS rules needed if there is no external gateway
|
|
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
|
|
# we need the plr edge id
|
|
lookup_id = 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:
|
|
edges_map[router_id] = {'edge_id': edge_id,
|
|
'lookup_id': lookup_id}
|
|
return edges_map
|
|
|
|
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 _set_rules_on_router_edge(self, context, router_id, neutron_id,
|
|
edge_id, fw_id, translated_rules,
|
|
delete_fw=False):
|
|
"""Recreate router edge firewall rules
|
|
|
|
Using the plugin code to recreate all the rules with the additional
|
|
FWaaS rules.
|
|
|
|
router_id is the is of the router about to be updated
|
|
(in case of distributed router - the plr)
|
|
neutron_id is the neutron router id
|
|
"""
|
|
# update the backend
|
|
router_db = self.core_plugin._get_router(context, neutron_id)
|
|
try:
|
|
with locking.LockManager.get_lock(str(edge_id)):
|
|
self.core_plugin.update_router_firewall(
|
|
context, router_id, router_db,
|
|
fwaas_rules=translated_rules)
|
|
except Exception as e:
|
|
# catch known library exceptions and raise Fwaas generic exception
|
|
LOG.error("Failed to update firewall %(fw)s on edge %(edge_id)s: "
|
|
"%(e)s", {'e': e, 'fw': fw_id, 'edge_id': edge_id})
|
|
raise exceptions.FirewallInternalDriverError(
|
|
driver=self.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
|
|
|
|
# get router-edge mapping
|
|
context = n_context.get_admin_context()
|
|
edges_map = self._get_routers_edges(context, apply_list)
|
|
if not edges_map:
|
|
routers = [r.router_id for r in apply_list]
|
|
LOG.warning("Cannot apply the firewall %(fw)s to any of the "
|
|
"routers %(rtrs)s",
|
|
{'fw': firewall['id'], 'rtrs': routers})
|
|
return
|
|
|
|
# Translate the FWaaS rules
|
|
rules = self._translate_rules(firewall['firewall_rule_list'])
|
|
|
|
# update each relevant edge with the new rules
|
|
for router_info in apply_list:
|
|
neutron_id = router_info.router_id
|
|
info = edges_map.get(neutron_id)
|
|
if info:
|
|
self._set_rules_on_router_edge(
|
|
context, info['lookup_id'], neutron_id, info['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,
|
|
delete_fw=False):
|
|
# get router-edge mapping
|
|
context = n_context.get_admin_context()
|
|
edges_map = self._get_routers_edges(context, apply_list)
|
|
|
|
# if the firewall is deleted, rules should be None
|
|
rules = None if delete_fw else []
|
|
|
|
# Go over all routers and update them on backend
|
|
for router_info in apply_list:
|
|
neutron_id = router_info.router_id
|
|
info = edges_map.get(neutron_id)
|
|
if info:
|
|
self._set_rules_on_router_edge(
|
|
context, info['lookup_id'], neutron_id, info['edge_id'],
|
|
firewall['id'], rules, delete_fw=delete_fw)
|
|
|
|
@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
|
|
And add the default allow-external rule.
|
|
"""
|
|
self._delete_firewall_or_set_default_policy(apply_list, firewall,
|
|
delete_fw=True)
|
|
|
|
@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 (=deny all) as default,
|
|
so we only need to delete the current rules.
|
|
"""
|
|
self._delete_firewall_or_set_default_policy(apply_list, firewall,
|
|
delete_fw=False)
|
|
|
|
def get_firewall_translated_rules(self, firewall):
|
|
if firewall['admin_state_up']:
|
|
return self._translate_rules(firewall['firewall_rule_list'])
|
|
return []
|