diff --git a/backport-CVE-2023-1786-Make-user-vendor-data-sensitive-and-remove-log-permi.patch b/backport-CVE-2023-1786-Make-user-vendor-data-sensitive-and-remove-log-permi.patch new file mode 100644 index 0000000000000000000000000000000000000000..1b0d6402b18d5d48982dd3082869b87eab83ea32 --- /dev/null +++ b/backport-CVE-2023-1786-Make-user-vendor-data-sensitive-and-remove-log-permi.patch @@ -0,0 +1,138 @@ +From a378b7e4f47375458651c0972e7cd813f6fe0a6b Mon Sep 17 00:00:00 2001 +From: James Falcon +Date: Wed, 26 Apr 2023 15:11:55 -0500 +Subject: [PATCH] Make user/vendor data sensitive and remove log permissions + (#2144) + +Because user data and vendor data may contain sensitive information, +this commit ensures that any user data or vendor data written to +instance-data.json gets redacted and is only available to root user. + +Also, modify the permissions of cloud-init.log to be 640, so that +sensitive data leaked to the log isn't world readable. +Additionally, remove the logging of user data and vendor data to +cloud-init.log from the Vultr datasource. + +LP: #2013967 +CVE: CVE-2023-1786 +--- + cloudinit/sources/__init__.py | 28 +++++++++++++++++++++++++--- + cloudinit/stages.py | 4 +++- + cloudinit/sources/tests/test_init.py | 27 ++++++++++++++++++++++++++- + 3 files changed, 49 insertions(+), 6 deletions(-) + +diff --git a/cloudinit/sources/__init__.py b/cloudinit/sources/__init__.py +index 90521ba2..9d91512f 100644 +--- a/cloudinit/sources/__init__.py ++++ b/cloudinit/sources/__init__.py +@@ -111,7 +111,10 @@ def process_instance_metadata(metadata, key_path="", sensitive_keys=()): + sub_key_path = key_path + '/' + key + else: + sub_key_path = key +- if key in sensitive_keys or sub_key_path in sensitive_keys: ++ if ( ++ key.lower() in sensitive_keys ++ or sub_key_path.lower() in sensitive_keys ++ ): + md_copy['sensitive_keys'].append(sub_key_path) + if isinstance(val, str) and val.startswith('ci-b64:'): + md_copy['base64_encoded_keys'].append(sub_key_path) +@@ -133,6 +136,12 @@ def redact_sensitive_keys(metadata, redact_value=REDACT_SENSITIVE_VALUE): + + Replace any keys values listed in 'sensitive_keys' with redact_value. + """ ++ # While 'sensitive_keys' should already sanitized to only include what ++ # is in metadata, it is possible keys will overlap. For example, if ++ # "merged_cfg" and "merged_cfg/ds/userdata" both match, it's possible that ++ # "merged_cfg" will get replaced first, meaning "merged_cfg/ds/userdata" ++ # no longer represents a valid key. ++ # Thus, we still need to do membership checks in this function. + if not metadata.get('sensitive_keys', []): + return metadata + md_copy = copy.deepcopy(metadata) +@@ -140,9 +149,14 @@ def redact_sensitive_keys(metadata, redact_value=REDACT_SENSITIVE_VALUE): + path_parts = key_path.split('/') + obj = md_copy + for path in path_parts: +- if isinstance(obj[path], dict) and path != path_parts[-1]: ++ if ( ++ path in obj ++ and isinstance(obj[path], dict) ++ and path != path_parts[-1] ++ ): + obj = obj[path] +- obj[path] = redact_value ++ if path in obj: ++ obj[path] = redact_value + return md_copy + + +@@ -215,7 +229,18 @@ class DataSource(CloudInitPickleMixin, metaclass=abc.ABCMeta): + + # N-tuple of keypaths or keynames redact from instance-data.json for + # non-root users +- sensitive_metadata_keys = ('security-credentials',) ++ sensitive_metadata_keys = ( ++ "merged_cfg", ++ "security-credentials", ++ "userdata", ++ "user-data", ++ "user_data", ++ "vendordata", ++ "vendor-data", ++ # Provide ds/vendor_data to avoid redacting top-level ++ # "vendor_data": {enabled: True} ++ "ds/vendor_data", ++ ) + + def __init__(self, sys_cfg, distro, paths, ud_proc=None): + self.sys_cfg = sys_cfg +diff --git a/cloudinit/stages.py b/cloudinit/stages.py +index 65f952e7..509d8f7f 100644 +--- a/cloudinit/stages.py ++++ b/cloudinit/stages.py +@@ -203,7 +203,9 @@ class Init(object): + util.ensure_dirs(self._initial_subdirs()) + log_file = util.get_cfg_option_str(self.cfg, 'def_log_file') + if log_file: +- util.ensure_file(log_file, mode=0o640, preserve_mode=True) ++ # At this point the log file should have already been created ++ # in the setupLogging function of log.py ++ util.ensure_file(log_file, mode=0o640, preserve_mode=False) + perms = self.cfg.get('syslog_fix_perms') + if not perms: + perms = {} +diff --git a/cloudinit/sources/tests/test_init.py b/cloudinit/sources/tests/test_init.py +index 96e4dd90..005a571b 100644 +--- a/cloudinit/sources/tests/test_init.py ++++ b/cloudinit/sources/tests/test_init.py +@@ -329,9 +329,24 @@ class TestDataSource(CiTestCase): + 'local-hostname': 'test-subclass-hostname', + 'region': 'myregion', + 'some': {'security-credentials': { +- 'cred1': 'sekret', 'cred2': 'othersekret'}}}) ++ 'cred1': 'sekret', 'cred2': 'othersekret' ++ } ++ }, ++ }, ++ ) + self.assertEqual( +- ('security-credentials',), datasource.sensitive_metadata_keys) ++ ( ++ "merged_cfg", ++ "security-credentials", ++ "userdata", ++ "user-data", ++ "user_data", ++ "vendordata", ++ "vendor-data", ++ "ds/vendor_data", ++ ), ++ datasource.sensitive_metadata_keys, ++ ) + datasource.get_data() + json_file = self.tmp_path(INSTANCE_JSON_FILE, tmp) + sensitive_json_file = self.tmp_path(INSTANCE_JSON_SENSITIVE_FILE, tmp) +-- +2.27.0 + diff --git a/backport-Create-the-log-file-with-640-permissions-858.patch b/backport-Create-the-log-file-with-640-permissions-858.patch new file mode 100644 index 0000000000000000000000000000000000000000..427a560dbd5698cfe82544b7ffe0b072033311c9 --- /dev/null +++ b/backport-Create-the-log-file-with-640-permissions-858.patch @@ -0,0 +1,28 @@ +From 29ac50f2b9e7634fc59fc161d77d27e970ae8080 Mon Sep 17 00:00:00 2001 +From: Robert Schweikert +Date: Wed, 2 Jun 2021 17:10:32 -0400 +Subject: [PATCH] - Create the log file with 640 permissions (#858) + +Security scanners are often simple minded and complain on arbitrary +settings such as file permissions. For /var/log/* having world read is +one of these cases. +--- + cloudinit/stages.py | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/cloudinit/stages.py b/cloudinit/stages.py +index bbded1e9..3688be2e 100644 +--- a/cloudinit/stages.py ++++ b/cloudinit/stages.py +@@ -156,7 +156,7 @@ class Init(object): + util.ensure_dirs(self._initial_subdirs()) + log_file = util.get_cfg_option_str(self.cfg, 'def_log_file') + if log_file: +- util.ensure_file(log_file, preserve_mode=True) ++ util.ensure_file(log_file, mode=0o640, preserve_mode=True) + perms = self.cfg.get('syslog_fix_perms') + if not perms: + perms = {} +-- +2.27.0 + diff --git a/backport-stages-don-t-reset-permissions-of-cloud-init.log-eve.patch b/backport-stages-don-t-reset-permissions-of-cloud-init.log-eve.patch new file mode 100644 index 0000000000000000000000000000000000000000..2661527fd6e5566bda344f61ae4411b32a96878d --- /dev/null +++ b/backport-stages-don-t-reset-permissions-of-cloud-init.log-eve.patch @@ -0,0 +1,107 @@ +From f5b3ad741679cd42d2c145e574168dafe3ac15c1 Mon Sep 17 00:00:00 2001 +From: Daniel Watkins +Date: Fri, 23 Oct 2020 15:20:18 -0400 +Subject: [PATCH] stages: don't reset permissions of cloud-init.log every boot + (#624) + +ensure_file needed modification to support doing this, so this commit +also includes the following changes: + +test_util: add tests for util.ensure_file +util: add preserve_mode parameter to ensure_file +util: add (partial) type annotations to ensure_file + +LP: #1900837 +--- + cloudinit/stages.py | 2 +- + cloudinit/tests/test_util.py | 45 ++++++++++++++++++++++++ + cloudinit/util.py | 8 +++-- + 3 files changed, 52 insertions(+), 3 deletions(-) + +diff --git a/cloudinit/stages.py b/cloudinit/stages.py +index 765f4aab..0cce6e80 100644 +--- a/cloudinit/stages.py ++++ b/cloudinit/stages.py +@@ -148,7 +148,7 @@ class Init(object): + util.ensure_dirs(self._initial_subdirs()) + log_file = util.get_cfg_option_str(self.cfg, 'def_log_file') + if log_file: +- util.ensure_file(log_file) ++ util.ensure_file(log_file, preserve_mode=True) + perms = self.cfg.get('syslog_fix_perms') + if not perms: + perms = {} +diff --git a/cloudinit/tests/test_util.py b/cloudinit/tests/test_util.py +index 096a3037..77714928 100644 +--- a/cloudinit/tests/test_util.py ++++ b/cloudinit/tests/test_util.py +@@ -771,4 +771,49 @@ class TestRedirectOutputPreexecFn: + + assert 0 == m_setgid.call_count + ++@mock.patch("cloudinit.util.write_file") ++class TestEnsureFile: ++ """Tests for ``cloudinit.util.ensure_file``.""" ++ ++ def test_parameters_passed_through(self, m_write_file): ++ """Test the parameters in the signature are passed to write_file.""" ++ util.ensure_file( ++ mock.sentinel.path, ++ mode=mock.sentinel.mode, ++ preserve_mode=mock.sentinel.preserve_mode, ++ ) ++ ++ assert 1 == m_write_file.call_count ++ args, kwargs = m_write_file.call_args ++ assert (mock.sentinel.path,) == args ++ assert mock.sentinel.mode == kwargs["mode"] ++ assert mock.sentinel.preserve_mode == kwargs["preserve_mode"] ++ ++ @pytest.mark.parametrize( ++ "kwarg,expected", ++ [ ++ # Files should be world-readable by default ++ ("mode", 0o644), ++ # The previous behaviour of not preserving mode should be retained ++ ("preserve_mode", False), ++ ], ++ ) ++ def test_defaults(self, m_write_file, kwarg, expected): ++ """Test that ensure_file defaults appropriately.""" ++ util.ensure_file(mock.sentinel.path) ++ ++ assert 1 == m_write_file.call_count ++ _args, kwargs = m_write_file.call_args ++ assert expected == kwargs[kwarg] ++ ++ def test_static_parameters_are_passed(self, m_write_file): ++ """Test that the static write_files parameters are passed correctly.""" ++ util.ensure_file(mock.sentinel.path) ++ ++ assert 1 == m_write_file.call_count ++ _args, kwargs = m_write_file.call_args ++ assert "" == kwargs["content"] ++ assert "ab" == kwargs["omode"] ++ ++ + # vi: ts=4 expandtab +diff --git a/cloudinit/util.py b/cloudinit/util.py +index e47f1cf6..83727544 100644 +--- a/cloudinit/util.py ++++ b/cloudinit/util.py +@@ -1804,8 +1804,10 @@ def append_file(path, content): + write_file(path, content, omode="ab", mode=None) + + +-def ensure_file(path, mode=0o644): +- write_file(path, content='', omode="ab", mode=mode) ++def ensure_file(path, mode=0o644, preserve_mode=False): ++ write_file( ++ path, content="", omode="ab", mode=mode, copy_mode=preserve_mode ++ ) + + + def safe_int(possible_int): +-- +2.27.0 + diff --git a/cloud-init.spec b/cloud-init.spec index 4d2c5829efcfbe1677f522084ce2af39dc567069..c30618f9e7a76422447ceec2f4c9136c4d728cec 100644 --- a/cloud-init.spec +++ b/cloud-init.spec @@ -1,6 +1,6 @@ Name: cloud-init Version: 19.4 -Release: 10 +Release: 11 Summary: the defacto multi-distribution package that handles early initialization of a cloud instance. License: ASL 2.0 or GPLv3 URL: http://launchpad.net/cloud-init @@ -20,6 +20,9 @@ Patch9: backport-testing-add-additional-mocks-to-test_net-tests-1356.patch Patch10:fix-a-small-unitest-error.patch Patch11: backport-CVE-2022-2084.patch Patch12: remove-schema-errors-from-log-for-cloudinit-config-cc_.patch +Patch13: backport-stages-don-t-reset-permissions-of-cloud-init.log-eve.patch +Patch14: backport-Create-the-log-file-with-640-permissions-858.patch +Patch15: backport-CVE-2023-1786-Make-user-vendor-data-sensitive-and-remove-log-permi.patch Patch9000: Fix-the-error-level-logs-displayed-for-the-cloud-init-local-service.patch @@ -128,6 +131,9 @@ fi %exclude /usr/share/doc/* %changelog +* Wed May 24 2023 fuanan - 19.4-11 +- fix CVE-2023-1786 + * Sun May 14 2023 shixuantong - 19.4-10 - fix CVE-2022-2084