Browse Source

[OVS FW] Allow egress ICMPv6 only for know addresses

Before that patch it was possible to send ICMPv6 packets like e.g.
    neutron_lib.constants.ICMPV6_TYPE_MLD_QUERY,
    neutron_lib.constants.ICMPV6_TYPE_RS,
    neutron_lib.constants.ICMPV6_TYPE_NS,
    neutron_lib.constants.ICMPV6_TYPE_NA

And that could cause some security issues as instance could advertise
that it owns IPv6 address which really don't belong to it.

Now rules in table=71 which allows that traffic are "per mac/ipaddress"
and are allowed only for fixed ips allocated to port and port's
allowed_address_pairs.

Conflicts:
    neutron/agent/linux/openvswitch_firewall/firewall.py
    neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py

Closes-Bug: #1902917
Change-Id: I4749fdc6a6cabd253b971bf4010ff76f5593c59c
(cherry picked from commit 4b5bcff64c)
changes/73/777873/1
Slawek Kaplonski 4 months ago
parent
commit
7b1f0dd1ae
3 changed files with 68 additions and 25 deletions
  1. +10
    -10
      doc/source/contributor/internals/openvswitch_firewall.rst
  2. +19
    -14
      neutron/agent/linux/openvswitch_firewall/firewall.py
  3. +39
    -1
      neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py

+ 10
- 10
doc/source/contributor/internals/openvswitch_firewall.rst View File

@ -208,16 +208,16 @@ solicitation and neighbour advertisement.
::
table=71, priority=95,icmp6,reg5=0x1,in_port=1,icmp_type=130 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x1,in_port=1,icmp_type=131 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x1,in_port=1,icmp_type=132 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x1,in_port=1,icmp_type=135 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x1,in_port=1,icmp_type=136 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x2,in_port=2,icmp_type=130 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x2,in_port=2,icmp_type=131 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x2,in_port=2,icmp_type=132 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x2,in_port=2,icmp_type=135 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x2,in_port=2,icmp_type=136 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:11,ipv6_src=fe80::11,icmp_type=130 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:11,ipv6_src=fe80::11,icmp_type=131 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:11,ipv6_src=fe80::11,icmp_type=132 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:11,ipv6_src=fe80::11,icmp_type=135 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x1,in_port=1,dl_src=fa:16:3e:a4:22:11,ipv6_src=fe80::11,icmp_type=136 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x2,in_port=2,dl_src=fa:16:3e:a4:22:22,ipv6_src=fe80::22,icmp_type=130 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x2,in_port=2,dl_src=fa:16:3e:a4:22:22,ipv6_src=fe80::22,icmp_type=131 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x2,in_port=2,dl_src=fa:16:3e:a4:22:22,ipv6_src=fe80::22,icmp_type=132 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x2,in_port=2,dl_src=fa:16:3e:a4:22:22,ipv6_src=fe80::22,icmp_type=135 actions=resubmit(,94)
table=71, priority=95,icmp6,reg5=0x2,in_port=2,dl_src=fa:16:3e:a4:22:22,ipv6_src=fe80::22,icmp_type=136 actions=resubmit(,94)
Following rules implement arp spoofing protection


+ 19
- 14
neutron/agent/linux/openvswitch_firewall/firewall.py View File

