Proposed fix, generalize schema validation warnings messages to avoid reporting potentially sensitive user-data values in /var/log/cloud-init.log.
cloud-init.postinst fixes to redact historic sensitive logs.
diff --git a/debian/changelog b/debian/changelog index 135671ae..baec8fd1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +cloud-init (22.2-0ubuntu1~22.04.3) jammy; urgency=medium + + * d/cloud-init.postinst: redact previously leaked schema errors from logs + * Remove schema errors from log (LP: #1978422) (CVE-2022-2084) + + -- James Falcon <email address hidden> Tue, 14 Jun 2022 06:31:00 -0500 + cloud-init (22.2-0ubuntu1~22.04.2) jammy; urgency=medium
* cherry-pick a2e62738: Fix cc_phone_home requiring 'tries' (#1500) diff --git a/debian/cloud-init.postinst b/debian/cloud-init.postinst index 683ba86d..85788a98 100644 --- a/debian/cloud-init.postinst +++ b/debian/cloud-init.postinst @@ -125,6 +125,27 @@ handle_preseed_local_cloud_config() { db_unregister "${debconf_name}" || : }
+fix_1978422_redact_sensitive_logs_on_invalid_userdata_schema() { + local oldver="$1" last_bad_ver="22.2-0ubuntu1~22.04.2" + dpkg --compare-versions "$oldver" le "$last_bad_ver" || return 0 + + MSG="Redacting sensitive logs due to invalid cloud-config user-data from" + INVALID_USERDATA_LOG="Invalid cloud-config provided:" + if grep -q "${INVALID_USERDATA_LOG}" /var/log/cloud-init.log; then + echo "${MSG} /var/log/cloud-init.log" + # Redact all schema warnings between + # 'Invalid cloud-config provided:' and the next timestamped log 2022- + sed -i '/Invalid cloud-config provided:/,/2022-/{/^[^2202-]/d};s/Invalid cloud-config provided:.*/Invalid cloud-config provided. To see errors, run: sudo cloud-init schema --system/' /var/log/cloud-init.log + fi + if grep -q "${INVALID_USERDATA_LOG}" /var/log/cloud-init-output.log; then + echo "${MSG} /var/log/cloud-init-output.log" + # Redact all schema warnings between + # 'Invalid cloud-config provided:' and the public/private key gen at + # 'Generating public/private rsa key pair' OR 'Cloud' + sed -i '/Invalid cloud-config provided:/,/Generating\|Cloud/{/Cloud/b; /^[^Generating]/d};s/Invalid cloud-config provided:.*/Invalid cloud-config provided. To see errors, run: sudo cloud-init schema --system\nGenerating public\/private rsa key pair./' /var/log/cloud-init-output.log + fi +} + fix_1336855() { ### Begin fix for LP: 1336855 # fix issue where cloud-init misidentifies the location of grub and @@ -375,6 +396,9 @@ EOF cleanup_ureadahead "$2" fix_lp1889555 "$2" change_cloud_init_output_log_permissions "$2" + + # Redact schema sensitive warning logs on invalid user-data + fix_1978422_redact_sensitive_logs_on_invalid_userdata_schema "$2" fi
#DEBHELPER# diff --git a/debian/patches/cpick-b0534cbf-Remove-schema-errors-from-log b/debian/patches/cpick-b0534cbf-Remove-schema-errors-from-log new file mode 100644 index 00000000..01e886d7 --- /dev/null +++ b/debian/patches/cpick-b0534cbf-Remove-schema-errors-from-log @@ -0,0 +1,148 @@ +From b0534cbf05221b141ebd2edb5a71e94742b369a3 Mon Sep 17 00:00:00 2001 +From: James Falcon <email address hidden> +Date: Tue, 14 Jun 2022 06:24:40 -0500 +Subject: [PATCH] Remove schema errors from log + +When schema errors are encountered, the section of userdata in question +gets printed to the cloud-init log. As this could contain sensitive +data, so log a generic warning instead and redirect user to run +`cloud-init schema --system` as root. + +LP: #1978422 +--- + cloudinit/cmd/main.py | 4 +++- + cloudinit/config/schema.py | 15 +++++++++++--- + tests/integration_tests/modules/test_cli.py | 20 ++++++++++++------ + tests/unittests/config/test_schema.py | 23 ++++++++++++++++++++- + 4 files changed, 51 insertions(+), 11 deletions(-) + +--- a/cloudinit/cmd/main.py ++++ b/cloudinit/cmd/main.py +@@ -454,7 +454,9 @@ def main_init(name, args): + + # Validate user-data adheres to schema definition + if os.path.exists(init.paths.get_ipath_cur("userdata_raw")): +- validate_cloudconfig_schema(config=init.cfg, strict=False) ++ validate_cloudconfig_schema( ++ config=init.cfg, strict=False, log_details=False ++ ) + else: + LOG.debug("Skipping user-data validation. No user-data found.") + +--- a/cloudinit/config/schema.py ++++ b/cloudinit/config/schema.py +@@ -196,6 +196,7 @@ def validate_cloudconfig_schema( + schema: dict = None, + strict: bool = False, + strict_metaschema: bool = False, ++ log_details: bool = True, + ): + """Validate provided config meets the schema definition. + +@@ -208,6 +209,9 @@ def validate_cloudconfig_schema( + logging warnings. + @param strict_metaschema: Boolean, when True validates schema using strict + metaschema definition at runtime (currently unused) ++ @param log_details: Boolean, when True logs details of validation errors. ++ If there are concerns about logging sensitive userdata, this should ++ be set to False. + + @raises: SchemaValidationError when provided config does not validate + against the provided schema. +@@ -232,12 +236,17 @@ def validate_cloudconfig_schema( + errors += ((path, error.message),) + if errors: + if strict: ++ # This could output/log sensitive data + raise SchemaValidationError(errors) +- else: ++ if log_details: + messages = ["{0}: {1}".format(k, msg) for k, msg in errors] +- LOG.warning( +- "Invalid cloud-config provided:\n%s", "\n".join(messages) ++ details = "\n" + "\n".join(messages) ++ else: ++ details = ( ++ "Please run 'sudo cloud-init schema --system' to " ++ "see the schema errors." + ) ++ LOG.warning("Invalid cloud-config provided: %s", details) + + + def annotated_cloudconfig_file( +--- a/tests/integration_tests/modules/test_cli.py ++++ b/tests/integration_tests/modules/test_cli.py +@@ -18,11 +18,18 @@ runcmd: + - echo 'hi' > /var/tmp/test + """ + ++# The '-' in 'hashed-password' fails schema validation + INVALID_USER_DATA_SCHEMA = """\ + #cloud-config +-updates: +- notnetwork: -1 +-apt_pipelining: bogus ++users: ++ - default ++ - name: newsuper ++ gecos: Big Stuff ++ groups: users, admin ++ sudo: ALL=(ALL) NOPASSWD:ALL ++ hashed-password: asdfasdf ++ shell: /bin/bash ++ lock_passwd: true + """ + + +@@ -69,11 +76,12 @@ def test_invalid_userdata_schema(client: + assert result.ok + log = client.read_from_file("/var/log/cloud-init.log") + warning = ( +- "[WARNING]: Invalid cloud-config provided:\napt_pipelining: 'bogus'" +- " is not valid under any of the given schemas\nupdates: Additional" +- " properties are not allowed ('notnetwork' was unexpected)" ++ "[WARNING]: Invalid cloud-config provided: Please run " ++ "'sudo cloud-init schema --system' to see the schema errors." + ) + assert warning in log ++ assert "asdfasdf" not in log ++ + result = client.execute("cloud-init status --long") + if not result.ok: + raise AssertionError( +--- a/tests/unittests/config/test_schema.py ++++ b/tests/unittests/config/test_schema.py +@@ -304,11 +304,32 @@ class TestValidateCloudConfigSchema: + assert "cloudinit.config.schema" == module + assert logging.WARNING == log_level + assert ( +- "Invalid cloud-config provided:\np1: -1 is not of type 'string'" ++ "Invalid cloud-config provided: \np1: -1 is not of type 'string'" + == log_msg + ) + + @skipUnlessJsonSchema() ++ def test_validateconfig_schema_sensitive(self, caplog): ++ """When log_details=False, ensure details are omitted""" ++ schema = { ++ "properties": {"hashed_password": {"type": "string"}}, ++ "additionalProperties": False, ++ } ++ validate_cloudconfig_schema( ++ {"hashed-password": "secret"}, ++ schema, ++ strict=False, ++ log_details=False, ++ ) ++ [(module, log_level, log_msg)] = caplog.record_tuples ++ assert "cloudinit.config.schema" == module ++ assert logging.WARNING == log_level ++ assert ( ++ "Invalid cloud-config provided: Please run 'sudo cloud-init " ++ "schema --system' to see the schema errors." == log_msg ++ ) ++ ++ @skipUnlessJsonSchema() + def test_validateconfig_schema_emits_warning_on_missing_jsonschema( + self, caplog + ): diff --git a/debian/patches/series b/debian/patches/series index 9e8c77f7..d44f86ae 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1 +1,2 @@ cpick-a2e62738-Fix-cc_phone_home-requiring-tries-1500 +cpick-b0534cbf-Remove-schema-errors-from-log
Proposed fix, generalize schema validation warnings messages to avoid reporting potentially sensitive user-data values in /var/log/ cloud-init. log.
cloud-init.postinst fixes to redact historic sensitive logs.
diff --git a/debian/changelog b/debian/changelog 22.04.3) jammy; urgency=medium init.postinst: redact previously leaked schema errors from logs 22.04.2) jammy; urgency=medium
index 135671ae..baec8fd1 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+cloud-init (22.2-0ubuntu1~
+
+ * d/cloud-
+ * Remove schema errors from log (LP: #1978422) (CVE-2022-2084)
+
+ -- James Falcon <email address hidden> Tue, 14 Jun 2022 06:31:00 -0500
+
cloud-init (22.2-0ubuntu1~
* cherry-pick a2e62738: Fix cc_phone_home requiring 'tries' (#1500) cloud-init. postinst b/debian/ cloud-init. postinst cloud-init. postinst cloud-init. postinst preseed_ local_cloud_ config( ) {
diff --git a/debian/
index 683ba86d..85788a98 100644
--- a/debian/
+++ b/debian/
@@ -125,6 +125,27 @@ handle_
db_unregister "${debconf_name}" || :
}
+fix_1978422_ redact_ sensitive_ logs_on_ invalid_ userdata_ schema( ) { ver="22. 2-0ubuntu1~ 22.04.2" USERDATA_ LOG="Invalid cloud-config provided:" USERDATA_ LOG}" /var/log/ cloud-init. log; then cloud-init. log" /,/2022- /{/^[^2202- ]/d};s/ Invalid cloud-config provided:.*/Invalid cloud-config provided. To see errors, run: sudo cloud-init schema --system/' /var/log/ cloud-init. log USERDATA_ LOG}" /var/log/ cloud-init- output. log; then cloud-init- output. log" /,/Generating\ |Cloud/ {/Cloud/ b; /^[^Generating] /d};s/Invalid cloud-config provided:.*/Invalid cloud-config provided. To see errors, run: sudo cloud-init schema --system\ nGenerating public\/private rsa key pair./' /var/log/ cloud-init- output. log ureadahead "$2" cloud_init_ output_ log_permissions "$2" redact_ sensitive_ logs_on_ invalid_ userdata_ schema "$2"
+ local oldver="$1" last_bad_
+ dpkg --compare-versions "$oldver" le "$last_bad_ver" || return 0
+
+ MSG="Redacting sensitive logs due to invalid cloud-config user-data from"
+ INVALID_
+ if grep -q "${INVALID_
+ echo "${MSG} /var/log/
+ # Redact all schema warnings between
+ # 'Invalid cloud-config provided:' and the next timestamped log 2022-
+ sed -i '/Invalid cloud-config provided:
+ fi
+ if grep -q "${INVALID_
+ echo "${MSG} /var/log/
+ # Redact all schema warnings between
+ # 'Invalid cloud-config provided:' and the public/private key gen at
+ # 'Generating public/private rsa key pair' OR 'Cloud'
+ sed -i '/Invalid cloud-config provided:
+ fi
+}
+
fix_1336855() {
### Begin fix for LP: 1336855
# fix issue where cloud-init misidentifies the location of grub and
@@ -375,6 +396,9 @@ EOF
cleanup_
fix_lp1889555 "$2"
change_
+
+ # Redact schema sensitive warning logs on invalid user-data
+ fix_1978422_
fi
#DEBHELPER# patches/ cpick-b0534cbf- Remove- schema- errors- from-log b/debian/ patches/ cpick-b0534cbf- Remove- schema- errors- from-log patches/ cpick-b0534cbf- Remove- schema- errors- from-log 41ebd2edb5a71e9 4742b369a3 Mon Sep 17 00:00:00 2001 cmd/main. py | 4 +++- config/ schema. py | 15 +++++++++++--- on_tests/ modules/ test_cli. py | 20 ++++++++++++------ /config/ test_schema. py | 23 +++++++ +++++++ ++++++- cmd/main. py cmd/main. py exists( init.paths. get_ipath_ cur("userdata_ raw")): cloudconfig_ schema( config= init.cfg, strict=False) cloudconfig_ schema( config/ schema. py config/ schema. py cloudconfig_ schema( cloudconfig_ schema( nError when provided config does not validate cloudconfig_ schema( nError( errors) "Invalid cloud-config provided: %s", details) cloudconfig_ file( integration_ tests/modules/ test_cli. py integration_ tests/modules/ test_cli. py USER_DATA_ SCHEMA = """\ userdata_ schema( client: read_from_ file("/ var/log/ cloud-init. log") \napt_pipelinin g: 'bogus'" execute( "cloud- init status --long") unittests/ config/ test_schema. py unittests/ config/ test_schema. py udConfigSchema: config. schema" == module Schema( ) nfig_schema_ sensitive( self, caplog): erties" : False, cloudconfig_ schema( record_ tuples config. schema" == module Schema( ) nfig_schema_ emits_warning_ on_missing_ jsonschema( patches/ series b/debian/ patches/ series patches/ series patches/ series a2e62738- Fix-cc_ phone_home- requiring- tries-1500 b0534cbf- Remove- schema- errors- from-log
diff --git a/debian/
new file mode 100644
index 00000000..01e886d7
--- /dev/null
+++ b/debian/
@@ -0,0 +1,148 @@
+From b0534cbf05221b1
+From: James Falcon <email address hidden>
+Date: Tue, 14 Jun 2022 06:24:40 -0500
+Subject: [PATCH] Remove schema errors from log
+
+When schema errors are encountered, the section of userdata in question
+gets printed to the cloud-init log. As this could contain sensitive
+data, so log a generic warning instead and redirect user to run
+`cloud-init schema --system` as root.
+
+LP: #1978422
+---
+ cloudinit/
+ cloudinit/
+ tests/integrati
+ tests/unittests
+ 4 files changed, 51 insertions(+), 11 deletions(-)
+
+--- a/cloudinit/
++++ b/cloudinit/
+@@ -454,7 +454,9 @@ def main_init(name, args):
+
+ # Validate user-data adheres to schema definition
+ if os.path.
+- validate_
++ validate_
++ config=init.cfg, strict=False, log_details=False
++ )
+ else:
+ LOG.debug("Skipping user-data validation. No user-data found.")
+
+--- a/cloudinit/
++++ b/cloudinit/
+@@ -196,6 +196,7 @@ def validate_
+ schema: dict = None,
+ strict: bool = False,
+ strict_metaschema: bool = False,
++ log_details: bool = True,
+ ):
+ """Validate provided config meets the schema definition.
+
+@@ -208,6 +209,9 @@ def validate_
+ logging warnings.
+ @param strict_metaschema: Boolean, when True validates schema using strict
+ metaschema definition at runtime (currently unused)
++ @param log_details: Boolean, when True logs details of validation errors.
++ If there are concerns about logging sensitive userdata, this should
++ be set to False.
+
+ @raises: SchemaValidatio
+ against the provided schema.
+@@ -232,12 +236,17 @@ def validate_
+ errors += ((path, error.message),)
+ if errors:
+ if strict:
++ # This could output/log sensitive data
+ raise SchemaValidatio
+- else:
++ if log_details:
+ messages = ["{0}: {1}".format(k, msg) for k, msg in errors]
+- LOG.warning(
+- "Invalid cloud-config provided:\n%s", "\n".join(messages)
++ details = "\n" + "\n".join(messages)
++ else:
++ details = (
++ "Please run 'sudo cloud-init schema --system' to "
++ "see the schema errors."
+ )
++ LOG.warning(
+
+
+ def annotated_
+--- a/tests/
++++ b/tests/
+@@ -18,11 +18,18 @@ runcmd:
+ - echo 'hi' > /var/tmp/test
+ """
+
++# The '-' in 'hashed-password' fails schema validation
+ INVALID_
+ #cloud-config
+-updates:
+- notnetwork: -1
+-apt_pipelining: bogus
++users:
++ - default
++ - name: newsuper
++ gecos: Big Stuff
++ groups: users, admin
++ sudo: ALL=(ALL) NOPASSWD:ALL
++ hashed-password: asdfasdf
++ shell: /bin/bash
++ lock_passwd: true
+ """
+
+
+@@ -69,11 +76,12 @@ def test_invalid_
+ assert result.ok
+ log = client.
+ warning = (
+- "[WARNING]: Invalid cloud-config provided:
+- " is not valid under any of the given schemas\nupdates: Additional"
+- " properties are not allowed ('notnetwork' was unexpected)"
++ "[WARNING]: Invalid cloud-config provided: Please run "
++ "'sudo cloud-init schema --system' to see the schema errors."
+ )
+ assert warning in log
++ assert "asdfasdf" not in log
++
+ result = client.
+ if not result.ok:
+ raise AssertionError(
+--- a/tests/
++++ b/tests/
+@@ -304,11 +304,32 @@ class TestValidateClo
+ assert "cloudinit.
+ assert logging.WARNING == log_level
+ assert (
+- "Invalid cloud-config provided:\np1: -1 is not of type 'string'"
++ "Invalid cloud-config provided: \np1: -1 is not of type 'string'"
+ == log_msg
+ )
+
+ @skipUnlessJson
++ def test_validateco
++ """When log_details=False, ensure details are omitted"""
++ schema = {
++ "properties": {"hashed_password": {"type": "string"}},
++ "additionalProp
++ }
++ validate_
++ {"hashed-password": "secret"},
++ schema,
++ strict=False,
++ log_details=False,
++ )
++ [(module, log_level, log_msg)] = caplog.
++ assert "cloudinit.
++ assert logging.WARNING == log_level
++ assert (
++ "Invalid cloud-config provided: Please run 'sudo cloud-init "
++ "schema --system' to see the schema errors." == log_msg
++ )
++
++ @skipUnlessJson
+ def test_validateco
+ self, caplog
+ ):
diff --git a/debian/
index 9e8c77f7..d44f86ae 100644
--- a/debian/
+++ b/debian/
@@ -1 +1,2 @@
cpick-
+cpick-