Blame SOURCES/ci-Azure-Ensure-platform-random_seed-is-always-serializ.patch

936480
From 2428320b2157a0fcc0f35bea12584286ebd02aab Mon Sep 17 00:00:00 2001
936480
From: Eduardo Otubo <otubo@redhat.com>
936480
Date: Wed, 15 May 2019 12:15:25 +0200
936480
Subject: [PATCH 1/5] Azure: Ensure platform random_seed is always serializable
936480
 as JSON.
936480
936480
RH-Author: Eduardo Otubo <otubo@redhat.com>
936480
Message-id: <20190515121529.11191-2-otubo@redhat.com>
936480
Patchwork-id: 87881
936480
O-Subject: [rhel-7 cloud-init PATCHv2 1/5] Azure: Ensure platform random_seed is always serializable as JSON.
936480
Bugzilla: 1687565
936480
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
936480
RH-Acked-by: Mohammed Gamal <mgamal@redhat.com>
936480
936480
From: "Jason Zions (MSFT)" <jasonzio@microsoft.com>
936480
936480
BZ: 1687565
936480
BRANCH: rhel7/master-18.5
936480
UPSTREAM: 0dc3a77f
936480
BREW: 21696239
936480
936480
commit 0dc3a77f41f4544e4cb5a41637af7693410d4cdf
936480
Author: Jason Zions (MSFT) <jasonzio@microsoft.com>
936480
Date:   Tue Mar 26 18:53:50 2019 +0000
936480
936480
    Azure: Ensure platform random_seed is always serializable as JSON.
936480
936480
    The Azure platform surfaces random bytes into /sys via Hyper-V.
936480
    Python 2.7 json.dump() raises an exception if asked to convert
936480
    a str with non-character content, and python 3.0 json.dump()
936480
    won't serialize a "bytes" value. As a result, c-i instance
936480
    data is often not written by Azure, making reboots slower (c-i
936480
    has to repeat work).
936480
936480
    The random data is base64-encoded and then decoded into a string
936480
    (str or unicode depending on the version of Python in use). The
936480
    base64 string has just as many bits of entropy, so we're not
936480
    throwing away useful "information", but we can be certain
936480
    json.dump() will correctly serialize the bits.
936480
936480
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
936480
936480
Conflicts:
936480
    tests/unittests/test_datasource/test_azure.py
936480
    Skipped the commit edf052c as it removes support for python-2.6
936480
936480
Signed-off-by: Eduardo Otubo <otubo@redhat.com>
936480
---
936480
 cloudinit/sources/DataSourceAzure.py          | 24 +++++++++++++++++++-----
936480
 tests/data/azure/non_unicode_random_string    |  1 +
936480
 tests/unittests/test_datasource/test_azure.py | 24 ++++++++++++++++++++++--
936480
 3 files changed, 42 insertions(+), 7 deletions(-)
936480
 create mode 100644 tests/data/azure/non_unicode_random_string
936480
936480
diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py
936480
index 2062ca5..a768b2c 100644
936480
--- a/cloudinit/sources/DataSourceAzure.py
936480
+++ b/cloudinit/sources/DataSourceAzure.py
936480
@@ -54,6 +54,7 @@ REPROVISION_MARKER_FILE = "/var/lib/cloud/data/poll_imds"
936480
 REPORTED_READY_MARKER_FILE = "/var/lib/cloud/data/reported_ready"
936480
 AGENT_SEED_DIR = '/var/lib/waagent'
936480
 IMDS_URL = "http://169.254.169.254/metadata/"
936480
+PLATFORM_ENTROPY_SOURCE = "/sys/firmware/acpi/tables/OEM0"
936480
 
936480
 # List of static scripts and network config artifacts created by
936480
 # stock ubuntu suported images.
936480
@@ -195,6 +196,8 @@ if util.is_FreeBSD():
936480
         RESOURCE_DISK_PATH = "/dev/" + res_disk
936480
     else:
936480
         LOG.debug("resource disk is None")
936480
+    # TODO Find where platform entropy data is surfaced
936480
+    PLATFORM_ENTROPY_SOURCE = None
936480
 