@ -880,19 +880,24 @@ class OVSFirewallDriver(firewall.FirewallDriver):
self._initialize_egress(port)
self._initialize_ingress(port)
def _initialize_egress_ipv6_icmp(self, port):
for icmp_type in firewall.ICMPV6_ALLOWED_EGRESS_TYPES:
self._add_flow(
table=ovs_consts.BASE_EGRESS_TABLE,
priority=95,
in_port=port.ofport,
reg_port=port.ofport,
dl_type=constants.ETHERTYPE_IPV6,
nw_proto=lib_const.PROTO_NUM_IPV6_ICMP,
icmp_type=icmp_type,
actions='resubmit(,%d)' % (
ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)
)
def _initialize_egress_ipv6_icmp(self, port, allowed_pairs):
# NOTE(slaweq): should we include also fe80::/64 (link-local) subnet
# in the allowed pairs here?
for mac_addr, ip_addr in allowed_pairs:
for icmp_type in firewall.ICMPV6_ALLOWED_EGRESS_TYPES:
self._add_flow(
table=ovs_consts.BASE_EGRESS_TABLE,
priority=95,
in_port=port.ofport,
reg_port=port.ofport,
dl_type=constants.ETHERTYPE_IPV6,
nw_proto=lib_const.PROTO_NUM_IPV6_ICMP,
icmp_type=icmp_type,
dl_src=mac_addr,
ipv6_src=ip_addr,
actions='resubmit(,%d)' % (
ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)
)
def _initialize_egress_no_port_security(self, port_id, ovs_ports=None):
try:
@ -965,7 +970,6 @@ class OVSFirewallDriver(firewall.FirewallDriver):
def _initialize_egress(self, port):
"""Identify egress traffic and send it to egress base"""
self._initialize_egress_ipv6_icmp(port)
# Apply mac/ip pairs for IPv4
allowed_pairs = port.allowed_pairs_v4.union(
@ -998,6 +1002,7 @@ class OVSFirewallDriver(firewall.FirewallDriver):
# Apply mac/ip pairs for IPv6
allowed_pairs = port.allowed_pairs_v6.union(
{(port.mac, ip_addr) for ip_addr in port.ipv6_addresses})
self._initialize_egress_ipv6_icmp(port, allowed_pairs)
for mac_addr, ip_addr in allowed_pairs:
self._add_flow(
table=ovs_consts.BASE_EGRESS_TABLE,


+ 39
- 1
neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py View File

@ -39,8 +39,14 @@ TESTING_SEGMENT = 1000
def create_ofport(port_dict, network_type=None, physical_network=None):
allowed_pairs_v4 = ovsfw.OFPort._get_allowed_pairs(
port_dict, version=constants.IPv4)
allowed_pairs_v6 = ovsfw.OFPort._get_allowed_pairs(
port_dict, version=constants.IPv6)
ovs_port = mock.Mock(vif_mac='00:00:00:00:00:00', ofport=1,
port_name="port-name")
port_name="port-name",
allowed_pairs_v4=allowed_pairs_v4,
allowed_pairs_v6=allowed_pairs_v6)
return ovsfw.OFPort(port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG,
segment_id=TESTING_SEGMENT,
network_type=network_type,
@ -937,6 +943,38 @@ class TestOVSFirewallDriver(base.BaseTestCase):
with testtools.ExpectedException(exceptions.OVSFWPortNotHandled):
self.firewall._remove_egress_no_port_security('foo')
def test__initialize_egress_ipv6_icmp(self):
port_dict = {
'device': 'port-id',
'security_groups': [1],
'fixed_ips': ["10.0.0.1"],
'allowed_address_pairs': [
{'mac_address': 'aa:bb:cc:dd:ee:ff',
'ip_address': '192.168.1.1'},
{'mac_address': 'aa:bb:cc:dd:ee:ff',
'ip_address': '2003::1'}
]}
of_port = create_ofport(port_dict)
self.mock_bridge.br.db_get_val.return_value = {'tag': TESTING_VLAN_TAG}
self.firewall._initialize_egress_ipv6_icmp(
of_port, set([('aa:bb:cc:dd:ee:ff', '2003::1')]))
expected_calls = []
for icmp_type in firewall.ICMPV6_ALLOWED_EGRESS_TYPES:
expected_calls.append(
mock.call(
table=ovs_consts.BASE_EGRESS_TABLE,
priority=95,
in_port=TESTING_VLAN_TAG,
reg5=TESTING_VLAN_TAG,
dl_type='0x86dd',
nw_proto=constants.PROTO_NUM_IPV6_ICMP,
icmp_type=icmp_type,
dl_src='aa:bb:cc:dd:ee:ff',
ipv6_src='2003::1',
actions='resubmit(,%d)' % (
ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)))
self.mock_bridge.br.add_flow.assert_has_calls(expected_calls)
def test_process_trusted_ports_caches_port_id(self):
vif_port = ovs_lib.VifPort('name', 1, 'id', 'mac', mock.ANY)
with mock.patch.object(self.firewall.int_br.br, 'get_vifs_by_ids',


Loading…
Cancel
Save