From 483f79cb3b94c8c7d176e748892a040c71132cb3 Mon Sep 17 00:00:00 2001 From: Chad Smith Date: Sat, 4 Feb 2023 13:37:02 -0700 Subject: [PATCH] netplan: keep custom strict perms when 50-cloud-init.yaml exists Retain existing config file permissions when those permissions are more strict than the default permissions set on /etc/netplan/50-cloud-init.yaml. --- cloudinit/net/netplan.py | 5 ++ .../unittests/test_distros/test_netconfig.py | 74 ++++++++++++------- 2 files changed, 53 insertions(+), 26 deletions(-) diff --git a/cloudinit/net/netplan.py b/cloudinit/net/netplan.py index 6bb2cb5..894b5db 100644 --- a/cloudinit/net/netplan.py +++ b/cloudinit/net/netplan.py @@ -237,6 +237,11 @@ class Renderer(renderer.Renderer): header += "\n" mode = 0o600 if features.NETPLAN_CONFIG_ROOT_READ_ONLY else 0o644 + if os.path.exists(fpnplan): + current_mode = util.get_permissions(fpnplan) + if current_mode & mode == current_mode: + # preserve mode if existing perms are more strict than default + mode = current_mode util.write_file(fpnplan, header + content, mode=mode) if self.clean_default: diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py index bae2dc4..97659c4 100644 --- a/tests/unittests/test_distros/test_netconfig.py +++ b/tests/unittests/test_distros/test_netconfig.py @@ -391,7 +391,6 @@ class TestNetCfgDistroUbuntuEni(TestNetCfgDistroBase): expected_cfgs = { self.eni_path(): V1_NET_CFG_OUTPUT, } - # ub_distro.apply_network_config(V1_NET_CFG, False) self._apply_and_verify_eni(self.distro.apply_network_config, V1_NET_CFG, expected_cfgs=expected_cfgs.copy()) @@ -411,8 +410,14 @@ class TestNetCfgDistroUbuntuNetplan(TestNetCfgDistroBase): self.distro = self._get_distro('ubuntu', renderers=['netplan']) self.devlist = ['eth0', 'lo'] - def _apply_and_verify_netplan(self, apply_fn, config, expected_cfgs=None, - bringup=False): + def _apply_and_verify_netplan( + self, + apply_fn, + config, + expected_cfgs=None, + bringup=False, + previous_files=(), + ): if not expected_cfgs: raise ValueError('expected_cfg must not be None') @@ -422,12 +427,12 @@ class TestNetCfgDistroUbuntuNetplan(TestNetCfgDistroBase): with mock.patch("cloudinit.net.netplan.get_devicelist", return_value=self.devlist): with self.reRooted(tmpd) as tmpd: + for previous_path, content, mode in previous_files: + util.write_file(previous_path, content, mode=mode) apply_fn(config, bringup) results = dir2dict(tmpd) - - mode = 0o600 if features.NETPLAN_CONFIG_ROOT_READ_ONLY else 0o644 - for cfgpath, expected in expected_cfgs.items(): + for cfgpath, expected, mode in expected_cfgs: print("----------") print(expected) print("^^^^ expected | rendered VVVVVVV") @@ -440,39 +445,56 @@ class TestNetCfgDistroUbuntuNetplan(TestNetCfgDistroBase): return '/etc/netplan/50-cloud-init.yaml' def test_apply_network_config_v1_to_netplan_ub(self): - expected_cfgs = { - self.netplan_path(): V1_TO_V2_NET_CFG_OUTPUT, - } + expected_cfgs = ( + (self.netplan_path(), V1_TO_V2_NET_CFG_OUTPUT, 0o600), + ) - # ub_distro.apply_network_config(V1_NET_CFG, False) self._apply_and_verify_netplan(self.distro.apply_network_config, V1_NET_CFG, - expected_cfgs=expected_cfgs.copy()) + expected_cfgs=expected_cfgs) def test_apply_network_config_v1_ipv6_to_netplan_ub(self): - expected_cfgs = { - self.netplan_path(): V1_TO_V2_NET_CFG_IPV6_OUTPUT, - } + expected_cfgs = ( + (self.netplan_path(), V1_TO_V2_NET_CFG_IPV6_OUTPUT, 0o600), + ) - # ub_distro.apply_network_config(V1_NET_CFG_IPV6, False) self._apply_and_verify_netplan(self.distro.apply_network_config, V1_NET_CFG_IPV6, - expected_cfgs=expected_cfgs.copy()) + expected_cfgs=expected_cfgs) def test_apply_network_config_v2_passthrough_ub(self): - expected_cfgs = { - self.netplan_path(): V2_TO_V2_NET_CFG_OUTPUT, - } - # ub_distro.apply_network_config(V2_NET_CFG, False) + expected_cfgs = ( + (self.netplan_path(), V2_TO_V2_NET_CFG_OUTPUT, 0o600), + ) self._apply_and_verify_netplan(self.distro.apply_network_config, V2_NET_CFG, - expected_cfgs=expected_cfgs.copy()) + expected_cfgs=expected_cfgs) + + def test_apply_network_config_v2_passthrough_retain_orig_perms(self): + """Custom permissions on existing netplan is kept when more strict.""" + expected_cfgs = ( + (self.netplan_path(), V2_TO_V2_NET_CFG_OUTPUT, 0o640), + ) + with mock.patch.object( + features, "NETPLAN_CONFIG_ROOT_READ_ONLY", False + ): + # When NETPLAN_CONFIG_ROOT_READ_ONLY is False default perms are 644 + # we keep 640 because it's more strict. + # 1640 is used to assert sticky bit preserved across write + self._apply_and_verify_netplan( + self.distro.apply_network_config, + V2_NET_CFG, + expected_cfgs=expected_cfgs, + previous_files=( + ("/etc/netplan/50-cloud-init.yaml", "a", 0o640), + ), + ) def test_apply_network_config_v2_passthrough_ub_old_behavior(self): """Kinetic and earlier have 50-cloud-init.yaml world-readable""" - expected_cfgs = { - self.netplan_path(): V2_TO_V2_NET_CFG_OUTPUT, - } + expected_cfgs = ( + (self.netplan_path(), V2_TO_V2_NET_CFG_OUTPUT, 0o644), + ) # ub_distro.apply_network_config(V2_NET_CFG, False) with mock.patch.object( features, "NETPLAN_CONFIG_ROOT_READ_ONLY", False @@ -480,7 +502,7 @@ class TestNetCfgDistroUbuntuNetplan(TestNetCfgDistroBase): self._apply_and_verify_netplan( self.distro.apply_network_config, V2_NET_CFG, - expected_cfgs=expected_cfgs.copy(), + expected_cfgs=expected_cfgs, ) class TestNetCfgDistroRedhat(TestNetCfgDistroBase): @@ -771,7 +793,6 @@ class TestNetCfgDistroArch(TestNetCfgDistroBase): """), } - # ub_distro.apply_network_config(V1_NET_CFG, False) self._apply_and_verify(self.distro.apply_network_config, V1_NET_CFG, expected_cfgs=expected_cfgs.copy(), @@ -920,6 +941,7 @@ class TestNetCfgDistroPhoton(TestNetCfgDistroBase): def get_mode(path, target=None): + # Mask upper st_mode bits like S_IFREG bit preserve sticky and isuid/osgid return os.stat(subp.target_path(target, path)).st_mode & 0o777 # vi: ts=4 expandtab -- 2.33.0