936480
 BUILTIN_DS_CONFIG = {
936480
     'agent_command': AGENT_START_BUILTIN,
936480
@@ -1100,16 +1103,27 @@ def _check_freebsd_cdrom(cdrom_dev):
936480
     return False
936480
 
936480
 
936480
-def _get_random_seed():
936480
+def _get_random_seed(source=PLATFORM_ENTROPY_SOURCE):
936480
     """Return content random seed file if available, otherwise,
936480
        return None."""
936480
     # azure / hyper-v provides random data here
936480
-    # TODO. find the seed on FreeBSD platform
936480
     # now update ds_cfg to reflect contents pass in config
936480
-    if util.is_FreeBSD():
936480
+    if source is None:
936480
         return None
936480
-    return util.load_file("/sys/firmware/acpi/tables/OEM0",
936480
-                          quiet=True, decode=False)
936480
+    seed = util.load_file(source, quiet=True, decode=False)
936480
+
936480
+    # The seed generally contains non-Unicode characters. load_file puts
936480
+    # them into a str (in python 2) or bytes (in python 3). In python 2,
936480
+    # bad octets in a str cause util.json_dumps() to throw an exception. In
936480
+    # python 3, bytes is a non-serializable type, and the handler load_file
936480
+    # uses applies b64 encoding *again* to handle it. The simplest solution
936480
+    # is to just b64encode the data and then decode it to a serializable
936480
+    # string. Same number of bits of entropy, just with 25% more zeroes.
936480
+    # There's no need to undo this base64-encoding when the random seed is
936480
+    # actually used in cc_seed_random.py.
936480
+    seed = base64.b64encode(seed).decode()
936480
+
936480
+    return seed
936480
 
936480
 
936480
 def list_possible_azure_ds_devs():
936480
diff --git a/tests/data/azure/non_unicode_random_string b/tests/data/azure/non_unicode_random_string
936480
new file mode 100644
936480
index 0000000..b9ecefb
936480
--- /dev/null
936480
+++ b/tests/data/azure/non_unicode_random_string
936480
@@ -0,0 +1 @@
936480
+OEM0d\x00\x00\x00\x01\x80VRTUALMICROSFT\x02\x17\x00\x06MSFT\x97\x00\x00\x00C\xb4{V\xf4X%\x061x\x90\x1c\xfen\x86\xbf~\xf5\x8c\x94&\x88\xed\x84\xf9B\xbd\xd3\xf1\xdb\xee:\xd9\x0fc\x0e\x83(\xbd\xe3'\xfc\x85,\xdf\xf4\x13\x99N\xc5\xf3Y\x1e\xe3\x0b\xa4H\x08J\xb9\xdcdb$
936480
\ No newline at end of file
936480
diff --git a/tests/unittests/test_datasource/test_azure.py b/tests/unittests/test_datasource/test_azure.py
936480
index 417d86a..eacf225 100644
936480
--- a/tests/unittests/test_datasource/test_azure.py
936480
+++ b/tests/unittests/test_datasource/test_azure.py
936480
@@ -7,11 +7,11 @@ from cloudinit.sources import (
936480
     UNSET, DataSourceAzure as dsaz, InvalidMetaDataException)
936480
 from cloudinit.util import (b64e, decode_binary, load_file, write_file,
936480
                             find_freebsd_part, get_path_dev_freebsd,
936480
-                            MountFailedError)
936480
+                            MountFailedError, json_dumps, load_json)
936480
 from cloudinit.version import version_string as vs
936480
 from cloudinit.tests.helpers import (
936480
     HttprettyTestCase, CiTestCase, populate_dir, mock, wrap_and_call,
936480
-    ExitStack, PY26, SkipTest)
936480
+    ExitStack, PY26, SkipTest, resourceLocation)
936480
 
936480
 import crypt
936480
 import httpretty
936480
@@ -1924,4 +1924,24 @@ class TestWBIsPlatformViable(CiTestCase):
936480
             self.logs.getvalue())
936480
 
936480
 
936480
+class TestRandomSeed(CiTestCase):
936480
+    """Test proper handling of random_seed"""
936480
+
936480
+    def test_non_ascii_seed_is_serializable(self):
936480
+        """Pass if a random string from the Azure infrastructure which
936480
+        contains at least one non-Unicode character can be converted to/from
936480
+        JSON without alteration and without throwing an exception.
936480
+        """
936480
+        path = resourceLocation("azure/non_unicode_random_string")
936480
+        result = dsaz._get_random_seed(path)
936480
+
936480
+        obj = {'seed': result}
936480
+        try:
936480
+            serialized = json_dumps(obj)
936480
+            deserialized = load_json(serialized)
936480
+        except UnicodeDecodeError:
936480
+            self.fail("Non-serializable random seed returned")
936480
+
936480
+        self.assertEqual(deserialized['seed'], result)
936480
+
936480
 # vi: ts=4 expandtab
936480
-- 
936480
1.8.3.1
936480