From fd633c77ed21a928ab26648b93791b1d0acf198b Mon Sep 17 00:00:00 2001 From: RH Virt Maint Bot <8309305-rh-virt-maint-bot@users.noreply.gitlab.com> Date: Fri, 4 Aug 2023 11:56:16 +0000 Subject: [PATCH] * Fri Aug 04 2023 Camilla Conte - 23.1.1-9 - 0030-NM-renderer-set-default-IPv6-addr-gen-mode-for-all-i.patch [bz#2188388] - Resolves: bz#2188388 --- ...default-IPv6-addr-gen-mode-for-all-i.patch | 283 ++++++++++++++++++ cloud-init.spec | 7 +- 2 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 0030-NM-renderer-set-default-IPv6-addr-gen-mode-for-all-i.patch diff --git a/0030-NM-renderer-set-default-IPv6-addr-gen-mode-for-all-i.patch b/0030-NM-renderer-set-default-IPv6-addr-gen-mode-for-all-i.patch new file mode 100644 index 0000000..680622b --- /dev/null +++ b/0030-NM-renderer-set-default-IPv6-addr-gen-mode-for-all-i.patch @@ -0,0 +1,283 @@ +From c720ab9703752535767691a31e4720e11674bb1f Mon Sep 17 00:00:00 2001 +From: Ani Sinha +Date: Fri, 4 Aug 2023 08:58:26 +0530 +Subject: [PATCH] NM renderer: set default IPv6 addr-gen-mode for all + interfaces to eui64 (#4291) + +By default, NetworkManager renderer in cloud-init does not set any specific +method for IPV6 addr-gen-mode in the keyfiles it writes. Hence, implicitly the +mode is set to `eui64` in the absence of any global addr-gen-mode option in +NetworkManager configuration. +Later when other interfaces get added via D-Bus API or by using nmcli commands +without explictly setting an addr-gen-mode, NM auto generates new profiles for +those interfaces with addr-gen-mode set to `stable-privacy`. This introduces +inconsistency of configurations between interfaces based on how they were +added. This can cause problems for the customers. + +In this change, cloud-init overrides NetworkManager's preferred default of +`stable-privacy` to use EUI64 using a drop in NetworkManager configuration +file. This setting can be overriden by using global-connection-defaults +setting in /etc/NetworkManager/NetworkManager.conf file. + +RHBZ: 2188388 + +Signed-off-by: Ani Sinha +(cherry picked from commit d41264cb4297a4b143a23f3677d33b81fbfc6e8e) + +Conflicts: + tests/unittests/test_net.py +--- + cloudinit/net/network_manager.py | 21 ++++++++ + tests/unittests/test_net.py | 91 +++++++++++++++++++++++++------- + 2 files changed, 94 insertions(+), 18 deletions(-) + +diff --git a/cloudinit/net/network_manager.py b/cloudinit/net/network_manager.py +index ca216928..8047f796 100644 +--- a/cloudinit/net/network_manager.py ++++ b/cloudinit/net/network_manager.py +@@ -21,6 +21,15 @@ from cloudinit.net.network_state import NetworkState + NM_RUN_DIR = "/etc/NetworkManager" + NM_LIB_DIR = "/usr/lib/NetworkManager" + NM_CFG_FILE = "/etc/NetworkManager/NetworkManager.conf" ++NM_IPV6_ADDR_GEN_CONF = """# This is generated by cloud-init. Do not edit. ++# ++[.config] ++ enable=nm-version-min:1.40 ++[connection.30-cloud-init-ip6-addr-gen-mode] ++ # Select EUI64 to be used if the profile does not specify it. ++ ipv6.addr-gen-mode=0 ++ ++""" + LOG = logging.getLogger(__name__) + + +@@ -368,6 +377,12 @@ class Renderer(renderer.Renderer): + name = conn_filename(con_id, target) + util.write_file(name, conn.dump(), 0o600) + ++ # Select EUI64 to be used by default by NM for creating the address ++ # for use with RFC4862 IPv6 Stateless Address Autoconfiguration. ++ util.write_file( ++ cloud_init_nm_conf_filename(target), NM_IPV6_ADDR_GEN_CONF, 0o600 ++ ) ++ + + def conn_filename(con_id, target=None): + target_con_dir = subp.target_path(target, NM_RUN_DIR) +@@ -375,6 +390,12 @@ def conn_filename(con_id, target=None): + return f"{target_con_dir}/system-connections/{con_file}" + + ++def cloud_init_nm_conf_filename(target=None): ++ target_con_dir = subp.target_path(target, NM_RUN_DIR) ++ conf_file = "30-cloud-init-ip6-addr-gen-mode.conf" ++ return f"{target_con_dir}/conf.d/{conf_file}" ++ ++ + def available(target=None): + # TODO: Move `uses_systemd` to a more appropriate location + # It is imported here to avoid circular import +diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py +index fd656a57..d49da696 100644 +--- a/tests/unittests/test_net.py ++++ b/tests/unittests/test_net.py +@@ -5679,9 +5679,25 @@ class TestNetworkManagerRendering(CiTestCase): + with_logs = True + + scripts_dir = "/etc/NetworkManager/system-connections" ++ conf_dir = "/etc/NetworkManager/conf.d" + + expected_name = "expected_network_manager" + ++ expected_conf_d = { ++ "30-cloud-init-ip6-addr-gen-mode.conf": textwrap.dedent( ++ """\ ++ # This is generated by cloud-init. Do not edit. ++ # ++ [.config] ++ enable=nm-version-min:1.40 ++ [connection.30-cloud-init-ip6-addr-gen-mode] ++ # Select EUI64 to be used if the profile does not specify it. ++ ipv6.addr-gen-mode=0 ++ ++ """ ++ ), ++ } ++ + def _get_renderer(self): + return network_manager.Renderer() + +@@ -5700,11 +5716,19 @@ class TestNetworkManagerRendering(CiTestCase): + renderer.render_network_state(ns, target=dir) + return dir2dict(dir) + +- def _compare_files_to_expected(self, expected, found): ++ def _compare_files_to_expected( ++ self, expected_scripts, expected_conf, found ++ ): + orig_maxdiff = self.maxDiff +- expected_d = dict( +- (os.path.join(self.scripts_dir, k), v) for k, v in expected.items() ++ conf_d = dict( ++ (os.path.join(self.conf_dir, k), v) ++ for k, v in expected_conf.items() ++ ) ++ scripts_d = dict( ++ (os.path.join(self.scripts_dir, k), v) ++ for k, v in expected_scripts.items() + ) ++ expected_d = {**conf_d, **scripts_d} + + try: + self.maxDiff = None +@@ -5765,6 +5789,7 @@ class TestNetworkManagerRendering(CiTestCase): + """ + ), + }, ++ self.expected_conf_d, + found, + ) + +@@ -5820,8 +5845,9 @@ class TestNetworkManagerRendering(CiTestCase): + gateway=10.0.2.2 + + """ +- ), ++ ) + }, ++ self.expected_conf_d, + found, + ) + +@@ -5857,33 +5883,44 @@ class TestNetworkManagerRendering(CiTestCase): + """ + ), + }, ++ self.expected_conf_d, + found, + ) + + def test_bond_config(self): + entry = NETWORK_CONFIGS["bond"] + found = self._render_and_read(network_config=yaml.load(entry["yaml"])) +- self._compare_files_to_expected(entry[self.expected_name], found) ++ self._compare_files_to_expected( ++ entry[self.expected_name], self.expected_conf_d, found ++ ) + + def test_vlan_config(self): + entry = NETWORK_CONFIGS["vlan"] + found = self._render_and_read(network_config=yaml.load(entry["yaml"])) +- self._compare_files_to_expected(entry[self.expected_name], found) ++ self._compare_files_to_expected( ++ entry[self.expected_name], self.expected_conf_d, found ++ ) + + def test_bridge_config(self): + entry = NETWORK_CONFIGS["bridge"] + found = self._render_and_read(network_config=yaml.load(entry["yaml"])) +- self._compare_files_to_expected(entry[self.expected_name], found) ++ self._compare_files_to_expected( ++ entry[self.expected_name], self.expected_conf_d, found ++ ) + + def test_manual_config(self): + entry = NETWORK_CONFIGS["manual"] + found = self._render_and_read(network_config=yaml.load(entry["yaml"])) +- self._compare_files_to_expected(entry[self.expected_name], found) ++ self._compare_files_to_expected( ++ entry[self.expected_name], self.expected_conf_d, found ++ ) + + def test_all_config(self): + entry = NETWORK_CONFIGS["all"] + found = self._render_and_read(network_config=yaml.load(entry["yaml"])) +- self._compare_files_to_expected(entry[self.expected_name], found) ++ self._compare_files_to_expected( ++ entry[self.expected_name], self.expected_conf_d, found ++ ) + self.assertNotIn( + "WARNING: Network config: ignoring eth0.101 device-level mtu", + self.logs.getvalue(), +@@ -5892,12 +5929,16 @@ class TestNetworkManagerRendering(CiTestCase): + def test_small_config(self): + entry = NETWORK_CONFIGS["small"] + found = self._render_and_read(network_config=yaml.load(entry["yaml"])) +- self._compare_files_to_expected(entry[self.expected_name], found) ++ self._compare_files_to_expected( ++ entry[self.expected_name], self.expected_conf_d, found ++ ) + + def test_v4_and_v6_static_config(self): + entry = NETWORK_CONFIGS["v4_and_v6_static"] + found = self._render_and_read(network_config=yaml.load(entry["yaml"])) +- self._compare_files_to_expected(entry[self.expected_name], found) ++ self._compare_files_to_expected( ++ entry[self.expected_name], self.expected_conf_d, found ++ ) + expected_msg = ( + "WARNING: Network config: ignoring iface0 device-level mtu:8999" + " because ipv4 subnet-level mtu:9000 provided." +@@ -5907,41 +5948,55 @@ class TestNetworkManagerRendering(CiTestCase): + def test_dhcpv6_only_config(self): + entry = NETWORK_CONFIGS["dhcpv6_only"] + found = self._render_and_read(network_config=yaml.load(entry["yaml"])) +- self._compare_files_to_expected(entry[self.expected_name], found) ++ self._compare_files_to_expected( ++ entry[self.expected_name], self.expected_conf_d, found ++ ) + + def test_simple_render_ipv6_slaac(self): + entry = NETWORK_CONFIGS["ipv6_slaac"] + found = self._render_and_read(network_config=yaml.load(entry["yaml"])) +- self._compare_files_to_expected(entry[self.expected_name], found) ++ self._compare_files_to_expected( ++ entry[self.expected_name], self.expected_conf_d, found ++ ) + + def test_dhcpv6_stateless_config(self): + entry = NETWORK_CONFIGS["dhcpv6_stateless"] + found = self._render_and_read(network_config=yaml.load(entry["yaml"])) +- self._compare_files_to_expected(entry[self.expected_name], found) ++ self._compare_files_to_expected( ++ entry[self.expected_name], self.expected_conf_d, found ++ ) + + def test_wakeonlan_disabled_config_v2(self): + entry = NETWORK_CONFIGS["wakeonlan_disabled"] + found = self._render_and_read( + network_config=yaml.load(entry["yaml_v2"]) + ) +- self._compare_files_to_expected(entry[self.expected_name], found) ++ self._compare_files_to_expected( ++ entry[self.expected_name], self.expected_conf_d, found ++ ) + + def test_wakeonlan_enabled_config_v2(self): + entry = NETWORK_CONFIGS["wakeonlan_enabled"] + found = self._render_and_read( + network_config=yaml.load(entry["yaml_v2"]) + ) +- self._compare_files_to_expected(entry[self.expected_name], found) ++ self._compare_files_to_expected( ++ entry[self.expected_name], self.expected_conf_d, found ++ ) + + def test_render_v4_and_v6(self): + entry = NETWORK_CONFIGS["v4_and_v6"] + found = self._render_and_read(network_config=yaml.load(entry["yaml"])) +- self._compare_files_to_expected(entry[self.expected_name], found) ++ self._compare_files_to_expected( ++ entry[self.expected_name], self.expected_conf_d, found ++ ) + + def test_render_v6_and_v4(self): + entry = NETWORK_CONFIGS["v6_and_v4"] + found = self._render_and_read(network_config=yaml.load(entry["yaml"])) +- self._compare_files_to_expected(entry[self.expected_name], found) ++ self._compare_files_to_expected( ++ entry[self.expected_name], self.expected_conf_d, found ++ ) + + + @mock.patch( diff --git a/cloud-init.spec b/cloud-init.spec index de18b37..978ed01 100644 --- a/cloud-init.spec +++ b/cloud-init.spec @@ -1,6 +1,6 @@ Name: cloud-init Version: 23.1.1 -Release: 8%{?dist} +Release: 9%{?dist} Summary: Cloud instance init scripts License: ASL 2.0 or GPLv3 URL: http://launchpad.net/cloud-init @@ -37,6 +37,7 @@ Patch26: 0026-Enable-SUSE-based-distros-for-ca-handling-2036.patch Patch27: 0027-Handle-non-existent-ca-cert-config-situation-2073.patch Patch28: 0028-logging-keep-current-file-mode-of-log-file-if-its-st.patch Patch29: 0029-DS-VMware-modify-a-few-log-level-4284.patch +Patch30: 0030-NM-renderer-set-default-IPv6-addr-gen-mode-for-all-i.patch BuildArch: noarch @@ -246,6 +247,10 @@ fi %config(noreplace) %{_sysconfdir}/rsyslog.d/21-cloudinit.conf %changelog +* Fri Aug 04 2023 Camilla Conte - 23.1.1-9 +- 0030-NM-renderer-set-default-IPv6-addr-gen-mode-for-all-i.patch [bz#2188388] +- Resolves: bz#2188388 + * Wed Jul 26 2023 Camilla Conte - 23.1.1-8 - 0022-test-fixes-update-tests-to-reflect-AUTOCONNECT_PRIOR.patch [bz#2217865] - 0023-test-fixes-remove-NM_CONTROLLED-no-from-tests.patch [bz#2217865]