summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debian/changelog13
-rw-r--r--debian/patches/lp-1461242-generate-ed25519-host-keys.patch115
-rw-r--r--debian/patches/lp-1469260-fix-consumption-of-vendor-data.patch162
-rw-r--r--debian/patches/series2
4 files changed, 292 insertions, 0 deletions
diff --git a/debian/changelog b/debian/changelog
index 60b31857..ad305204 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,16 @@
+cloud-init (0.7.5-0ubuntu1.11) trusty; urgency=medium
+
+ [ Felipe Reyes ]
+ * d/patches/fix-consumption-of-vendor-data.patch:
+ - Fix consumption of vendor-data in OpenStack to allow namespacing
+ (LP: #1469260).
+
+ [ Scott Moser ]
+ * d/patches/lp-1461242-generate-ed25519-host-keys.patch:
+ - ssh: generate ed25519 host keys if supported (LP: #1461242)
+
+ -- Scott Moser <smoser@ubuntu.com> Fri, 11 Sep 2015 20:22:00 -0400
+
cloud-init (0.7.5-0ubuntu1.10) trusty; urgency=medium
[ Daniel Watkins ]
diff --git a/debian/patches/lp-1461242-generate-ed25519-host-keys.patch b/debian/patches/lp-1461242-generate-ed25519-host-keys.patch
new file mode 100644
index 00000000..f4088bd2
--- /dev/null
+++ b/debian/patches/lp-1461242-generate-ed25519-host-keys.patch
@@ -0,0 +1,115 @@
+Author: Scott Moser <smoser@ubuntu.com>
+Bug: https://launchpad.net/bugs/1461242
+Applied-Upstream: yes
+Origin: http://bazaar.launchpad.net/~cloud-init-dev/cloud-init/trunk/revision/1125
+Description: ssh: generate ed25519 host keys if supported
+ .
+ now we attempt to generate ed25519 host keys.
+ If ssh-keygen does not support it, a debug log message will be written.
+
+=== modified file 'cloudinit/config/cc_ssh.py'
+--- a/cloudinit/config/cc_ssh.py
++++ b/cloudinit/config/cc_ssh.py
+@@ -20,6 +20,7 @@
+
+ import glob
+ import os
++import sys
+
+ # Ensure this is aliased to a name not 'distros'
+ # since the module attribute 'distros'
+@@ -33,26 +34,18 @@ DISABLE_ROOT_OPTS = ("no-port-forwarding
+ "no-X11-forwarding,command=\"echo \'Please login as the user \\\"$USER\\\" "
+ "rather than the user \\\"root\\\".\';echo;sleep 10\"")
+
+-KEY_2_FILE = {
+- "rsa_private": ("/etc/ssh/ssh_host_rsa_key", 0600),
+- "rsa_public": ("/etc/ssh/ssh_host_rsa_key.pub", 0644),
+- "dsa_private": ("/etc/ssh/ssh_host_dsa_key", 0600),
+- "dsa_public": ("/etc/ssh/ssh_host_dsa_key.pub", 0644),
+- "ecdsa_private": ("/etc/ssh/ssh_host_ecdsa_key", 0600),
+- "ecdsa_public": ("/etc/ssh/ssh_host_ecdsa_key.pub", 0644),
+-}
+-
+-PRIV_2_PUB = {
+- 'rsa_private': 'rsa_public',
+- 'dsa_private': 'dsa_public',
+- 'ecdsa_private': 'ecdsa_public',
+-}
+-
+-KEY_GEN_TPL = 'o=$(ssh-keygen -yf "%s") && echo "$o" root@localhost > "%s"'
++GENERATE_KEY_NAMES = ['rsa', 'dsa', 'ecdsa', 'ed25519']
++KEY_FILE_TPL = '/etc/ssh/ssh_host_%s_key'
+
+-GENERATE_KEY_NAMES = ['rsa', 'dsa', 'ecdsa']
++CONFIG_KEY_TO_FILE = {}
++PRIV_TO_PUB = {}
++for k in GENERATE_KEY_NAMES:
++ CONFIG_KEY_TO_FILE.update({"%s_private" % k: (KEY_FILE_TPL % k, 0o600)})
++ CONFIG_KEY_TO_FILE.update(
++ {"%s_public" % k: (KEY_FILE_TPL % k + ".pub", 0o600)})
++ PRIV_TO_PUB["%s_private" % k] = "%s_public" % k
+
+-KEY_FILE_TPL = '/etc/ssh/ssh_host_%s_key'
++KEY_GEN_TPL = 'o=$(ssh-keygen -yf "%s") && echo "$o" root@localhost > "%s"'
+
+
+ def handle(_name, cfg, cloud, log, _args):
+@@ -69,15 +62,15 @@ def handle(_name, cfg, cloud, log, _args
+ if "ssh_keys" in cfg:
+ # if there are keys in cloud-config, use them
+ for (key, val) in cfg["ssh_keys"].iteritems():
+- if key in KEY_2_FILE:
+- tgt_fn = KEY_2_FILE[key][0]
+- tgt_perms = KEY_2_FILE[key][1]
++ if key in CONFIG_KEY_TO_FILE:
++ tgt_fn = CONFIG_KEY_TO_FILE[key][0]
++ tgt_perms = CONFIG_KEY_TO_FILE[key][1]
+ util.write_file(tgt_fn, val, tgt_perms)
+
+- for (priv, pub) in PRIV_2_PUB.iteritems():
++ for (priv, pub) in PRIV_TO_PUB.items():
+ if pub in cfg['ssh_keys'] or not priv in cfg['ssh_keys']:
+ continue
+- pair = (KEY_2_FILE[priv][0], KEY_2_FILE[pub][0])
++ pair = (CONFIG_KEY_TO_FILE[priv][0], CONFIG_KEY_TO_FILE[pub][0])
+ cmd = ['sh', '-xc', KEY_GEN_TPL % pair]
+ try:
+ # TODO(harlowja): Is this guard needed?
+@@ -92,18 +85,28 @@ def handle(_name, cfg, cloud, log, _args
+ genkeys = util.get_cfg_option_list(cfg,
+ 'ssh_genkeytypes',
+ GENERATE_KEY_NAMES)
++ lang_c = os.environ.copy()
++ lang_c['LANG'] = 'C'
+ for keytype in genkeys:
+ keyfile = KEY_FILE_TPL % (keytype)
++ if os.path.exists(keyfile):
++ continue
+ util.ensure_dir(os.path.dirname(keyfile))
+- if not os.path.exists(keyfile):
+- cmd = ['ssh-keygen', '-t', keytype, '-N', '', '-f', keyfile]
++ cmd = ['ssh-keygen', '-t', keytype, '-N', '', '-f', keyfile]
++
++ # TODO(harlowja): Is this guard needed?
++ with util.SeLinuxGuard("/etc/ssh", recursive=True):
+ try:
+- # TODO(harlowja): Is this guard needed?
+- with util.SeLinuxGuard("/etc/ssh", recursive=True):
+- util.subp(cmd, capture=False)
+- except:
+- util.logexc(log, "Failed generating key type %s to "
+- "file %s", keytype, keyfile)
++ out, err = util.subp(cmd, capture=True, env=lang_c)
++ sys.stdout.write(out)
++ except util.ProcessExecutionError as e:
++ err = e.stderr.lower()
++ if (e.exit_code == 1 and
++ err.lower().startswith("unknown key")):
++ log.debug("ssh-keygen: unknown key type '%s'", keytype)
++ else:
++ util.logexc(log, "Failed generating key type %s to "
++ "file %s", keytype, keyfile)
+
+ try:
+ (users, _groups) = ds.normalize_users_groups(cfg, cloud.distro)
diff --git a/debian/patches/lp-1469260-fix-consumption-of-vendor-data.patch b/debian/patches/lp-1469260-fix-consumption-of-vendor-data.patch
new file mode 100644
index 00000000..12ed15c3
--- /dev/null
+++ b/debian/patches/lp-1469260-fix-consumption-of-vendor-data.patch
@@ -0,0 +1,162 @@
+Author: Scott Moser <smoser@ubuntu.com>
+Bug: https://bugs.launchpad.net/cloud-init/+bug/1469260
+Applied-Upstream: yes
+Origin: http://bazaar.launchpad.net/~cloud-init-dev/cloud-init/trunk/revision/1013
+Description: OpenStack: fix consumption of vendor-data to allow namespacing
+ .
+ Not all vendor data is destined for cloud-init. This sanely reads
+ the vendor data as a dict, array or a string.
+
+--- a/cloudinit/sources/DataSourceConfigDrive.py
++++ b/cloudinit/sources/DataSourceConfigDrive.py
+@@ -125,7 +125,14 @@
+ self.userdata_raw = results.get('userdata')
+ self.version = results['version']
+ self.files.update(results.get('files', {}))
+- self.vendordata_raw = results.get('vendordata')
++ vd = results.get('vendordata')
++ self.vendordata_pure = vd
++ try:
++ self.vendordata_raw = openstack.convert_vendordata_json(vd)
++ except ValueError as e:
++ LOG.warn("Invalid content in vendor-data: %s", e)
++ self.vendordata_raw = None
++
+ return True
+
+
+--- a/cloudinit/sources/DataSourceOpenStack.py
++++ b/cloudinit/sources/DataSourceOpenStack.py
+@@ -143,13 +143,13 @@
+ self.version = results['version']
+ self.files.update(results.get('files', {}))
+
+- # if vendordata includes 'cloud-init', then read that explicitly
+- # for cloud-init (for namespacing).
+ vd = results.get('vendordata')
+- if isinstance(vd, dict) and 'cloud-init' in vd:
+- self.vendordata_raw = vd['cloud-init']
+- else:
+- self.vendordata_raw = vd
++ self.vendordata_pure = vd
++ try:
++ self.vendordata_raw = openstack.convert_vendordata_json(vd)
++ except ValueError as e:
++ LOG.warn("Invalid content in vendor-data: %s", e)
++ self.vendordata_raw = None
+
+ return True
+
+--- a/cloudinit/sources/helpers/openstack.py
++++ b/cloudinit/sources/helpers/openstack.py
+@@ -21,6 +21,7 @@
+ import abc
+ import base64
+ import copy
++import functools
+ import os
+
+ from cloudinit import ec2_utils
+@@ -196,6 +197,9 @@
+ If not a valid location, raise a NonReadable exception.
+ """
+
++ load_json_anytype = functools.partial(
++ util.load_json, root_types=(dict, basestring, list))
++
+ def datafiles(version):
+ files = {}
+ files['metadata'] = (
+@@ -214,7 +218,7 @@
+ files['vendordata'] = (
+ self._path_join("openstack", version, 'vendor_data.json'),
+ False,
+- util.load_json,
++ load_json_anytype,
+ )
+ return files
+
+@@ -437,3 +441,28 @@
+ return ec2_utils.get_instance_metadata(ssl_details=self.ssl_details,
+ timeout=self.timeout,
+ retries=self.retries)
++
++
++def convert_vendordata_json(data, recurse=True):
++ """ data: a loaded json *object* (strings, arrays, dicts).
++ return something suitable for cloudinit vendordata_raw.
++
++ if data is:
++ None: return None
++ string: return string
++ list: return data
++ the list is then processed in UserDataProcessor
++ dict: return convert_vendordata_json(data.get('cloud-init'))
++ """
++ if not data:
++ return None
++ if isinstance(data, (str, unicode, basestring)):
++ return data
++ if isinstance(data, list):
++ return copy.deepcopy(data)
++ if isinstance(data, dict):
++ if recurse is True:
++ return convert_vendordata_json(data.get('cloud-init'),
++ recurse=False)
++ raise ValueError("vendordata['cloud-init'] cannot be dict")
++ raise ValueError("Unknown data type for vendordata: %s" % type(data))
+--- a/tests/unittests/test_datasource/test_openstack.py
++++ b/tests/unittests/test_datasource/test_openstack.py
+@@ -19,6 +19,7 @@
+ import copy
+ import json
+ import re
++import unittest
+
+ from StringIO import StringIO
+
+@@ -241,7 +242,8 @@
+ self.assertEquals(EC2_META, ds_os.ec2_metadata)
+ self.assertEquals(USER_DATA, ds_os.userdata_raw)
+ self.assertEquals(2, len(ds_os.files))
+- self.assertEquals(VENDOR_DATA, ds_os.vendordata_raw)
++ self.assertEquals(VENDOR_DATA, ds_os.vendordata_pure)
++ self.assertEquals(ds_os.vendordata_raw, None)
+
+ @hp.activate
+ def test_bad_datasource_meta(self):
+@@ -299,3 +301,34 @@
+ found = ds_os.get_data()
+ self.assertFalse(found)
+ self.assertIsNone(ds_os.version)
++
++
++class TestVendorDataLoading(unittest.TestCase):
++ def cvj(self, data):
++ return openstack.convert_vendordata_json(data)
++
++ def test_vd_load_none(self):
++ # non-existant vendor-data should return none
++ self.assertIsNone(self.cvj(None))
++
++ def test_vd_load_string(self):
++ self.assertEqual(self.cvj("foobar"), "foobar")
++
++ def test_vd_load_list(self):
++ data = [{'foo': 'bar'}, 'mystring', list(['another', 'list'])]
++ self.assertEqual(self.cvj(data), data)
++
++ def test_vd_load_dict_no_ci(self):
++ self.assertEqual(self.cvj({'foo': 'bar'}), None)
++
++ def test_vd_load_dict_ci_dict(self):
++ self.assertRaises(ValueError, self.cvj,
++ {'foo': 'bar', 'cloud-init': {'x': 1}})
++
++ def test_vd_load_dict_ci_string(self):
++ data = {'foo': 'bar', 'cloud-init': 'VENDOR_DATA'}
++ self.assertEqual(self.cvj(data), data['cloud-init'])
++
++ def test_vd_load_dict_ci_list(self):
++ data = {'foo': 'bar', 'cloud-init': ['VD_1', 'VD_2']}
++ self.assertEqual(self.cvj(data), data['cloud-init'])
diff --git a/debian/patches/series b/debian/patches/series
index a238b1bc..8bd4a410 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -13,3 +13,5 @@ lp-1411582-azure-udev-ephemeral-disks.patch
lp-1470880-fix-gce-az-determination.patch
lp-1470890-include-regions-in-dynamic-mirror-discovery.patch
lp-1490796-azure-fix-mount_cb-for-symlinks.patch
+lp-1469260-fix-consumption-of-vendor-data.patch
+lp-1461242-generate-ed25519-host-keys.patch