[ovs fw] Restrict IPv6 NA and DHCP(v6) IP and MAC source addresses
Neighbor Advertisments are used to inform other machines of the MAC
address to use to reach an IPv6. This commits prevents VMs from
pretending they are assigned IPv6 they should not use.
It also prevents sending UDP packets with spoofed IP or MAC even using
DHCP(v6) request ports.
Co-authored-by: David Sinquin <david.sinquin@gandi.net>
Closes-bug: #1902917
Conflicts:
neutron/agent/linux/openvswitch_firewall/firewall.py
Change-Id: Iffb6643359562487414460f5a7e19a7fae9f935c
(cherry picked from commit ca7822e210
)
This commit is contained in:
parent
3e818718b6
commit
ccea0021bc
@ -39,8 +39,11 @@ ICMPV6_ALLOWED_INGRESS_TYPES = (n_const.ICMPV6_TYPE_MLD_QUERY,
|
|||||||
# List of ICMPv6 types that should be permitted (egress) by default.
|
# List of ICMPv6 types that should be permitted (egress) by default.
|
||||||
ICMPV6_ALLOWED_EGRESS_TYPES = (n_const.ICMPV6_TYPE_MLD_QUERY,
|
ICMPV6_ALLOWED_EGRESS_TYPES = (n_const.ICMPV6_TYPE_MLD_QUERY,
|
||||||
n_const.ICMPV6_TYPE_RS,
|
n_const.ICMPV6_TYPE_RS,
|
||||||
n_const.ICMPV6_TYPE_NS,
|
n_const.ICMPV6_TYPE_NS)
|
||||||
n_const.ICMPV6_TYPE_NA)
|
|
||||||
|
# List of ICMPv6 types that should be permitted depending on payload content
|
||||||
|
# to avoid spoofing (egress) by default.
|
||||||
|
ICMPV6_RESTRICTED_EGRESS_TYPES = (n_const.ICMPV6_TYPE_NA, )
|
||||||
|
|
||||||
|
|
||||||
def port_sec_enabled(port):
|
def port_sec_enabled(port):
|
||||||
|
@ -892,8 +892,7 @@ class OVSFirewallDriver(firewall.FirewallDriver):
|
|||||||
self._initialize_ingress(port)
|
self._initialize_ingress(port)
|
||||||
|
|
||||||
def _initialize_egress_ipv6_icmp(self, port, allowed_pairs):
|
def _initialize_egress_ipv6_icmp(self, port, allowed_pairs):
|
||||||
# NOTE(slaweq): should we include also fe80::/64 (link-local) subnet
|
allowed_pairs = allowed_pairs.union({(port.mac, port.lla_address)})
|
||||||
# in the allowed pairs here?
|
|
||||||
for mac_addr, ip_addr in allowed_pairs:
|
for mac_addr, ip_addr in allowed_pairs:
|
||||||
for icmp_type in firewall.ICMPV6_ALLOWED_EGRESS_TYPES:
|
for icmp_type in firewall.ICMPV6_ALLOWED_EGRESS_TYPES:
|
||||||
self._add_flow(
|
self._add_flow(
|
||||||
@ -909,6 +908,19 @@ class OVSFirewallDriver(firewall.FirewallDriver):
|
|||||||
actions='resubmit(,%d)' % (
|
actions='resubmit(,%d)' % (
|
||||||
ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)
|
ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)
|
||||||
)
|
)
|
||||||
|
for icmp_type in firewall.ICMPV6_RESTRICTED_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,
|
||||||
|
nd_target=ip_addr,
|
||||||
|
actions='resubmit(,%d)' % (
|
||||||
|
ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)
|
||||||
|
)
|
||||||
|
|
||||||
def _initialize_egress_no_port_security(self, port_id, ovs_ports=None):
|
def _initialize_egress_no_port_security(self, port_id, ovs_ports=None):
|
||||||
try:
|
try:
|
||||||
@ -983,9 +995,9 @@ class OVSFirewallDriver(firewall.FirewallDriver):
|
|||||||
"""Identify egress traffic and send it to egress base"""
|
"""Identify egress traffic and send it to egress base"""
|
||||||
|
|
||||||
# Apply mac/ip pairs for IPv4
|
# Apply mac/ip pairs for IPv4
|
||||||
allowed_pairs = port.allowed_pairs_v4.union(
|
allowed_mac_ipv4_pairs = port.allowed_pairs_v4.union(
|
||||||
{(port.mac, ip_addr) for ip_addr in port.ipv4_addresses})
|
{(port.mac, ip_addr) for ip_addr in port.ipv4_addresses})
|
||||||
for mac_addr, ip_addr in allowed_pairs:
|
for mac_addr, ip_addr in allowed_mac_ipv4_pairs:
|
||||||
self._add_flow(
|
self._add_flow(
|
||||||
table=ovs_consts.BASE_EGRESS_TABLE,
|
table=ovs_consts.BASE_EGRESS_TABLE,
|
||||||
priority=95,
|
priority=95,
|
||||||
@ -1011,10 +1023,10 @@ class OVSFirewallDriver(firewall.FirewallDriver):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Apply mac/ip pairs for IPv6
|
# Apply mac/ip pairs for IPv6
|
||||||
allowed_pairs = port.allowed_pairs_v6.union(
|
allowed_mac_ipv6_pairs = port.allowed_pairs_v6.union(
|
||||||
{(port.mac, ip_addr) for ip_addr in port.ipv6_addresses})
|
{(port.mac, ip_addr) for ip_addr in port.ipv6_addresses})
|
||||||
self._initialize_egress_ipv6_icmp(port, allowed_pairs)
|
self._initialize_egress_ipv6_icmp(port, allowed_mac_ipv6_pairs)
|
||||||
for mac_addr, ip_addr in allowed_pairs:
|
for mac_addr, ip_addr in allowed_mac_ipv6_pairs:
|
||||||
self._add_flow(
|
self._add_flow(
|
||||||
table=ovs_consts.BASE_EGRESS_TABLE,
|
table=ovs_consts.BASE_EGRESS_TABLE,
|
||||||
priority=65,
|
priority=65,
|
||||||
@ -1029,9 +1041,18 @@ class OVSFirewallDriver(firewall.FirewallDriver):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# DHCP discovery
|
# DHCP discovery
|
||||||
for dl_type, src_port, dst_port in (
|
additional_ipv4_filters = [
|
||||||
(constants.ETHERTYPE_IP, 68, 67),
|
{"dl_src": mac, "nw_src": ip}
|
||||||
(constants.ETHERTYPE_IPV6, 546, 547)):
|
for mac, ip in allowed_mac_ipv4_pairs]
|
||||||
|
additional_ipv4_filters.append(
|
||||||
|
{"dl_src": port.mac, "nw_src": "0.0.0.0"})
|
||||||
|
additional_ipv6_filters = [
|
||||||
|
{"dl_src": mac, "ipv6_src": ip}
|
||||||
|
for mac, ip in allowed_mac_ipv6_pairs]
|
||||||
|
for dl_type, src_port, dst_port, filters_list in (
|
||||||
|
(constants.ETHERTYPE_IP, 68, 67, additional_ipv4_filters),
|
||||||
|
(constants.ETHERTYPE_IPV6, 546, 547, additional_ipv6_filters)):
|
||||||
|
for additional_filters in filters_list:
|
||||||
self._add_flow(
|
self._add_flow(
|
||||||
table=ovs_consts.BASE_EGRESS_TABLE,
|
table=ovs_consts.BASE_EGRESS_TABLE,
|
||||||
priority=80,
|
priority=80,
|
||||||
@ -1042,7 +1063,8 @@ class OVSFirewallDriver(firewall.FirewallDriver):
|
|||||||
tp_src=src_port,
|
tp_src=src_port,
|
||||||
tp_dst=dst_port,
|
tp_dst=dst_port,
|
||||||
actions='resubmit(,{:d})'.format(
|
actions='resubmit(,{:d})'.format(
|
||||||
ovs_consts.ACCEPT_OR_INGRESS_TABLE)
|
ovs_consts.ACCEPT_OR_INGRESS_TABLE),
|
||||||
|
**additional_filters
|
||||||
)
|
)
|
||||||
# Ban dhcp service running on an instance
|
# Ban dhcp service running on an instance
|
||||||
for dl_type, src_port, dst_port in (
|
for dl_type, src_port, dst_port in (
|
||||||
|
@ -994,6 +994,19 @@ class TestOVSFirewallDriver(base.BaseTestCase):
|
|||||||
ipv6_src='2003::1',
|
ipv6_src='2003::1',
|
||||||
actions='resubmit(,%d)' % (
|
actions='resubmit(,%d)' % (
|
||||||
ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)))
|
ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)))
|
||||||
|
for icmp_type in firewall.ICMPV6_RESTRICTED_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,
|
||||||
|
nd_target='2003::1',
|
||||||
|
actions='resubmit(,%d)' % (
|
||||||
|
ovs_consts.ACCEPTED_EGRESS_TRAFFIC_NORMAL_TABLE)))
|
||||||
self.mock_bridge.br.add_flow.assert_has_calls(expected_calls)
|
self.mock_bridge.br.add_flow.assert_has_calls(expected_calls)
|
||||||
|
|
||||||
def test_process_trusted_ports_caches_port_id(self):
|
def test_process_trusted_ports_caches_port_id(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user