diff -Nru neutron-12.1.1/debian/changelog neutron-12.1.1/debian/changelog --- neutron-12.1.1/debian/changelog 2021-04-26 17:01:49.000000000 +0000 +++ neutron-12.1.1/debian/changelog 2023-04-18 15:23:51.000000000 +0000 @@ -1,3 +1,39 @@ +neutron (2:12.1.1-0ubuntu8.1) bionic-security; urgency=medium + + * SECURITY UPDATE: IPv6 impersonation in Open vSwitch firewall rules + - debian/patches/CVE-2021-20267-1.patch: allow egress ICMPv6 only for + known addresses in + doc/source/contributor/internals/openvswitch_firewall.rst, + neutron/agent/linux/openvswitch_firewall/firewall.py, + neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py. + - debian/patches/CVE-2021-20267-2.patch: restrict IPv6 NA and DHCP(v6) + IP and MAC source addresses in neutron/agent/firewall.py, + neutron/agent/linux/openvswitch_firewall/firewall.py, + neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py. + - CVE-2021-20267 + * SECURITY UPDATE: hardware address impersonation with ebtables-nft + - debian/patches/CVE-2021-38598.patch: make ARP protection commands + compatible with "ebtables-nft" in + neutron/plugins/ml2/drivers/linuxbridge/agent/arp_protect.py, + neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_arp_protect.py. + - CVE-2021-38598 + * SECURITY UPDATE: dnsmasq reconfiguration issue + - debian/patches/CVE-2021-40085.patch: remove dhcp_extra_opt value + after first newline character in neutron/agent/linux/dhcp.py, + neutron/tests/unit/agent/linux/test_dhcp.py. + - CVE-2021-40085 + * SECURITY UPDATE: memory consumption via API requests + - debian/patches/CVE-2021-40797.patch: don't use singleton in + routes.middleware.RoutesMiddleware in neutron/api/extensions.py. + - CVE-2021-40797 + * SECURITY UPDATE: uncontrolled resource consumption flaw + - debian/patches/CVE-2022-3277.patch: do not allow a tenant to create a + default SG for another one in neutron/db/securitygroups_db.py, + neutron/tests/unit/db/test_securitygroups_db.py. + - CVE-2022-3277 + + -- Marc Deslauriers Tue, 18 Apr 2023 11:23:51 -0400 + neutron (2:12.1.1-0ubuntu8) bionic; urgency=medium * Backport fix for TCP checksum issue (LP: #1832021) diff -Nru neutron-12.1.1/debian/patches/CVE-2021-20267-1.patch neutron-12.1.1/debian/patches/CVE-2021-20267-1.patch --- neutron-12.1.1/debian/patches/CVE-2021-20267-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ neutron-12.1.1/debian/patches/CVE-2021-20267-1.patch 2023-04-18 15:23:51.000000000 +0000 @@ -0,0 +1,175 @@ +Backport of: + +From 7b1f0dd1aed68f98b5472c2dfa821e9338f286f0 Mon Sep 17 00:00:00 2001 +From: Slawek Kaplonski +Date: Fri, 19 Feb 2021 11:35:02 +0100 +Subject: [PATCH] [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 4b5bcff64c4725b37f094dfd2613ec58f723d304) +--- + .../internals/openvswitch_firewall.rst | 20 +++++----- + .../linux/openvswitch_firewall/firewall.py | 33 ++++++++------- + .../openvswitch_firewall/test_firewall.py | 40 ++++++++++++++++++- + 3 files changed, 68 insertions(+), 25 deletions(-) + +--- a/doc/source/contributor/internals/openvswitch_firewall.rst ++++ b/doc/source/contributor/internals/openvswitch_firewall.rst +@@ -206,16 +206,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 + +--- a/neutron/agent/linux/openvswitch_firewall/firewall.py ++++ b/neutron/agent/linux/openvswitch_firewall/firewall.py +@@ -770,19 +770,24 @@ class OVSFirewallDriver(firewall.Firewal + 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): + try: +@@ -837,7 +842,6 @@ class OVSFirewallDriver(firewall.Firewal + + 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( +@@ -870,6 +874,7 @@ class OVSFirewallDriver(firewall.Firewal + # 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, +--- a/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py ++++ b/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py +@@ -36,8 +36,14 @@ TESTING_VLAN_TAG = 1 + + + def create_ofport(port_dict): ++ 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) + + +@@ -764,6 +770,38 @@ class TestOVSFirewallDriver(base.BaseTes + 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): + self.firewall.process_trusted_ports(['port_id']) + self.assertIn('port_id', self.firewall.sg_port_map.unfiltered) diff -Nru neutron-12.1.1/debian/patches/CVE-2021-20267-2.patch neutron-12.1.1/debian/patches/CVE-2021-20267-2.patch --- neutron-12.1.1/debian/patches/CVE-2021-20267-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ neutron-12.1.1/debian/patches/CVE-2021-20267-2.patch 2023-04-18 15:23:51.000000000 +0000 @@ -0,0 +1,171 @@ +From ccea0021bc49eefde9af6568942159b52588daae Mon Sep 17 00:00:00 2001 +From: Slawek Kaplonski +Date: Mon, 29 Mar 2021 22:21:15 +0200 +Subject: [PATCH] [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 + +Closes-bug: #1902917 + +Conflicts: + neutron/agent/linux/openvswitch_firewall/firewall.py + +Change-Id: Iffb6643359562487414460f5a7e19a7fae9f935c +(cherry picked from commit ca7822e2108c151bda992ef8a6d454ec2c6d890e) +--- + neutron/agent/firewall.py | 7 +- + .../linux/openvswitch_firewall/firewall.py | 66 ++++++++++++------- + .../openvswitch_firewall/test_firewall.py | 13 ++++ + 3 files changed, 62 insertions(+), 24 deletions(-) + +--- a/neutron/agent/firewall.py ++++ b/neutron/agent/firewall.py +@@ -39,8 +39,11 @@ ICMPV6_ALLOWED_INGRESS_TYPES = (n_const. + # List of ICMPv6 types that should be permitted (egress) by default. + ICMPV6_ALLOWED_EGRESS_TYPES = (n_const.ICMPV6_TYPE_MLD_QUERY, + n_const.ICMPV6_TYPE_RS, +- n_const.ICMPV6_TYPE_NS, +- n_const.ICMPV6_TYPE_NA) ++ n_const.ICMPV6_TYPE_NS) ++ ++# 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): +--- a/neutron/agent/linux/openvswitch_firewall/firewall.py ++++ b/neutron/agent/linux/openvswitch_firewall/firewall.py +@@ -771,8 +771,7 @@ class OVSFirewallDriver(firewall.Firewal + self._initialize_ingress(port) + + 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? ++ allowed_pairs = allowed_pairs.union({(port.mac, port.lla_address)}) + for mac_addr, ip_addr in allowed_pairs: + for icmp_type in firewall.ICMPV6_ALLOWED_EGRESS_TYPES: + self._add_flow( +@@ -788,6 +787,19 @@ class OVSFirewallDriver(firewall.Firewal + actions='resubmit(,%d)' % ( + 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): + try: +@@ -844,9 +856,9 @@ class OVSFirewallDriver(firewall.Firewal + """Identify egress traffic and send it to egress base""" + + # 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}) +- for mac_addr, ip_addr in allowed_pairs: ++ for mac_addr, ip_addr in allowed_mac_ipv4_pairs: + self._add_flow( + table=ovs_consts.BASE_EGRESS_TABLE, + priority=95, +@@ -872,10 +884,10 @@ class OVSFirewallDriver(firewall.Firewal + ) + + # 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}) +- self._initialize_egress_ipv6_icmp(port, allowed_pairs) +- for mac_addr, ip_addr in allowed_pairs: ++ self._initialize_egress_ipv6_icmp(port, allowed_mac_ipv6_pairs) ++ for mac_addr, ip_addr in allowed_mac_ipv6_pairs: + self._add_flow( + table=ovs_consts.BASE_EGRESS_TABLE, + priority=65, +@@ -890,21 +902,31 @@ class OVSFirewallDriver(firewall.Firewal + ) + + # DHCP discovery +- for dl_type, src_port, dst_port in ( +- (constants.ETHERTYPE_IP, 68, 67), +- (constants.ETHERTYPE_IPV6, 546, 547)): +- self._add_flow( +- table=ovs_consts.BASE_EGRESS_TABLE, +- priority=80, +- reg_port=port.ofport, +- in_port=port.ofport, +- dl_type=dl_type, +- nw_proto=lib_const.PROTO_NUM_UDP, +- tp_src=src_port, +- tp_dst=dst_port, +- actions='resubmit(,{:d})'.format( +- ovs_consts.ACCEPT_OR_INGRESS_TABLE) +- ) ++ additional_ipv4_filters = [ ++ {"dl_src": mac, "nw_src": ip} ++ 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( ++ table=ovs_consts.BASE_EGRESS_TABLE, ++ priority=80, ++ reg_port=port.ofport, ++ in_port=port.ofport, ++ dl_type=dl_type, ++ nw_proto=lib_const.PROTO_NUM_UDP, ++ tp_src=src_port, ++ tp_dst=dst_port, ++ actions='resubmit(,{:d})'.format( ++ ovs_consts.ACCEPT_OR_INGRESS_TABLE), ++ **additional_filters ++ ) + # Ban dhcp service running on an instance + for dl_type, src_port, dst_port in ( + (constants.ETHERTYPE_IP, 67, 68), +--- a/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py ++++ b/neutron/tests/unit/agent/linux/openvswitch_firewall/test_firewall.py +@@ -800,6 +800,19 @@ class TestOVSFirewallDriver(base.BaseTes + ipv6_src='2003::1', + actions='resubmit(,%d)' % ( + 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) + + def test_process_trusted_ports_caches_port_id(self): diff -Nru neutron-12.1.1/debian/patches/CVE-2021-38598.patch neutron-12.1.1/debian/patches/CVE-2021-38598.patch --- neutron-12.1.1/debian/patches/CVE-2021-38598.patch 1970-01-01 00:00:00.000000000 +0000 +++ neutron-12.1.1/debian/patches/CVE-2021-38598.patch 2023-04-18 15:16:11.000000000 +0000 @@ -0,0 +1,226 @@ +Backport of: + +From 4245963c71280bebd94e9005230d610931a7c2df Mon Sep 17 00:00:00 2001 +From: Rodolfo Alonso Hernandez +Date: Wed, 7 Apr 2021 13:16:21 +0000 +Subject: [PATCH] Make ARP protection commands compatible with "ebtables-nft" + +"nftables" compatible binary, "ebtables-nft", is not 100% compatible +with the legacy API, as reported in LP#1922892. + +This patch fixes the following issues when using "ebtables-nft" (while +keeping compatibility with legacy binary): +- When a new chain is created, a default DROP rule is added at the end + of the chain (append). This will prevent the error code 4 when the + chain is listed. +- The chain rules are added at the begining of the chain (insert), + before the default DROP rule. This will prioritize the port rules. +- The MAC rules are cleaned before the new ones are added. That will + prevent the deletion of any new needed rule, now added after the + deletion. +- The "ebtables" command will retry on error code 4. This is the + error returned when the chains are listed and no rule is present + in a new created chain (reporeted in LP#1922892). + +This code is backwards compatible, that means it works with the legacy +"ebtables" binary; this is currently installed in the Neutron CI [1]. +In order to test with the new binary, "ebtables-nft", two new CI jobs +are added to the periodic queue [2]. + +[1]https://github.com/openstack/neutron/blob/1ad9ca56b07ffdc9f7e0bc6a62af61961b9128eb/roles/legacy_ebtables/tasks/main.yaml +[2]https://review.opendev.org/c/openstack/neutron/+/785144 + +Closes-Bug: #1922892 +Related-Bug: #1508155 +Closes-Bug: #1938670 + +Conflicts: + neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_arp_protect.py + +Change-Id: I9463b000f6f63e65aaf91d60b30f6c92c01e3baf +(cherry picked from commit 0a931391d8990f3e654b4bfda24ae4119c609bbf) +(cherry picked from commit fafa5dacd5057120562184a734e7345e7c0e9639) +--- + .../drivers/linuxbridge/agent/arp_protect.py | 54 +++++++++---------- + .../linuxbridge/agent/test_arp_protect.py | 44 ++++----------- + 2 files changed, 35 insertions(+), 63 deletions(-) + +--- a/neutron/plugins/ml2/drivers/linuxbridge/agent/arp_protect.py ++++ b/neutron/plugins/ml2/drivers/linuxbridge/agent/arp_protect.py +@@ -73,12 +73,6 @@ def delete_arp_spoofing_protection(vifs) + _delete_arp_spoofing_protection(vifs, current_rules, table='nat', + chain='PREROUTING') + +- # TODO(haleyb) this can go away in "R" cycle, it's here to cleanup +- # old chains in the filter table +- current_rules = ebtables(['-L'], table='filter').splitlines() +- _delete_arp_spoofing_protection(vifs, current_rules, table='filter', +- chain='FORWARD') +- + + def _delete_arp_spoofing_protection(vifs, current_rules, table, chain): + # delete the jump rule and then delete the whole chain +@@ -93,10 +87,11 @@ def _delete_arp_spoofing_protection(vifs + chain=chain) + + +-def _delete_unreferenced_arp_protection(current_vifs, table, chain): ++@lockutils.synchronized('ebtables') ++def delete_unreferenced_arp_protection(current_vifs): + # deletes all jump rules and chains that aren't in current_vifs but match + # the spoof prefix +- current_rules = ebtables(['-L'], table=table).splitlines() ++ current_rules = ebtables(['-L'], table='nat').splitlines() + to_delete = [] + for line in current_rules: + # we're looking to find and turn the following: +@@ -108,19 +103,8 @@ def _delete_unreferenced_arp_protection( + to_delete.append(devname) + LOG.info("Clearing orphaned ARP spoofing entries for devices %s", + to_delete) +- _delete_arp_spoofing_protection(to_delete, current_rules, table=table, +- chain=chain) +- +- +-@lockutils.synchronized('ebtables') +-def delete_unreferenced_arp_protection(current_vifs): +- _delete_unreferenced_arp_protection(current_vifs, +- table='nat', chain='PREROUTING') +- +- # TODO(haleyb) this can go away in "R" cycle, it's here to cleanup +- # old chains in the filter table +- _delete_unreferenced_arp_protection(current_vifs, +- table='filter', chain='FORWARD') ++ _delete_arp_spoofing_protection(to_delete, current_rules, table='nat', ++ chain='PREROUTING') + + + @lockutils.synchronized('ebtables') +@@ -134,12 +118,17 @@ def _install_arp_spoofing_protection(vif + vif_chain = chain_name(vif) + if not chain_exists(vif_chain, current_rules): + ebtables(['-N', vif_chain, '-P', 'DROP']) +- # flush the chain to clear previous accepts. this will cause dropped ARP +- # packets until the allows are installed, but that's better than leaked +- # spoofed packets and ARP can handle losses. +- ebtables(['-F', vif_chain]) ++ # Append a default DROP rule at the end of the chain. This will ++ # avoid "ebtables-nft" error when listing the chain. ++ ebtables(['-A', vif_chain, '-j', 'DROP']) ++ else: ++ # Flush the chain to clear previous accepts. This will cause dropped ++ # ARP packets until the allows are installed, but that's better than ++ # leaked spoofed packets and ARP can handle losses. ++ ebtables(['-F', vif_chain]) ++ ebtables(['-A', vif_chain, '-j', 'DROP']) + for addr in sorted(addresses): +- ebtables(['-A', vif_chain, '-p', 'ARP', '--arp-ip-src', addr, ++ ebtables(['-I', vif_chain, '-p', 'ARP', '--arp-ip-src', addr, + '-j', 'ACCEPT']) + # check if jump rule already exists, if not, install it + if not vif_jump_present(vif, current_rules): +@@ -172,17 +161,22 @@ def _install_mac_spoofing_protection(vif + # mac filter chain for each vif which has a default deny + if not chain_exists(vif_chain, current_rules): + ebtables(['-N', vif_chain, '-P', 'DROP']) ++ # Append a default DROP rule at the end of the chain. This will ++ # avoid "ebtables-nft" error when listing the chain. ++ ebtables(['-A', vif_chain, '-j', 'DROP']) ++ + # check if jump rule already exists, if not, install it + if not _mac_vif_jump_present(vif, current_rules): +- ebtables(['-A', 'PREROUTING', '-i', vif, '-j', vif_chain]) ++ ebtables(['-I', 'PREROUTING', '-i', vif, '-j', vif_chain]) ++ ++ _delete_vif_mac_rules(vif, current_rules) + # we can't just feed all allowed macs at once because we can exceed + # the maximum argument size. limit to 500 per rule. + for chunk in (mac_addresses[i:i + 500] + for i in range(0, len(mac_addresses), 500)): +- new_rule = ['-A', vif_chain, '-i', vif, ++ new_rule = ['-I', vif_chain, '-i', vif, + '--among-src', ','.join(sorted(chunk)), '-j', 'RETURN'] + ebtables(new_rule) +- _delete_vif_mac_rules(vif, current_rules) + + + def _mac_vif_jump_present(vif, current_rules): +@@ -223,7 +217,7 @@ NAMESPACE = None + + @tenacity.retry( + wait=tenacity.wait_exponential(multiplier=0.01), +- retry=tenacity.retry_if_exception(lambda e: e.returncode == 255), ++ retry=tenacity.retry_if_exception(lambda e: e.returncode in [255, 4]), + reraise=True + ) + def ebtables(comm, table='nat'): +--- a/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_arp_protect.py ++++ b/neutron/tests/unit/plugins/ml2/drivers/linuxbridge/agent/test_arp_protect.py +@@ -72,31 +72,35 @@ class TestLinuxBridgeARPSpoofing(base.Ba + 'neutronMAC-%s' % vif, '-P', 'DROP'], + check_exit_code=True, extra_ok_codes=None, + log_fail_as_error=True, run_as_root=True), +- mock.ANY, + mock.call(['ebtables', '-t', 'nat', '--concurrent', '-A', ++ 'neutronMAC-%s' % vif, '-j', 'DROP'], ++ check_exit_code=True, extra_ok_codes=None, ++ log_fail_as_error=True, run_as_root=True), ++ mock.ANY, ++ mock.call(['ebtables', '-t', 'nat', '--concurrent', '-I', + 'PREROUTING', '-i', vif, '-j', mac_chain], + check_exit_code=True, extra_ok_codes=None, + log_fail_as_error=True, run_as_root=True), +- mock.call(['ebtables', '-t', 'nat', '--concurrent', '-A', ++ mock.ANY, ++ mock.call(['ebtables', '-t', 'nat', '--concurrent', '-I', + mac_chain, '-i', vif, + '--among-src', '%s' % ','.join(sorted(mac_addresses)), + '-j', 'RETURN'], + check_exit_code=True, extra_ok_codes=None, + log_fail_as_error=True, run_as_root=True), + mock.ANY, +- mock.ANY, + mock.call(['ebtables', '-t', 'nat', '--concurrent', '-N', + spoof_chain, '-P', 'DROP'], + check_exit_code=True, extra_ok_codes=None, + log_fail_as_error=True, run_as_root=True), +- mock.call(['ebtables', '-t', 'nat', '--concurrent', '-F', +- spoof_chain], ++ mock.call(['ebtables', '-t', 'nat', '--concurrent', '-A', ++ spoof_chain, '-j', 'DROP'], + check_exit_code=True, extra_ok_codes=None, +- log_fail_as_error=True, run_as_root=True), ++ log_fail_as_error=True, run_as_root=True) + ] + for addr in sorted(ip_addresses): + expected.extend([ +- mock.call(['ebtables', '-t', 'nat', '--concurrent', '-A', ++ mock.call(['ebtables', '-t', 'nat', '--concurrent', '-I', + spoof_chain, '-p', 'ARP', + '--arp-ip-src', addr, '-j', 'ACCEPT'], + check_exit_code=True, extra_ok_codes=None, +@@ -144,24 +148,6 @@ class TestLinuxBridgeARPSpoofing(base.Ba + mac_chain], + check_exit_code=True, extra_ok_codes=None, + log_fail_as_error=True, run_as_root=True), +- mock.call(['ebtables', '-t', 'filter', '--concurrent', '-L'], +- check_exit_code=True, extra_ok_codes=None, +- log_fail_as_error=True, run_as_root=True), +- mock.ANY, +- mock.call(['ebtables', '-t', 'filter', '--concurrent', '-D', +- 'FORWARD', '-i', VIF, '-j', spoof_chain, +- '-p', 'ARP'], +- check_exit_code=True, extra_ok_codes=None, +- log_fail_as_error=True, run_as_root=True), +- mock.call(['ebtables', '-t', 'filter', '--concurrent', '-X', +- spoof_chain], +- check_exit_code=True, extra_ok_codes=None, +- log_fail_as_error=True, run_as_root=True), +- mock.ANY, +- mock.call(['ebtables', '-t', 'filter', '--concurrent', '-X', +- mac_chain], +- check_exit_code=True, extra_ok_codes=None, +- log_fail_as_error=True, run_as_root=True), + ] + + arp_protect.delete_arp_spoofing_protection([VIF]) diff -Nru neutron-12.1.1/debian/patches/CVE-2021-40085.patch neutron-12.1.1/debian/patches/CVE-2021-40085.patch --- neutron-12.1.1/debian/patches/CVE-2021-40085.patch 1970-01-01 00:00:00.000000000 +0000 +++ neutron-12.1.1/debian/patches/CVE-2021-40085.patch 2023-04-18 15:21:42.000000000 +0000 @@ -0,0 +1,75 @@ +Backport of: + +From 110fed07cb83deb3abd85073cb351066713b6384 Mon Sep 17 00:00:00 2001 +From: Slawek Kaplonski +Date: Mon, 23 Aug 2021 13:01:37 +0200 +Subject: [PATCH] Remove dhcp_extra_opt value after first newline character + +Passing newline to the dnsmasq may cause security issues, especially +that in case of Neutron that dhcp options' values are controlled by +cloud users. +This patch removes everything what is after first newline character +in the dhcp_extra_opt's values before passing them to dnsmasq. + +Conflicts: + neutron/tests/unit/agent/linux/test_dhcp.py + +Closes-Bug: #1939733 +Change-Id: Ifeaf258f0b5ea86f25620ac4116d618980a7272e +(cherry picked from commit df891f0593d234e01f27d7c0376d9702e178ecfb) +--- + neutron/agent/linux/dhcp.py | 7 ++++--- + neutron/tests/unit/agent/linux/test_dhcp.py | 7 ++++++- + ...wline-chars-in-dhcp-extra-options-bf86d30371556d63.yaml | 6 ++++++ + 3 files changed, 16 insertions(+), 4 deletions(-) + create mode 100644 releasenotes/notes/fix-newline-chars-in-dhcp-extra-options-bf86d30371556d63.yaml + +--- a/neutron/agent/linux/dhcp.py ++++ b/neutron/agent/linux/dhcp.py +@@ -1123,10 +1123,11 @@ class Dnsmasq(DhcpLocalProcess): + else: + option = 'option6:%s' % option + if extra_tag: +- tags = ('tag:' + tag, extra_tag[:-1], '%s' % option) ++ tags = ['tag:' + tag, extra_tag[:-1], '%s' % option] + else: +- tags = ('tag:' + tag, '%s' % option) +- return ','.join(tags + args) ++ tags = ['tag:' + tag, '%s' % option] ++ ++ return ','.join(tags + [v.split("\n", 1)[0] for v in args]) + + @staticmethod + def _convert_to_literal_addrs(ip_version, ips): +--- a/neutron/tests/unit/agent/linux/test_dhcp.py ++++ b/neutron/tests/unit/agent/linux/test_dhcp.py +@@ -225,6 +225,9 @@ class FakeV6PortExtraOpt(object): + self.extra_dhcp_opts = [ + DhcpOpt(opt_name='dns-server', + opt_value='ffea:3ba5:a17a:4ba3::100', ++ ip_version=6), ++ DhcpOpt(opt_name='malicious-option', ++ opt_value='aaa\nbbb.ccc\n', + ip_version=6)] + + +@@ -2578,7 +2581,9 @@ class TestDnsmasq(TestBase): + exp_opt_name = '/dhcp/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/opts' + exp_opt_data = ('tag:tag0,option6:domain-search,openstacklocal\n' + 'tag:hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh,' +- 'option6:dns-server,ffea:3ba5:a17a:4ba3::100').lstrip() ++ 'option6:dns-server,ffea:3ba5:a17a:4ba3::100\n' ++ 'tag:hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh,' ++ 'option6:malicious-option,aaa').lstrip() + dm = self._get_dnsmasq(FakeV6NetworkStatelessDHCP()) + dm._output_hosts_file() + dm._output_opts_file() +--- /dev/null ++++ b/releasenotes/notes/fix-newline-chars-in-dhcp-extra-options-bf86d30371556d63.yaml +@@ -0,0 +1,6 @@ ++--- ++security: ++ - | ++ Fix `bug 1939733 `_ by ++ dropping from the dhcp extra option values everything what is after first ++ newline (``\n``) character before passing them to the dnsmasq. diff -Nru neutron-12.1.1/debian/patches/CVE-2021-40797.patch neutron-12.1.1/debian/patches/CVE-2021-40797.patch --- neutron-12.1.1/debian/patches/CVE-2021-40797.patch 1970-01-01 00:00:00.000000000 +0000 +++ neutron-12.1.1/debian/patches/CVE-2021-40797.patch 2023-04-18 15:21:52.000000000 +0000 @@ -0,0 +1,42 @@ +From dfbb9bcb5dae282c575b33171d691a5fca946beb Mon Sep 17 00:00:00 2001 +From: Slawek Kaplonski +Date: Fri, 3 Sep 2021 16:04:02 +0200 +Subject: [PATCH] Don't use singleton in routes.middleware.RoutesMiddleware + +It seems that using default singleton=True in the +routes.middleware.RoutesMiddleware which is leading to use thread-local +RequestConfig singleton object is not working well with eventlet +monkeypatching of threading library which we are doing in Neutron. +As a result it leaks memory in neutron-api workers every time when API +request to not existing API endpoint is made by user. + +To avoid that memory leak, let's use singletone=False in that +RoutesMiddleware object, at least until problem with thread-local +singleton and eventlet monkey patching will be solved. + +Closes-Bug: #1942179 +Change-Id: Id3a529248d3984506f0166bdc32e334127a01b7b +(cherry picked from commit e610a5eb9e71aa2549fb11e2139370d227787da2) +--- + neutron/api/extensions.py | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/neutron/api/extensions.py b/neutron/api/extensions.py +index 8111b142612..1080c69b105 100644 +--- a/neutron/api/extensions.py ++++ b/neutron/api/extensions.py +@@ -197,8 +197,13 @@ class ExtensionMiddleware(base.ConfigurableMiddleware): + controller = req_controllers[request_ext.key] + controller.add_handler(request_ext.handler) + ++ # NOTE(slaweq): It seems that using singleton=True in conjunction ++ # with eventlet monkey patching of the threading library doesn't work ++ # well and there is memory leak. See ++ # https://bugs.launchpad.net/neutron/+bug/1942179 for details + self._router = routes.middleware.RoutesMiddleware(self._dispatch, +- mapper) ++ mapper, ++ singleton=False) + super(ExtensionMiddleware, self).__init__(application) + + @classmethod diff -Nru neutron-12.1.1/debian/patches/CVE-2022-3277.patch neutron-12.1.1/debian/patches/CVE-2022-3277.patch --- neutron-12.1.1/debian/patches/CVE-2022-3277.patch 1970-01-01 00:00:00.000000000 +0000 +++ neutron-12.1.1/debian/patches/CVE-2022-3277.patch 2023-04-18 15:23:29.000000000 +0000 @@ -0,0 +1,51 @@ +Backport of: + +From cbeee87fa44cd200d4997e02042098460167dce1 Mon Sep 17 00:00:00 2001 +From: Brian Haley +Date: Thu, 1 Sep 2022 21:13:44 -0400 +Subject: [PATCH] Do not allow a tenant to create a default SG for another one + +The attempt to list security groups for a project, or any +random string, can create a default SG for it. Only allow if +privileges support it. + +Closes-bug: #1988026 + +Change-Id: Ieef7011f48cd2188d4254ff16d90a6465bbabfe3 +(cherry picked from commit 01fc2b9195f999df4d810df4ee63f77ecbc81f7e) +--- + neutron/db/securitygroups_db.py | 4 ++++ + neutron/tests/unit/db/test_securitygroups_db.py | 12 ++++++++++++ + 2 files changed, 16 insertions(+) + +--- a/neutron/db/securitygroups_db.py ++++ b/neutron/db/securitygroups_db.py +@@ -774,6 +774,10 @@ class SecurityGroupDbMixin(ext_sg.Securi + + :returns: the default security group id for given tenant. + """ ++ # Do not allow a tenant to create a default SG for another one. ++ # See Bug 1987410. ++ if tenant_id != context.tenant_id and not context.is_admin: ++ return + default_group_id = self._get_default_sg_id(context, tenant_id) + if default_group_id: + return default_group_id +--- a/neutron/tests/unit/db/test_securitygroups_db.py ++++ b/neutron/tests/unit/db/test_securitygroups_db.py +@@ -460,3 +460,15 @@ class SecurityGroupDbMixinTestCase(testl + {'port_range_min': 100, + 'port_range_max': 200, + 'protocol': '111'}) ++ ++ def test__ensure_default_security_group_tenant_mismatch(self): ++ with mock.patch.object( ++ self.mixin, '_get_default_sg_id') as get_default_sg_id,\ ++ mock.patch.object( ++ self.mixin, 'create_security_group') as create_sg: ++ context = mock.Mock() ++ context.tenant_id = 'tenant_0' ++ context.is_admin = False ++ self.mixin._ensure_default_security_group(context, 'tenant_1') ++ create_sg.assert_not_called() ++ get_default_sg_id.assert_not_called() diff -Nru neutron-12.1.1/debian/patches/series neutron-12.1.1/debian/patches/series --- neutron-12.1.1/debian/patches/series 2021-04-26 17:01:49.000000000 +0000 +++ neutron-12.1.1/debian/patches/series 2023-04-18 15:22:01.000000000 +0000 @@ -13,3 +13,9 @@ 0001-Do-not-initialize-snat-ns-twice.patch 0001-Handle-OVSFWPortNotFound-and-OVSFWTagNotFound-in-ovs.patch 0001-Workaround-for-TCP-checksum-issue-with-ovs-dpdk-and-.patch +CVE-2021-20267-1.patch +CVE-2021-20267-2.patch +CVE-2021-38598.patch +CVE-2021-40085.patch +CVE-2021-40797.patch +CVE-2022-3277.patch