From 184c836a16e9954a2cba11ae21f07923077ec904 Mon Sep 17 00:00:00 2001 From: James Falcon Date: Mon, 19 Jul 2021 14:13:21 -0500 Subject: Initial hotplug support (#936) Adds a udev script which will invoke a hotplug hook script on all net add events. The script will write some udev arguments to a systemd FIFO socket (to ensure we have only instance of cloud-init running at a time), which is then read by a new service that calls a new 'cloud-init devel hotplug-hook' command to handle the new event. This hotplug-hook command will: - Fetch the pickled datsource - Verify that the hotplug event is supported/enabled - Update the metadata for the datasource - Ensure the hotplugged device exists within the datasource - Apply the config change on the datasource metadata - Bring up the new interface (or apply global network configuration) - Save the updated metadata back to the pickle cache Also scattered in some unrelated typing where helpful --- packages/redhat/cloud-init.spec.in | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'packages/redhat/cloud-init.spec.in') diff --git a/packages/redhat/cloud-init.spec.in b/packages/redhat/cloud-init.spec.in index 16138012..b930709b 100644 --- a/packages/redhat/cloud-init.spec.in +++ b/packages/redhat/cloud-init.spec.in @@ -119,6 +119,12 @@ version_pys=$(cd "$RPM_BUILD_ROOT" && find . -name version.py -type f) ( cd "$RPM_BUILD_ROOT" && sed -i "s,@@PACKAGED_VERSION@@,%{version}-%{release}," $version_pys ) +# patch hotplug /usr/libexec script path +hotplug_file=$(cd "$RPM_BUILD_ROOT" && find . -name 10-cloud-init-hook-hotplug.rules -type f) + +( cd "$RPM_BUILD_ROOT" && + sed -i "s,/usr/lib,%{_libexecdir}," $hotplug_file ) + %clean rm -rf $RPM_BUILD_ROOT @@ -172,6 +178,7 @@ fi %files /lib/udev/rules.d/66-azure-ephemeral.rules +/lib/udev/rules.d/10-cloud-init-hook-hotplug.rules %if "%{init_system}" == "systemd" /usr/lib/systemd/system-generators/cloud-init-generator -- cgit v1.2.3 From b3e31ba228d32c318872fb68edda272f679e1004 Mon Sep 17 00:00:00 2001 From: Ryan Harper Date: Tue, 12 Oct 2021 09:31:36 -0500 Subject: Inhibit sshd-keygen@.service if cloud-init is active (#1028) In some cloud-init enabled images the sshd-keygen@.service may race with cloud-init and prevent ssh host keys from being generated or generating host keys twice slowing boot and consuming additional entropy during boot. This drop-in unit adds a condition to the sshd-keygen@.service which prevents running if cloud-init is active. --- packages/redhat/cloud-init.spec.in | 1 + packages/suse/cloud-init.spec.in | 1 + setup.py | 5 ++++- systemd/disable-sshd-keygen-if-cloud-init-active.conf | 8 ++++++++ 4 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 systemd/disable-sshd-keygen-if-cloud-init-active.conf (limited to 'packages/redhat/cloud-init.spec.in') diff --git a/packages/redhat/cloud-init.spec.in b/packages/redhat/cloud-init.spec.in index b930709b..22db4b50 100644 --- a/packages/redhat/cloud-init.spec.in +++ b/packages/redhat/cloud-init.spec.in @@ -182,6 +182,7 @@ fi %if "%{init_system}" == "systemd" /usr/lib/systemd/system-generators/cloud-init-generator +%{_sysconfdir}/systemd/system/sshd-keygen@.service.d/disable-sshd-keygen-if-cloud-init-active.conf %{_unitdir}/cloud-* %else %attr(0755, root, root) %{_initddir}/cloud-config diff --git a/packages/suse/cloud-init.spec.in b/packages/suse/cloud-init.spec.in index 004b875f..da8107b4 100644 --- a/packages/suse/cloud-init.spec.in +++ b/packages/suse/cloud-init.spec.in @@ -126,6 +126,7 @@ version_pys=$(cd "%{buildroot}" && find . -name version.py -type f) %{_sysconfdir}/dhcp/dhclient-exit-hooks.d/hook-dhclient %{_sysconfdir}/NetworkManager/dispatcher.d/hook-network-manager +%{_sysconfdir}/systemd/system/sshd-keygen@.service.d/disable-sshd-keygen-if-cloud-init-active.conf # Python code is here... %{python_sitelib}/* diff --git a/setup.py b/setup.py index 100575ff..6e463bf8 100755 --- a/setup.py +++ b/setup.py @@ -40,6 +40,7 @@ def is_generator(p): def pkg_config_read(library, var): fallbacks = { 'systemd': { + 'systemdsystemconfdir': '/etc/systemd/system', 'systemdsystemunitdir': '/lib/systemd/system', 'systemdsystemgeneratordir': '/lib/systemd/system-generators', } @@ -270,7 +271,9 @@ if not platform.system().endswith('BSD'): (ETC + '/NetworkManager/dispatcher.d/', ['tools/hook-network-manager']), (ETC + '/dhcp/dhclient-exit-hooks.d/', ['tools/hook-dhclient']), - (LIB + '/udev/rules.d', [f for f in glob('udev/*.rules')]) + (LIB + '/udev/rules.d', [f for f in glob('udev/*.rules')]), + (ETC + '/systemd/system/sshd-keygen@.service.d/', + ['systemd/disable-sshd-keygen-if-cloud-init-active.conf']), ]) # Use a subclass for install that handles # adding on the right init system configuration files diff --git a/systemd/disable-sshd-keygen-if-cloud-init-active.conf b/systemd/disable-sshd-keygen-if-cloud-init-active.conf new file mode 100644 index 00000000..71e35876 --- /dev/null +++ b/systemd/disable-sshd-keygen-if-cloud-init-active.conf @@ -0,0 +1,8 @@ +# In some cloud-init enabled images the sshd-keygen template service may race +# with cloud-init during boot causing issues with host key generation. This +# drop-in config adds a condition to sshd-keygen@.service if it exists and +# prevents the sshd-keygen units from running *if* cloud-init is going to run. +# +[Unit] +ConditionPathExists=!/run/systemd/generator.early/multi-user.target.wants/cloud-init.target +EOF -- cgit v1.2.3 From a90d8338f07b30a46887f7c133baade63129a53a Mon Sep 17 00:00:00 2001 From: James Falcon Date: Fri, 29 Oct 2021 15:39:29 -0500 Subject: Allow libexec for hotplug (#1088) When we added the install hotplug module, we forgot to update the redhet/cloud-init.spec.in file and allow for execution on /usr/libexec. This PR adds that functionality. --- cloudinit/config/cc_install_hotplug.py | 10 +++++++--- cloudinit/sources/__init__.py | 1 + packages/redhat/cloud-init.spec.in | 7 ------- .../test_handler/test_handler_install_hotplug.py | 23 +++++++++++++++------- 4 files changed, 24 insertions(+), 17 deletions(-) (limited to 'packages/redhat/cloud-init.spec.in') diff --git a/cloudinit/config/cc_install_hotplug.py b/cloudinit/config/cc_install_hotplug.py index d6b2a2df..da98c409 100644 --- a/cloudinit/config/cc_install_hotplug.py +++ b/cloudinit/config/cc_install_hotplug.py @@ -85,11 +85,11 @@ __doc__ = get_schema_doc(schema) HOTPLUG_UDEV_PATH = "/etc/udev/rules.d/10-cloud-init-hook-hotplug.rules" -HOTPLUG_UDEV_RULES = """\ +HOTPLUG_UDEV_RULES_TEMPLATE = """\ # Installed by cloud-init due to network hotplug userdata ACTION!="add|remove", GOTO="cloudinit_end" LABEL="cloudinit_hook" -SUBSYSTEM=="net", RUN+="/usr/lib/cloud-init/hook-hotplug" +SUBSYSTEM=="net", RUN+="{libexecdir}/hook-hotplug" LABEL="cloudinit_end" """ @@ -129,8 +129,12 @@ def handle(_name, cfg, cloud, log, _args): log.debug("Skipping hotplug install, udevadm not found") return + # This may need to turn into a distro property at some point + libexecdir = "/usr/libexec/cloud-init" + if not os.path.exists(libexecdir): + libexecdir = "/usr/lib/cloud-init" util.write_file( filename=HOTPLUG_UDEV_PATH, - content=HOTPLUG_UDEV_RULES, + content=HOTPLUG_UDEV_RULES_TEMPLATE.format(libexecdir=libexecdir), ) subp.subp(["udevadm", "control", "--reload-rules"]) diff --git a/cloudinit/sources/__init__.py b/cloudinit/sources/__init__.py index d61d280d..f2f2343c 100644 --- a/cloudinit/sources/__init__.py +++ b/cloudinit/sources/__init__.py @@ -196,6 +196,7 @@ class DataSource(CloudInitPickleMixin, metaclass=abc.ABCMeta): EventType.BOOT_NEW_INSTANCE, EventType.BOOT, EventType.BOOT_LEGACY, + EventType.HOTPLUG, }} default_update_events = {EventScope.NETWORK: { EventType.BOOT_NEW_INSTANCE, diff --git a/packages/redhat/cloud-init.spec.in b/packages/redhat/cloud-init.spec.in index 22db4b50..1491822b 100644 --- a/packages/redhat/cloud-init.spec.in +++ b/packages/redhat/cloud-init.spec.in @@ -119,12 +119,6 @@ version_pys=$(cd "$RPM_BUILD_ROOT" && find . -name version.py -type f) ( cd "$RPM_BUILD_ROOT" && sed -i "s,@@PACKAGED_VERSION@@,%{version}-%{release}," $version_pys ) -# patch hotplug /usr/libexec script path -hotplug_file=$(cd "$RPM_BUILD_ROOT" && find . -name 10-cloud-init-hook-hotplug.rules -type f) - -( cd "$RPM_BUILD_ROOT" && - sed -i "s,/usr/lib,%{_libexecdir}," $hotplug_file ) - %clean rm -rf $RPM_BUILD_ROOT @@ -178,7 +172,6 @@ fi %files /lib/udev/rules.d/66-azure-ephemeral.rules -/lib/udev/rules.d/10-cloud-init-hook-hotplug.rules %if "%{init_system}" == "systemd" /usr/lib/systemd/system-generators/cloud-init-generator diff --git a/tests/unittests/test_handler/test_handler_install_hotplug.py b/tests/unittests/test_handler/test_handler_install_hotplug.py index 19b0cc41..5d6b1e77 100644 --- a/tests/unittests/test_handler/test_handler_install_hotplug.py +++ b/tests/unittests/test_handler/test_handler_install_hotplug.py @@ -7,7 +7,7 @@ import pytest from cloudinit.config.cc_install_hotplug import ( handle, HOTPLUG_UDEV_PATH, - HOTPLUG_UDEV_RULES, + HOTPLUG_UDEV_RULES_TEMPLATE, ) from cloudinit.event import EventScope, EventType @@ -38,7 +38,10 @@ def mocks(): class TestInstallHotplug: - def test_rules_installed_when_supported_and_enabled(self, mocks): + @pytest.mark.parametrize('libexec_exists', [True, False]) + def test_rules_installed_when_supported_and_enabled( + self, mocks, libexec_exists + ): mocks.m_which.return_value = 'udevadm' mocks.m_update_enabled.return_value = True m_cloud = mock.MagicMock() @@ -46,11 +49,17 @@ class TestInstallHotplug: EventScope.NETWORK: {EventType.HOTPLUG} } - handle(None, {}, m_cloud, mock.Mock(), None) - mocks.m_write.assert_called_once_with( - filename=HOTPLUG_UDEV_PATH, - content=HOTPLUG_UDEV_RULES, - ) + if libexec_exists: + libexecdir = "/usr/libexec/cloud-init" + else: + libexecdir = "/usr/lib/cloud-init" + with mock.patch('os.path.exists', return_value=libexec_exists): + handle(None, {}, m_cloud, mock.Mock(), None) + mocks.m_write.assert_called_once_with( + filename=HOTPLUG_UDEV_PATH, + content=HOTPLUG_UDEV_RULES_TEMPLATE.format( + libexecdir=libexecdir), + ) assert mocks.m_subp.call_args_list == [mock.call([ 'udevadm', 'control', '--reload-rules', ])] -- cgit v1.2.3