diff --git a/cloudinit/config/cc_ssh_import_id.py b/cloudinit/config/cc_ssh_import_id.py index 7b6272ccaac..a4ca1b981f7 100644 --- a/cloudinit/config/cc_ssh_import_id.py +++ b/cloudinit/config/cc_ssh_import_id.py @@ -19,7 +19,7 @@ from cloudinit.settings import PER_INSTANCE # https://launchpad.net/ssh-import-id -distros = ["ubuntu", "debian", "cos"] +distros = ["alpine", "cos", "debian", "ubuntu"] SSH_IMPORT_ID_BINARY = "ssh-import-id" MODULE_DESCRIPTION = """\ @@ -152,13 +152,24 @@ def import_ssh_ids(ids, user): # I'm including the `--preserve-env` here as a one-off, but we should # have a better way of setting env earlier in boot and using it later. # Perhaps a 'set_env' module? - cmd = [ - "sudo", - "--preserve-env=https_proxy", - "-Hu", - user, - SSH_IMPORT_ID_BINARY, - ] + ids + if subp.which("sudo"): + cmd = [ + "sudo", + "--preserve-env=https_proxy", + "-Hu", + user, + SSH_IMPORT_ID_BINARY, + ] + ids + elif subp.which("doas"): + cmd = [ + "doas", + "-u", + user, + SSH_IMPORT_ID_BINARY, + ] + ids + else: + LOG.error("Neither sudo nor doas available! Unable to import SSH ids.") + return LOG.debug("Importing SSH ids for user %s.", user) try: diff --git a/tests/unittests/config/test_cc_ssh_import_id.py b/tests/unittests/config/test_cc_ssh_import_id.py index 572094a43e2..2d5608a6464 100644 --- a/tests/unittests/config/test_cc_ssh_import_id.py +++ b/tests/unittests/config/test_cc_ssh_import_id.py @@ -76,3 +76,52 @@ def test_skip_inapplicable_configs(self, m_which, cfg, log, caplog): cloud = get_cloud("ubuntu") cc_ssh_import_id.handle("name", cfg, cloud, []) assert log in caplog.text + + @mock.patch("cloudinit.ssh_util.pwd.getpwnam") + @mock.patch("cloudinit.config.cc_ssh_import_id.subp.subp") + @mock.patch("cloudinit.subp.which") + def test_use_sudo(self, m_which, m_subp, m_getpwnam): + """Check that sudo is available and use that""" + m_which.return_value = "/usr/bin/ssh-import-id" + ids = ["waffle"] + user = "bob" + cc_ssh_import_id.import_ssh_ids(ids, user) + m_subp.assert_called_once_with( + [ + "sudo", + "--preserve-env=https_proxy", + "-Hu", + user, + "ssh-import-id", + ] + + ids, + capture=False, + ) + + @mock.patch("cloudinit.ssh_util.pwd.getpwnam") + @mock.patch("cloudinit.config.cc_ssh_import_id.subp.subp") + @mock.patch("cloudinit.subp.which") + def test_use_doas(self, m_which, m_subp, m_getpwnam): + """Check that doas is available and use that""" + m_which.side_effect = [None, "/usr/bin/doas"] + ids = ["waffle"] + user = "bob" + cc_ssh_import_id.import_ssh_ids(ids, user) + m_subp.assert_called_once_with( + ["doas", "-u", user, "ssh-import-id"] + ids, capture=False + ) + + @mock.patch("cloudinit.ssh_util.pwd.getpwnam") + @mock.patch("cloudinit.config.cc_ssh_import_id.subp.subp") + @mock.patch("cloudinit.subp.which") + def test_use_neither_sudo_nor_doas( + self, m_which, m_subp, m_getpwnam, caplog + ): + """Test when neither sudo nor doas is available""" + m_which.return_value = None + ids = ["waffle"] + user = "bob" + cc_ssh_import_id.import_ssh_ids(ids, user) + assert ( + "Neither sudo nor doas available! Unable to import SSH ids" + ) in caplog.text