summaryrefslogtreecommitdiff
path: root/cloudinit
diff options
context:
space:
mode:
authorPavel Zakharov <pavel.zakharov@delphix.com>2019-10-31 16:26:54 +0000
committerServer Team CI Bot <josh.powers+server-team-bot@canonical.com>2019-10-31 16:26:54 +0000
commit45ea695f9b4fce180c662ab4211575d64912634e (patch)
tree23aada81d0d2e1e27858865f8adb1c2f895fb240 /cloudinit
parentfcc92ad15199318abfad067c63f5ab941addc720 (diff)
downloadvyos-cloud-init-45ea695f9b4fce180c662ab4211575d64912634e.tar.gz
vyos-cloud-init-45ea695f9b4fce180c662ab4211575d64912634e.zip
Add config for ssh-key import and consuming user-data
This patch enables control over SSH public-key import and discarding supplied user-data (both disabled by default). allow-userdata: false ssh: allow_public_ssh_keys: false This feature enables closed appliances to prevent customers from unintentionally breaking the appliance which were not designed for user interaction. The downstream change for this is here: https://github.com/delphix/cloud-init/pull/4
Diffstat (limited to 'cloudinit')
-rwxr-xr-xcloudinit/config/cc_ssh.py15
-rw-r--r--cloudinit/config/tests/test_ssh.py46
-rw-r--r--cloudinit/stages.py6
3 files changed, 55 insertions, 12 deletions
diff --git a/cloudinit/config/cc_ssh.py b/cloudinit/config/cc_ssh.py
index fdd8f4d3..050285a8 100755
--- a/cloudinit/config/cc_ssh.py
+++ b/cloudinit/config/cc_ssh.py
@@ -56,9 +56,13 @@ root login is disabled, and root login opts are set to::
no-port-forwarding,no-agent-forwarding,no-X11-forwarding
Authorized keys for the default user/first user defined in ``users`` can be
-specified using `ssh_authorized_keys``. Keys should be specified as a list of
+specified using ``ssh_authorized_keys``. Keys should be specified as a list of
public keys.
+Importing ssh public keys for the default user (defined in ``users``)) is
+enabled by default. This feature may be disabled by setting
+``allow_publish_ssh_keys: false``.
+
.. note::
see the ``cc_set_passwords`` module documentation to enable/disable ssh
password authentication
@@ -91,6 +95,7 @@ public keys.
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAGEA3FSyQwBI6Z+nCSjUU ...
- ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA3I7VUf2l5gSn5uavROsc5HRDpZ ...
+ allow_public_ssh_keys: <true/false>
ssh_publish_hostkeys:
enabled: <true/false> (Defaults to true)
blacklist: <list of key types> (Defaults to [dsa])
@@ -207,7 +212,13 @@ def handle(_name, cfg, cloud, log, _args):
disable_root_opts = util.get_cfg_option_str(cfg, "disable_root_opts",
ssh_util.DISABLE_USER_OPTS)
- keys = cloud.get_public_ssh_keys() or []
+ keys = []
+ if util.get_cfg_option_bool(cfg, 'allow_public_ssh_keys', True):
+ keys = cloud.get_public_ssh_keys() or []
+ else:
+ log.debug('Skipping import of publish ssh keys per '
+ 'config setting: allow_public_ssh_keys=False')
+
if "ssh_authorized_keys" in cfg:
cfgkeys = cfg["ssh_authorized_keys"]
keys.extend(cfgkeys)
diff --git a/cloudinit/config/tests/test_ssh.py b/cloudinit/config/tests/test_ssh.py
index e7789842..0c554414 100644
--- a/cloudinit/config/tests/test_ssh.py
+++ b/cloudinit/config/tests/test_ssh.py
@@ -5,6 +5,9 @@ import os.path
from cloudinit.config import cc_ssh
from cloudinit import ssh_util
from cloudinit.tests.helpers import CiTestCase, mock
+import logging
+
+LOG = logging.getLogger(__name__)
MODPATH = "cloudinit.config.cc_ssh."
@@ -87,7 +90,7 @@ class TestHandleSsh(CiTestCase):
cc_ssh.PUBLISH_HOST_KEYS = False
cloud = self.tmp_cloud(
distro='ubuntu', metadata={'public-keys': keys})
- cc_ssh.handle("name", cfg, cloud, None, None)
+ cc_ssh.handle("name", cfg, cloud, LOG, None)
options = ssh_util.DISABLE_USER_OPTS.replace("$USER", "NONE")
options = options.replace("$DISABLE_USER", "root")
m_glob.assert_called_once_with('/etc/ssh/ssh_host_*key*')
@@ -103,6 +106,31 @@ class TestHandleSsh(CiTestCase):
@mock.patch(MODPATH + "glob.glob")
@mock.patch(MODPATH + "ug_util.normalize_users_groups")
@mock.patch(MODPATH + "os.path.exists")
+ def test_dont_allow_public_ssh_keys(self, m_path_exists, m_nug,
+ m_glob, m_setup_keys):
+ """Test allow_public_ssh_keys=False ignores ssh public keys from
+ platform.
+ """
+ cfg = {"allow_public_ssh_keys": False}
+ keys = ["key1"]
+ user = "clouduser"
+ m_glob.return_value = [] # Return no matching keys to prevent removal
+ # Mock os.path.exits to True to short-circuit the key writing logic
+ m_path_exists.return_value = True
+ m_nug.return_value = ({user: {"default": user}}, {})
+ cloud = self.tmp_cloud(
+ distro='ubuntu', metadata={'public-keys': keys})
+ cc_ssh.handle("name", cfg, cloud, LOG, None)
+
+ options = ssh_util.DISABLE_USER_OPTS.replace("$USER", user)
+ options = options.replace("$DISABLE_USER", "root")
+ self.assertEqual([mock.call(set(), user),
+ mock.call(set(), "root", options=options)],
+ m_setup_keys.call_args_list)
+
+ @mock.patch(MODPATH + "glob.glob")
+ @mock.patch(MODPATH + "ug_util.normalize_users_groups")
+ @mock.patch(MODPATH + "os.path.exists")
def test_handle_no_cfg_and_default_root(self, m_path_exists, m_nug,
m_glob, m_setup_keys):
"""Test handle with no config and a default distro user."""
@@ -115,7 +143,7 @@ class TestHandleSsh(CiTestCase):
m_nug.return_value = ({user: {"default": user}}, {})
cloud = self.tmp_cloud(
distro='ubuntu', metadata={'public-keys': keys})
- cc_ssh.handle("name", cfg, cloud, None, None)
+ cc_ssh.handle("name", cfg, cloud, LOG, None)
options = ssh_util.DISABLE_USER_OPTS.replace("$USER", user)
options = options.replace("$DISABLE_USER", "root")
@@ -140,7 +168,7 @@ class TestHandleSsh(CiTestCase):
m_nug.return_value = ({user: {"default": user}}, {})
cloud = self.tmp_cloud(
distro='ubuntu', metadata={'public-keys': keys})
- cc_ssh.handle("name", cfg, cloud, None, None)
+ cc_ssh.handle("name", cfg, cloud, LOG, None)
options = ssh_util.DISABLE_USER_OPTS.replace("$USER", user)
options = options.replace("$DISABLE_USER", "root")
@@ -165,7 +193,7 @@ class TestHandleSsh(CiTestCase):
cloud = self.tmp_cloud(
distro='ubuntu', metadata={'public-keys': keys})
cloud.get_public_ssh_keys = mock.Mock(return_value=keys)
- cc_ssh.handle("name", cfg, cloud, None, None)
+ cc_ssh.handle("name", cfg, cloud, LOG, None)
self.assertEqual([mock.call(set(keys), user),
mock.call(set(keys), "root", options="")],
@@ -196,7 +224,7 @@ class TestHandleSsh(CiTestCase):
cfg = {}
expected_call = [self.test_hostkeys[key_type] for key_type
in ['ecdsa', 'ed25519', 'rsa']]
- cc_ssh.handle("name", cfg, cloud, None, None)
+ cc_ssh.handle("name", cfg, cloud, LOG, None)
self.assertEqual([mock.call(expected_call)],
cloud.datasource.publish_host_keys.call_args_list)
@@ -225,7 +253,7 @@ class TestHandleSsh(CiTestCase):
cfg = {'ssh_publish_hostkeys': {'enabled': True}}
expected_call = [self.test_hostkeys[key_type] for key_type
in ['ecdsa', 'ed25519', 'rsa']]
- cc_ssh.handle("name", cfg, cloud, None, None)
+ cc_ssh.handle("name", cfg, cloud, LOG, None)
self.assertEqual([mock.call(expected_call)],
cloud.datasource.publish_host_keys.call_args_list)
@@ -252,7 +280,7 @@ class TestHandleSsh(CiTestCase):
cloud.datasource.publish_host_keys = mock.Mock()
cfg = {'ssh_publish_hostkeys': {'enabled': False}}
- cc_ssh.handle("name", cfg, cloud, None, None)
+ cc_ssh.handle("name", cfg, cloud, LOG, None)
self.assertFalse(cloud.datasource.publish_host_keys.call_args_list)
cloud.datasource.publish_host_keys.assert_not_called()
@@ -282,7 +310,7 @@ class TestHandleSsh(CiTestCase):
'blacklist': ['dsa', 'rsa']}}
expected_call = [self.test_hostkeys[key_type] for key_type
in ['ecdsa', 'ed25519']]
- cc_ssh.handle("name", cfg, cloud, None, None)
+ cc_ssh.handle("name", cfg, cloud, LOG, None)
self.assertEqual([mock.call(expected_call)],
cloud.datasource.publish_host_keys.call_args_list)
@@ -312,6 +340,6 @@ class TestHandleSsh(CiTestCase):
'blacklist': []}}
expected_call = [self.test_hostkeys[key_type] for key_type
in ['dsa', 'ecdsa', 'ed25519', 'rsa']]
- cc_ssh.handle("name", cfg, cloud, None, None)
+ cc_ssh.handle("name", cfg, cloud, LOG, None)
self.assertEqual([mock.call(expected_call)],
cloud.datasource.publish_host_keys.call_args_list)
diff --git a/cloudinit/stages.py b/cloudinit/stages.py
index 77c21de0..71f3a49e 100644
--- a/cloudinit/stages.py
+++ b/cloudinit/stages.py
@@ -549,7 +549,11 @@ class Init(object):
with events.ReportEventStack("consume-user-data",
"reading and applying user-data",
parent=self.reporter):
- self._consume_userdata(frequency)
+ if util.get_cfg_option_bool(self.cfg, 'allow_userdata', True):
+ self._consume_userdata(frequency)
+ else:
+ LOG.debug('allow_userdata = False: discarding user-data')
+
with events.ReportEventStack("consume-vendor-data",
"reading and applying vendor-data",
parent=self.reporter):