Co-existing between fwg and sg
The current driver is implemeted at [1], which will work in standalone mode. However, the most important function of fwaas v2 is "defense in depth". So this patch will enable fwg and sg to co-exist. That means a packet must be allowed by both of them. [1]https://review.openstack.org/#/c/447251/ Co-Authored-By: Chandan Dutta Chowdhury <chandanc@juniper.net> Change-Id: I3dc11c96637df765afa6abcc6ac9b24f942e53f7
This commit is contained in:
parent
f4e3d9cc69
commit
4d64670274
|
@ -244,7 +244,7 @@ class OVSFirewallDriver(driver_base.FirewallL2DriverBase):
|
|||
|
||||
# NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver
|
||||
def _accept_flow(self, **flow):
|
||||
for f in rules.create_accept_flows(flow):
|
||||
for f in rules.create_accept_flows(flow, self.sg_enabled):
|
||||
self._add_flow(**f)
|
||||
|
||||
def _drop_flow(self, **flow):
|
||||
|
@ -289,6 +289,9 @@ class OVSFirewallDriver(driver_base.FirewallL2DriverBase):
|
|||
# differs in constants
|
||||
def _drop_all_unmatched_flows(self):
|
||||
for table in fwaas_ovs_consts.OVS_FIREWALL_TABLES:
|
||||
if (table == fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE and
|
||||
self.sg_enabled):
|
||||
continue
|
||||
self.int_br.br.add_flow(table=table, priority=0, actions='drop')
|
||||
|
||||
# NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver
|
||||
|
@ -492,6 +495,19 @@ class OVSFirewallDriver(driver_base.FirewallL2DriverBase):
|
|||
self._initialize_egress(port)
|
||||
self._initialize_ingress(port)
|
||||
|
||||
def _fwaas_process_colocated_ingress(self, port):
|
||||
for mac_addr in port.all_allowed_macs:
|
||||
self._add_flow(
|
||||
table=ovs_consts.ACCEPT_OR_INGRESS_TABLE,
|
||||
priority=105,
|
||||
dl_dst=mac_addr,
|
||||
reg_net=port.vlan_tag,
|
||||
actions='set_field:{:d}->reg{:d},resubmit(,{:d})'.format(
|
||||
port.ofport,
|
||||
fwaas_ovs_consts.REG_PORT,
|
||||
fwaas_ovs_consts.FW_BASE_INGRESS_TABLE),
|
||||
)
|
||||
|
||||
# NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver
|
||||
# which differs in constants (table numbers)
|
||||
def _initialize_egress_ipv6_icmp(self, port):
|
||||
|
@ -615,6 +631,9 @@ class OVSFirewallDriver(driver_base.FirewallL2DriverBase):
|
|||
)
|
||||
|
||||
# DHCP discovery
|
||||
accept_or_ingress = fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE
|
||||
if self.sg_enabled:
|
||||
accept_or_ingress = ovs_consts.ACCEPT_OR_INGRESS_TABLE
|
||||
for dl_type, src_port, dst_port in (
|
||||
(constants.ETHERTYPE_IP, 68, 67),
|
||||
(constants.ETHERTYPE_IPV6, 546, 547)):
|
||||
|
@ -627,8 +646,7 @@ class OVSFirewallDriver(driver_base.FirewallL2DriverBase):
|
|||
nw_proto=lib_const.PROTO_NUM_UDP,
|
||||
tp_src=src_port,
|
||||
tp_dst=dst_port,
|
||||
actions='resubmit(,{:d})'.format(
|
||||
fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE)
|
||||
actions='resubmit(,{:d})'.format(accept_or_ingress)
|
||||
)
|
||||
# Ban dhcp service running on an instance
|
||||
for dl_type, src_port, dst_port in (
|
||||
|
@ -670,33 +688,37 @@ class OVSFirewallDriver(driver_base.FirewallL2DriverBase):
|
|||
|
||||
# Fill in accept_or_ingress table by checking that traffic is ingress
|
||||
# and if not, accept it
|
||||
for mac_addr in port.all_allowed_macs:
|
||||
if self.sg_enabled:
|
||||
self._fwaas_process_colocated_ingress(port)
|
||||
else:
|
||||
for mac_addr in port.all_allowed_macs:
|
||||
self._add_flow(
|
||||
table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE,
|
||||
priority=100,
|
||||
dl_dst=mac_addr,
|
||||
reg_net=port.vlan_tag,
|
||||
actions='set_field:{:d}->reg{:d},resubmit(,{:d})'.format(
|
||||
port.ofport,
|
||||
fwaas_ovs_consts.REG_PORT,
|
||||
fwaas_ovs_consts.FW_BASE_INGRESS_TABLE),
|
||||
)
|
||||
for ethertype in [constants.ETHERTYPE_IP,
|
||||
constants.ETHERTYPE_IPV6]:
|
||||
self._add_flow(
|
||||
table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE,
|
||||
priority=90,
|
||||
dl_type=ethertype,
|
||||
reg_port=port.ofport,
|
||||
ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED,
|
||||
actions='ct(commit,zone=NXM_NX_REG{:d}[0..15]),normal'.
|
||||
format(fwaas_ovs_consts.REG_NET)
|
||||
)
|
||||
self._add_flow(
|
||||
table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE,
|
||||
priority=100,
|
||||
dl_dst=mac_addr,
|
||||
reg_net=port.vlan_tag,
|
||||
actions='set_field:{:d}->reg{:d},resubmit(,{:d})'.format(
|
||||
port.ofport,
|
||||
fwaas_ovs_consts.REG_PORT,
|
||||
fwaas_ovs_consts.FW_BASE_INGRESS_TABLE),
|
||||
)
|
||||
for ethertype in [constants.ETHERTYPE_IP, constants.ETHERTYPE_IPV6]:
|
||||
self._add_flow(
|
||||
table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE,
|
||||
priority=90,
|
||||
dl_type=ethertype,
|
||||
priority=80,
|
||||
reg_port=port.ofport,
|
||||
ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED,
|
||||
actions='ct(commit,zone=NXM_NX_REG{:d}[0..15]),normal'.format(
|
||||
fwaas_ovs_consts.REG_NET)
|
||||
actions='normal'
|
||||
)
|
||||
self._add_flow(
|
||||
table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE,
|
||||
priority=80,
|
||||
reg_port=port.ofport,
|
||||
actions='normal'
|
||||
)
|
||||
|
||||
# NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver
|
||||
# which differs in constants (table numbers)
|
||||
|
@ -935,13 +957,17 @@ class OVSFirewallDriver(driver_base.FirewallL2DriverBase):
|
|||
# which differs in constants (table numbers)
|
||||
def delete_all_port_flows(self, port):
|
||||
"""Delete all flows for given port"""
|
||||
accept_or_ingress = fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE
|
||||
if self.sg_enabled:
|
||||
accept_or_ingress = ovs_consts.ACCEPT_OR_INGRESS_TABLE
|
||||
|
||||
for mac_addr in port.all_allowed_macs:
|
||||
self._strict_delete_flow(priority=95,
|
||||
table=ovs_consts.TRANSIENT_TABLE,
|
||||
dl_dst=mac_addr,
|
||||
dl_vlan=port.vlan_tag)
|
||||
self._delete_flows(
|
||||
table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE,
|
||||
table=accept_or_ingress,
|
||||
dl_dst=mac_addr, reg_net=port.vlan_tag)
|
||||
self._strict_delete_flow(priority=105,
|
||||
table=ovs_consts.TRANSIENT_TABLE,
|
||||
|
|
|
@ -19,6 +19,8 @@ from oslo_log import log as logging
|
|||
|
||||
from neutron.agent import firewall
|
||||
from neutron.common import utils
|
||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants \
|
||||
as ovs_consts
|
||||
from neutron_fwaas.services.firewall.drivers.linux.l2.openvswitch_firewall \
|
||||
import constants as fwaas_ovs_consts
|
||||
|
||||
|
@ -167,11 +169,24 @@ def create_icmp_flows(flow_template, rule):
|
|||
return [flow]
|
||||
|
||||
|
||||
def create_accept_flows(flow):
|
||||
def resubmit_to_sg(flow):
|
||||
if flow['table'] == fwaas_ovs_consts.FW_RULES_EGRESS_TABLE:
|
||||
flow['actions'] = 'resubmit(,{:d})'.format(
|
||||
ovs_consts.RULES_EGRESS_TABLE)
|
||||
if flow['table'] == fwaas_ovs_consts.FW_RULES_INGRESS_TABLE:
|
||||
flow['actions'] = 'resubmit(,{:d})'.format(
|
||||
ovs_consts.RULES_INGRESS_TABLE)
|
||||
|
||||
|
||||
def create_accept_flows(flow, sg_enabled=False):
|
||||
flow['ct_state'] = CT_STATES[0]
|
||||
if sg_enabled:
|
||||
resubmit_to_sg(flow)
|
||||
result = [flow.copy()]
|
||||
flow['ct_state'] = CT_STATES[1]
|
||||
if flow['table'] == fwaas_ovs_consts.FW_RULES_INGRESS_TABLE:
|
||||
if sg_enabled:
|
||||
resubmit_to_sg(flow)
|
||||
elif flow['table'] == fwaas_ovs_consts.FW_RULES_INGRESS_TABLE:
|
||||
flow['actions'] = (
|
||||
'ct(commit,zone=NXM_NX_REG{:d}[0..15]),{:s}'.format(
|
||||
fwaas_ovs_consts.REG_NET, flow['actions']))
|
||||
|
|
|
@ -447,6 +447,45 @@ class TestOVSFirewallDriver(base.BaseTestCase):
|
|||
for call in exp_ingress_classifier, exp_egress_classifier, filter_rule:
|
||||
self.assertIn(call, calls)
|
||||
|
||||
def test_prepare_port_filter_in_coexistence_mode(self):
|
||||
port_dict = {'device': 'port-id',
|
||||
'firewall_group': 1,
|
||||
'fixed_ips': [{'subnet_id': "some_subnet_id_here",
|
||||
'ip_address': "10.0.0.1"}],
|
||||
'lvlan': TESTING_VLAN_TAG}
|
||||
self._prepare_firewall_group()
|
||||
self.firewall.sg_enabled = True
|
||||
self.firewall.prepare_port_filter(port_dict)
|
||||
exp_egress_classifier = mock.call(
|
||||
actions='set_field:{:d}->reg5,set_field:{:d}->reg6,'
|
||||
'resubmit(,{:d})'.format(
|
||||
self.port_ofport, TESTING_VLAN_TAG,
|
||||
fwaas_ovs_consts.FW_BASE_EGRESS_TABLE),
|
||||
in_port=self.port_ofport,
|
||||
priority=105,
|
||||
table=ovs_consts.TRANSIENT_TABLE)
|
||||
exp_ingress_classifier = mock.call(
|
||||
actions='set_field:{:d}->reg5,set_field:{:d}->reg6,'
|
||||
'strip_vlan,resubmit(,{:d})'.format(
|
||||
self.port_ofport, TESTING_VLAN_TAG,
|
||||
fwaas_ovs_consts.FW_BASE_INGRESS_TABLE),
|
||||
dl_dst=self.port_mac,
|
||||
dl_vlan='0x%x' % TESTING_VLAN_TAG,
|
||||
priority=95,
|
||||
table=ovs_consts.TRANSIENT_TABLE)
|
||||
filter_rule = mock.call(
|
||||
actions='resubmit(,{:d})'.format(ovs_consts.RULES_INGRESS_TABLE),
|
||||
dl_type="0x{:04x}".format(n_const.ETHERTYPE_IP),
|
||||
nw_proto=constants.PROTO_NUM_TCP,
|
||||
priority=70,
|
||||
reg5=self.port_ofport,
|
||||
ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED,
|
||||
table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE,
|
||||
tcp_dst='0x007b')
|
||||
calls = self.mock_bridge.br.add_flow.call_args_list
|
||||
for call in exp_ingress_classifier, exp_egress_classifier, filter_rule:
|
||||
self.assertIn(call, calls)
|
||||
|
||||
def test_prepare_port_filter_port_security_disabled(self):
|
||||
port_dict = {'device': 'port-id',
|
||||
'firewall_group': 1,
|
||||
|
@ -504,6 +543,44 @@ class TestOVSFirewallDriver(base.BaseTestCase):
|
|||
self.mock_bridge.br.add_flow.assert_has_calls(filter_rules,
|
||||
any_order=True)
|
||||
|
||||
def test_update_port_filter_in_coexistence_mode(self):
|
||||
port_dict = {'device': 'port-id',
|
||||
'firewall_group': 1,
|
||||
'lvlan': TESTING_VLAN_TAG}
|
||||
self._prepare_firewall_group()
|
||||
self.firewall.sg_enabled = True
|
||||
self.firewall.prepare_port_filter(port_dict)
|
||||
port_dict['firewall_group'] = 2
|
||||
self.mock_bridge.reset_mock()
|
||||
|
||||
self.firewall.update_port_filter(port_dict)
|
||||
self.assertTrue(self.mock_bridge.br.delete_flows.called)
|
||||
filter_rules = [
|
||||
mock.call(
|
||||
actions='resubmit(,{:d})'.format(
|
||||
ovs_consts.RULES_EGRESS_TABLE),
|
||||
dl_type="0x{:04x}".format(n_const.ETHERTYPE_IP),
|
||||
nw_proto=constants.PROTO_NUM_UDP,
|
||||
priority=71,
|
||||
ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED,
|
||||
reg5=self.port_ofport,
|
||||
table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE),
|
||||
# XXX FIXME NOTE(ivasilevskaya) this test originally tested that
|
||||
# flows for SG with remote_group=this group were generated with
|
||||
# proper conjunction action. If the original idea that conj_manager
|
||||
# isn't needed for firewall groups proves to be wrong this needs to
|
||||
# be revizited and properly fixed/covered with tests
|
||||
mock.call(
|
||||
actions='resubmit(,{:d})'.format(
|
||||
ovs_consts.RULES_EGRESS_TABLE),
|
||||
ct_state=fwaas_ovs_consts.OF_STATE_ESTABLISHED_NOT_REPLY,
|
||||
dl_type=mock.ANY,
|
||||
nw_proto=6,
|
||||
priority=70, reg5=self.port_ofport,
|
||||
table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE)]
|
||||
self.mock_bridge.br.add_flow.assert_has_calls(filter_rules,
|
||||
any_order=True)
|
||||
|
||||
def test_update_port_filter_create_new_port_if_not_present(self):
|
||||
port_dict = {'device': 'port-id',
|
||||
'firewall_group': 1}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
prelude: >
|
||||
Coexistence between security group and firewall group.
|
||||
features:
|
||||
- L2 firewall group driver based OVS can work in coexistence mode.
|
||||
That means, if a port is associated with both firewall group and
|
||||
security group, then a packet must be allowed by both features.
|
||||
other:
|
||||
- If a port is associated with both firewall group & security group and
|
||||
there is a security group logging, which is enabled to collect ``DROP``
|
||||
events for this port, then most of invalid packets will be dropped at
|
||||
firewall group for performance reason except first dropped packet, which
|
||||
is allowed by firewall group but not accepted by security group. So not
|
||||
every dropped packet will be logged (like in case of security group
|
||||
works in standalone mode).
|
||||
|
Loading…
Reference in New Issue