Merge "NSX|V3: Add DHCP relay firewall rules"

This commit is contained in:
Jenkins 2017-10-05 07:56:09 +00:00 committed by Gerrit Code Review
commit 57b1ca6d64
8 changed files with 262 additions and 28 deletions

View File

@ -181,7 +181,7 @@ class NsxV3AvailabilityZone(common_az.ConfiguredAvailabilityZone):
nsxlib.feature_supported(nsxlib_consts.FEATURE_DHCP_RELAY)):
relay_id = None
if cfg.CONF.nsx_v3.init_objects_by_tags:
# Find the TZ by its tag
# Find the relay service by its tag
relay_id = nsxlib.get_id_by_resource_and_tag(
nsxlib.relay_service.resource_type,
cfg.CONF.nsx_v3.search_objects_scope,
@ -191,8 +191,13 @@ class NsxV3AvailabilityZone(common_az.ConfiguredAvailabilityZone):
relay_id = nsxlib.relay_service.get_id_by_name_or_id(
self.dhcp_relay_service)
self.dhcp_relay_service = relay_id
# if there is a relay service - also find the server ips
if self.dhcp_relay_service:
self.dhcp_relay_servers = nsxlib.relay_service.get_server_ips(
self.dhcp_relay_service)
else:
self.dhcp_relay_service = None
self.dhcp_relay_servers = None
class NsxV3AvailabilityZones(common_az.ConfiguredAvailabilityZones):

View File

@ -3305,6 +3305,26 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
return self.fwaas_callbacks.update_router_firewall(
context, self.nsxlib, router_id, ports)
def _get_port_relay_servers(self, context, port_id, network_id=None):
if not network_id:
port = self.get_port(context, port_id)
network_id = port['network_id']
net_az = self.get_network_az_by_net_id(context, network_id)
return net_az.dhcp_relay_servers
def _get_port_relay_services(self):
# DHCP services: UDP 67, 68, 2535
#TODO(asarfaty): use configurable ports
service1 = self.nsxlib.firewall_section.get_nsservice(
nsxlib_consts.L4_PORT_SET_NSSERVICE,
l4_protocol=nsxlib_consts.UDP,
destination_ports=['67-68'])
service2 = self.nsxlib.firewall_section.get_nsservice(
nsxlib_consts.L4_PORT_SET_NSSERVICE,
l4_protocol=nsxlib_consts.UDP,
destination_ports=['2535'])
return [service1, service2]
def get_extra_fw_rules(self, context, router_id, port_id=None):
"""Return firewall rules that should be added to the router firewall
@ -3317,8 +3337,59 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
port should be returned, and the rules should be ingress/egress
(but not both) and include the source/dest nsx logical port.
"""
#TODO(asarfaty): DHCP relay rules
return []
extra_rules = []
# DHCP relay rules:
# get the list of relevant relay servers
elv_ctx = context.elevated()
if port_id:
relay_servers = self._get_port_relay_servers(elv_ctx, port_id)
else:
relay_servers = []
filters = {'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF],
'device_id': [router_id]}
ports = self.get_ports(elv_ctx, filters=filters)
for port in ports:
port_relay_servers = self._get_port_relay_servers(
elv_ctx, port['id'], network_id=port['network_id'])
if port_relay_servers:
relay_servers.extend(port_relay_servers)
# Add rules to allow dhcp traffic relay servers
if relay_servers:
# if it is a single port, the source/dest is this logical port
if port_id:
_net_id, nsx_port_id = nsx_db.get_nsx_switch_and_port_id(
context.session, port_id)
port_target = [{'target_type': 'LogicalPort',
'target_id': nsx_port_id}]
else:
port_target = None
# translate the relay server ips to the firewall format
relay_target = []
if self.fwaas_callbacks:
relay_target = (self.fwaas_callbacks.fwaas_driver.
translate_addresses_to_target(set(relay_servers)))
dhcp_services = self._get_port_relay_services()
# ingress rule
extra_rules.append({
'display_name': "DHCP Relay ingress traffic",
'action': nsxlib_consts.FW_ACTION_ALLOW,
'sources': relay_target,
'destinations': port_target,
'services': dhcp_services,
'direction': 'IN'})
# egress rule
extra_rules.append({
'display_name': "DHCP Relay egress traffic",
'action': nsxlib_consts.FW_ACTION_ALLOW,
'destinations': relay_target,
'sources': port_target,
'services': dhcp_services,
'direction': 'OUT'})
return extra_rules
def _get_ports_and_address_groups(self, context, router_id, network_id,
exclude_sub_ids=None):

View File

@ -98,7 +98,7 @@ class CommonEdgeFwaasV3Driver(fwaas_base.FwaasDriverBase):
cidr,
consts.IPV6 if netaddr.valid_ipv6(cidr) else consts.IPV4)
def _translate_addresses(self, cidrs):
def translate_addresses_to_target(self, cidrs):
return [self._translate_cidr(ip) for ip in cidrs]
@staticmethod
@ -170,7 +170,7 @@ class CommonEdgeFwaasV3Driver(fwaas_base.FwaasDriverBase):
'target_id': replace_dest}]
nsx_rule['direction'] = 'IN'
elif rule.get('destination_ip_address'):
nsx_rule['destinations'] = self._translate_addresses(
nsx_rule['destinations'] = self.translate_addresses_to_target(
[rule['destination_ip_address']])
if replace_src:
# set this value as the source logical port,
@ -179,7 +179,7 @@ class CommonEdgeFwaasV3Driver(fwaas_base.FwaasDriverBase):
'target_id': replace_src}]
nsx_rule['direction'] = 'OUT'
elif rule.get('source_ip_address'):
nsx_rule['sources'] = self._translate_addresses(
nsx_rule['sources'] = self.translate_addresses_to_target(
[rule['source_ip_address']])
if rule.get('protocol'):
nsx_rule['services'] = self._translate_services(rule)

View File

@ -53,7 +53,6 @@ class Nsxv3FwaasCallbacksV1(com_clbcks.NsxFwaasCallbacks):
This method should be called on FWaaS updates, and on router
interfaces changes.
"""
# find the backend router and its firewall section
nsx_id, sect_id = self.fwaas_driver.get_backend_router_and_fw_section(
context, router_id)

View File

@ -188,27 +188,37 @@ def update_dhcp_relay(resource, event, trigger, **kwargs):
# initialize the availability zones and nsxlib
config.register_nsxv3_azs(cfg.CONF, cfg.CONF.nsx_v3.availability_zones)
# get all neutron router interfaces ports
admin_cxt = neutron_context.get_admin_context()
with utils.NsxV3PluginWrapper() as plugin:
filters = {'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF]}
ports = plugin.get_ports(admin_cxt, filters=filters)
for port in ports:
# get the backend router port by the tag
nsx_port_id = nsxlib.get_id_by_resource_and_tag(
'LogicalRouterDownLinkPort',
'os-neutron-rport-id', port['id'])
if not nsx_port_id:
LOG.warning("Couldn't find nsx router port for interface %s",
port['id'])
continue
# get the network of this port
network_id = port['network_id']
# check the relay service on the az of the network
az = plugin.get_network_az_by_net_id(admin_cxt, network_id)
nsxlib.logical_router_port.update(
nsx_port_id, relay_service_uuid=az.dhcp_relay_service)
#TODO(asarfaty) also update the firewall rules of the routers
# Make sure FWaaS was initialized
plugin.init_fwaas_for_admin_utils()
# get all neutron routers and interfaces ports
routers = plugin.get_routers(admin_cxt)
for router in routers:
LOG.info("Updating router %s", router['id'])
filters = {'device_owner': [l3_db.DEVICE_OWNER_ROUTER_INTF],
'device_id': [router['id']]}
ports = plugin.get_ports(admin_cxt, filters=filters)
for port in ports:
# get the backend router port by the tag
nsx_port_id = nsxlib.get_id_by_resource_and_tag(
'LogicalRouterDownLinkPort',
'os-neutron-rport-id', port['id'])
if not nsx_port_id:
LOG.warning("Couldn't find nsx router port for interface "
"%s", port['id'])
continue
# get the network of this port
network_id = port['network_id']
# check the relay service on the az of the network
az = plugin.get_network_az_by_net_id(admin_cxt, network_id)
nsxlib.logical_router_port.update(
nsx_port_id, relay_service_uuid=az.dhcp_relay_service)
# if FWaaS is enables, also update the firewall rules
plugin.update_router_firewall(admin_cxt, router['id'])
LOG.info("Done.")

View File

@ -13,14 +13,22 @@
# under the License.
from oslo_config import cfg
from neutron.db import db_base_plugin_v2
from neutron import manager
from neutron_lib import context
from neutron_lib.plugins import constants as const
from neutron_lib.plugins import directory
from neutron_fwaas.services.firewall import fwaas_plugin as fwaas_plugin_v1
from neutron_fwaas.services.firewall import fwaas_plugin_v2
from vmware_nsx.db import db as nsx_db
from vmware_nsx.plugins.nsx_v3 import plugin
from vmware_nsx.plugins.nsx_v3 import utils as v3_utils
from vmware_nsx.services.fwaas.nsx_v3 import fwaas_callbacks_v1
from vmware_nsx.services.fwaas.nsx_v3 import fwaas_callbacks_v2
from vmware_nsxlib.v3 import nsx_constants
_NSXLIB = None
@ -107,6 +115,35 @@ class NsxV3PluginWrapper(plugin.NsxV3Plugin):
def __exit__(self, exc_type, exc_value, traceback):
directory.add_plugin(const.CORE, None)
def _init_fwaas_plugin(self, provider, callbacks_class, plugin_callbacks):
fwaas_plugin_class = manager.NeutronManager.load_class_for_provider(
'neutron.service_plugins', provider)
fwaas_plugin = fwaas_plugin_class()
self.fwaas_callbacks = callbacks_class(self.nsxlib)
# override the fwplugin_rpc since there is no RPC support in adminutils
self.fwaas_callbacks.fwplugin_rpc = plugin_callbacks(fwaas_plugin)
def init_fwaas_for_admin_utils(self):
# initialize the FWaaS plugin and callbacks
self.fwaas_callbacks = None
# This is an ugly patch to find out if it is v1 or v2
service_plugins = cfg.CONF.service_plugins
for srv_plugin in service_plugins:
if 'firewall' in srv_plugin:
if 'v2' in srv_plugin:
# FWaaS V2
self._init_fwaas_plugin(
'firewall_v2',
fwaas_callbacks_v2.Nsxv3FwaasCallbacksV2,
fwaas_plugin_v2.FirewallCallbacks)
else:
# FWaaS V1
self._init_fwaas_plugin(
'firewall',
fwaas_callbacks_v1.Nsxv3FwaasCallbacksV1,
fwaas_plugin_v1.FirewallCallbacks)
return
def _init_dhcp_metadata(self):
pass

View File

@ -29,6 +29,9 @@ from vmware_nsxlib.v3 import nsx_constants as consts
FAKE_FW_ID = 'fake_fw_uuid'
FAKE_ROUTER_ID = 'fake_rtr_uuid'
MOCK_NSX_ID = 'nsx_router_id'
FAKE_PORT_ID = 'fake_port_uuid'
FAKE_NET_ID = 'fake_net_uuid'
FAKE_NSX_PORT_ID = 'fake_nsx_port_uuid'
MOCK_DEFAULT_RULE_ID = 'nsx_default_rule_id'
MOCK_SECTION_ID = 'sec_id'
DEFAULT_RULE = {'is_default': True,
@ -177,6 +180,8 @@ class Nsxv3FwaasTestCase(test_v3_plugin.NsxV3PluginTestCaseMixin):
"update") as update_fw, \
mock.patch.object(self.plugin, '_get_router_interfaces',
return_value=[]), \
mock.patch.object(self.plugin, 'get_ports',
return_value=[]), \
mock.patch.object(self.plugin, 'get_router',
return_value=apply_list[0]), \
mock.patch.object(self.plugin.fwaas_callbacks,
@ -199,6 +204,8 @@ class Nsxv3FwaasTestCase(test_v3_plugin.NsxV3PluginTestCaseMixin):
"update") as update_fw,\
mock.patch.object(self.plugin, '_get_router_interfaces',
return_value=[]), \
mock.patch.object(self.plugin, 'get_ports',
return_value=[]), \
mock.patch.object(self.plugin, 'get_router',
return_value=apply_list[0]), \
mock.patch.object(self.plugin.fwaas_callbacks,
@ -269,6 +276,8 @@ class Nsxv3FwaasTestCase(test_v3_plugin.NsxV3PluginTestCaseMixin):
"update") as update_fw, \
mock.patch.object(self.plugin, '_get_router_interfaces',
return_value=[]), \
mock.patch.object(self.plugin, 'get_ports',
return_value=[]), \
mock.patch.object(self.plugin, 'get_router',
return_value=apply_list[0]), \
mock.patch.object(self.plugin.fwaas_callbacks,
@ -281,3 +290,47 @@ class Nsxv3FwaasTestCase(test_v3_plugin.NsxV3PluginTestCaseMixin):
update_fw.assert_called_once_with(
MOCK_SECTION_ID,
rules=[self._default_rule()])
def test_create_firewall_with_dhcp_relay(self):
apply_list = self._fake_apply_list()
firewall = self._fake_firewall_no_rule()
relay_server = '1.1.1.1'
port = {'id': FAKE_PORT_ID, 'network_id': FAKE_NET_ID}
with mock.patch("vmware_nsxlib.v3.security.NsxLibFirewallSection."
"update") as update_fw,\
mock.patch.object(self.plugin, '_get_router_interfaces',
return_value=[port]), \
mock.patch.object(self.plugin, 'get_ports',
return_value=[port]), \
mock.patch.object(self.plugin, 'get_router',
return_value=apply_list[0]), \
mock.patch.object(self.plugin, '_get_port_relay_servers',
return_value=[relay_server]),\
mock.patch.object(self.plugin.fwaas_callbacks,
'_get_router_firewall_id',
return_value=firewall['id']), \
mock.patch.object(self.plugin.fwaas_callbacks,
'_get_fw_from_plugin',
return_value=firewall):
self.firewall.create_firewall('nsx', apply_list, firewall)
# expecting 2 allow rules for the relay servers + default rule
expected_rules = expected_rules = [
{'display_name': "DHCP Relay ingress traffic",
'action': consts.FW_ACTION_ALLOW,
'destinations': None,
'sources': [{'target_id': relay_server,
'target_type': 'IPv4Address'}],
'services': self.plugin._get_port_relay_services(),
'direction': 'IN'},
{'display_name': "DHCP Relay egress traffic",
'action': consts.FW_ACTION_ALLOW,
'sources': None,
'destinations': [{'target_id': relay_server,
'target_type': 'IPv4Address'}],
'services': self.plugin._get_port_relay_services(),
'direction': 'OUT'},
self._default_rule()
]
update_fw.assert_called_once_with(
MOCK_SECTION_ID,
rules=expected_rules)

View File

@ -29,6 +29,7 @@ from vmware_nsxlib.v3 import nsx_constants as consts
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_NSX_PORT_ID = 'fake_nsx_port_uuid'
MOCK_NSX_ID = 'nsx_nsx_router_id'
MOCK_DEFAULT_RULE_ID = 'nsx_default_rule_id'
@ -191,9 +192,11 @@ class Nsxv3FwaasTestCase(test_v3_plugin.NsxV3PluginTestCaseMixin):
def test_create_firewall_no_rules(self):
apply_list = self._fake_apply_list()
firewall = self._fake_empty_firewall_group()
port = {'id': FAKE_PORT_ID}
port = {'id': FAKE_PORT_ID, 'network_id': FAKE_NET_ID}
with mock.patch.object(self.plugin, '_get_router_interfaces',
return_value=[port]),\
mock.patch.object(self.plugin, 'get_port',
return_value=port),\
mock.patch.object(self.plugin.fwaas_callbacks, 'get_port_fwg',
return_value=firewall),\
mock.patch("vmware_nsx.db.db.get_nsx_switch_and_port_id",
@ -224,9 +227,11 @@ class Nsxv3FwaasTestCase(test_v3_plugin.NsxV3PluginTestCaseMixin):
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)
port = {'id': FAKE_PORT_ID}
port = {'id': FAKE_PORT_ID, 'network_id': FAKE_NET_ID}
with mock.patch.object(self.plugin, '_get_router_interfaces',
return_value=[port]),\
mock.patch.object(self.plugin, 'get_port',
return_value=port),\
mock.patch.object(self.plugin.fwaas_callbacks, 'get_port_fwg',
return_value=firewall),\
mock.patch("vmware_nsx.db.db.get_nsx_switch_and_port_id",
@ -302,3 +307,57 @@ class Nsxv3FwaasTestCase(test_v3_plugin.NsxV3PluginTestCaseMixin):
update_fw.assert_called_once_with(
MOCK_SECTION_ID,
rules=[self._default_rule()])
def test_create_firewall_with_dhcp_relay(self):
apply_list = self._fake_apply_list()
firewall = self._fake_empty_firewall_group()
port = {'id': FAKE_PORT_ID, 'network_id': FAKE_NET_ID}
relay_server = '1.1.1.1'
with mock.patch.object(self.plugin, '_get_router_interfaces',
return_value=[port]),\
mock.patch.object(self.plugin, 'get_port',
return_value=port),\
mock.patch.object(self.plugin, '_get_port_relay_servers',
return_value=[relay_server]),\
mock.patch.object(self.plugin.fwaas_callbacks, 'get_port_fwg',
return_value=firewall),\
mock.patch("vmware_nsx.db.db.get_nsx_switch_and_port_id",
return_value=(0, FAKE_NSX_PORT_ID)),\
mock.patch("vmware_nsxlib.v3.security.NsxLibFirewallSection."
"update") as update_fw:
self.firewall.create_firewall_group('nsx', apply_list, firewall)
# expecting 2 allow rules for the relay servers,
# 2 block rules for the logical port (egress & ingress)
# and last default allow all rule
expected_rules = [
{'display_name': "DHCP Relay ingress traffic",
'action': consts.FW_ACTION_ALLOW,
'destinations': [{'target_type': 'LogicalPort',
'target_id': FAKE_NSX_PORT_ID}],
'sources': [{'target_id': relay_server,
'target_type': 'IPv4Address'}],
'services': self.plugin._get_port_relay_services(),
'direction': 'IN'},
{'display_name': "DHCP Relay egress traffic",
'action': consts.FW_ACTION_ALLOW,
'sources': [{'target_type': 'LogicalPort',
'target_id': FAKE_NSX_PORT_ID}],
'destinations': [{'target_id': relay_server,
'target_type': 'IPv4Address'}],
'services': self.plugin._get_port_relay_services(),
'direction': 'OUT'},
{'display_name': "Block port ingress",
'action': consts.FW_ACTION_DROP,
'destinations': [{'target_type': 'LogicalPort',
'target_id': FAKE_NSX_PORT_ID}],
'direction': 'IN'},
{'display_name': "Block port egress",
'action': consts.FW_ACTION_DROP,
'sources': [{'target_type': 'LogicalPort',
'target_id': FAKE_NSX_PORT_ID}],
'direction': 'OUT'},
self._default_rule()
]
update_fw.assert_called_once_with(
MOCK_SECTION_ID,
rules=expected_rules)