public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Thomas Lamprecht <t.lamprecht@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH common 04/10] section config: add redaction of sensitive values in raw configs
Date: Sun,  1 Nov 2026 22:34:53 +0100	[thread overview]
Message-ID: <20261101213835.353604-5-t.lamprecht@proxmox.com> (raw)
In-Reply-To: <20261101213835.353604-1-t.lamprecht@proxmox.com>

Section config files like the SDN IPAM and DNS configs hold API tokens
and are included verbatim in the system report, so they need the same
raw-text redaction as key/value config files.

Sections are delimited as in parse_config, so each value is checked
against the schema of the section the parser assigns it to. Sections of
a type without a registered plugin are still checked against the
properties that are not isolated to a plugin.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
 src/PVE/SectionConfig.pm                      | 48 +++++++++++++++++++
 .../section_config_property_isolation_test.pl | 43 +++++++++++++++++
 test/section_config_test.pl                   | 40 ++++++++++++++++
 3 files changed, 131 insertions(+)

diff --git a/src/PVE/SectionConfig.pm b/src/PVE/SectionConfig.pm
index 1febf76..04a79de 100644
--- a/src/PVE/SectionConfig.pm
+++ b/src/PVE/SectionConfig.pm
@@ -1807,6 +1807,54 @@ sub write_config {
     return $out;
 }
 
+=head3 $base->redact_raw_config(...)
+
+=head3 $base->redact_raw_config($raw, $placeholder)
+
+    $redacted = PVE::Example::BasePlugin->redact_raw_config($raw, $placeholder)
+    $redacted = $class->redact_raw_config($raw, $placeholder)
+
+Returns the contents of a C<L<PVE::SectionConfig>> file with the value of every
+property marked as C<sensitive> replaced by C<$placeholder>, including sensitive
+keys of property strings.
+
+Works on the raw text, so lines that
+C<L<< parse_config()|/$base->parse_config(...) >>> would skip as invalid are
+kept. As in C<L<< parse_config()|/$base->parse_config(...) >>>, a section ends
+at the next empty line. Outside of sections of a known
+I<L<< type|/$plugin->type() >>>, only properties that are not isolated to a
+plugin are recognized.
+
+=cut
+
+sub redact_raw_config($class, $raw, $placeholder) {
+    my ($type, $in_section) = ('', 0);
+    my @lines = split(/\n/, $raw, -1);
+    for my $line (@lines) {
+        next if $line =~ m/^\s*#/;
+
+        if ($line eq '') {
+            ($type, $in_section) = ('', 0);
+            next;
+        }
+
+        if (!$in_section) {
+            my ($header_type) = $class->parse_section_header($line);
+            if (defined($header_type)) {
+                ($type, $in_section) = ($header_type, 1);
+                next;
+            }
+        }
+
+        if (my ($prefix, $key, $value) = $line =~ m/^(\s+(\S+)\s+)(.*)$/) {
+            my $schema = $class->get_property_schema($type, $key) or next;
+            $line =
+                $prefix . PVE::JSONSchema::redact_sensitive_value($schema, $value, $placeholder);
+        }
+    }
+    return join("\n", @lines);
+}
+
 sub assert_if_modified {
     my ($cfg, $digest) = @_;
 
diff --git a/test/section_config_property_isolation_test.pl b/test/section_config_property_isolation_test.pl
index 11b3142..a487952 100755
--- a/test/section_config_property_isolation_test.pl
+++ b/test/section_config_property_isolation_test.pl
@@ -85,6 +85,7 @@ sub properties {
             description => 'Another field',
             type => 'string',
             optional => 1,
+            sensitive => 1,
         },
         arrayfield => {
             description => "Array Field with property string",
@@ -395,6 +396,7 @@ my sub make_schema($is_update) {
                                 description => 'Another field',
                                 type => 'string',
                                 optional => 1,
+                                sensitive => 1,
                             },
                             arrayfield => {
                                 description => "Array Field with property string",
@@ -481,6 +483,47 @@ ok(
     "property lookup does not add a property list",
 );
 
+my $redact_in = <<"EOF";
+one: a
+	another secret
+
+two: b
+	another visible
+
+unknown: c
+	another isolated
+
+two: d
+one: d-ignored
+	another visible
+
+one: e
+two: e-ignored
+	another secret
+	another
+EOF
+my $redact_out = <<"EOF";
+one: a
+	another <redacted>
+
+two: b
+	another visible
+
+unknown: c
+	another isolated
+
+two: d
+one: d-ignored
+	another visible
+
+one: e
+two: e-ignored
+	another <redacted>
+	another
+EOF
+is(Conf->redact_raw_config($redact_in, '<redacted>'), $redact_out,
+    "property-isolation redact test");
+
 done_testing();
 
 1;
diff --git a/test/section_config_test.pl b/test/section_config_test.pl
index 2cdff98..3e52834 100755
--- a/test/section_config_test.pl
+++ b/test/section_config_test.pl
@@ -75,6 +75,7 @@ sub properties {
         another => {
             description => 'Another field',
             type => 'string',
+            sensitive => 1,
         },
     };
 }
@@ -115,6 +116,7 @@ sub properties {
                     subfield1 => {
                         type => 'string',
                         description => 'first subfield',
+                        sensitive => 1,
                     },
                     subfield2 => {
                         type => 'integer',
@@ -300,6 +302,7 @@ my $expected_create_schema = {
                     'subfield1' => {
                         'description' => 'first subfield',
                         'type' => 'string',
+                        'sensitive' => 1,
                     },
                 },
                 'type' => 'string',
@@ -311,6 +314,7 @@ my $expected_create_schema = {
             'description' => 'Another field',
             'optional' => 1,
             'type' => 'string',
+            'sensitive' => 1,
         },
     },
 };
@@ -369,6 +373,7 @@ my $expected_update_schema = {
                     subfield1 => {
                         description => 'first subfield',
                         type => 'string',
+                        sensitive => 1,
                     },
                 },
             },
@@ -379,11 +384,46 @@ my $expected_update_schema = {
             description => 'Another field',
             optional => 1,
             type => 'string',
+            sensitive => 1,
         },
     },
 };
 is_deeply($update_schema, $expected_update_schema, "update schema test");
 
+my $redact_in = <<"EOF";
+one: t1
+	common foo
+	another secret1
+
+# another: comment
+two: t2
+	field2 5
+	another  secret2
+invalid line
+	arrayfield subfield2=2,subfield1=secret3
+
+bad: unknown
+	another secret4
+	unknownfield visible
+EOF
+my $redact_out = <<"EOF";
+one: t1
+	common foo
+	another <redacted>
+
+# another: comment
+two: t2
+	field2 5
+	another  <redacted>
+invalid line
+	arrayfield subfield2=2,subfield1=<redacted>
+
+bad: unknown
+	another <redacted>
+	unknownfield visible
+EOF
+is(Conf->redact_raw_config($redact_in, '<redacted>'), $redact_out, "redact raw config");
+
 done_testing();
 
 1;
-- 
2.47.3





  parent reply	other threads:[~2026-11-01 21:39 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-01 21:34 [PATCH common,qemu-server,network,manager 0/10] mark secrets in schemas and redact them from the system report Thomas Lamprecht
2026-11-01 21:34 ` [PATCH common 01/10] section config: do not add isolated property lists on lookups Thomas Lamprecht
2026-11-01 21:34 ` [PATCH common 02/10] schema: allow marking properties as sensitive Thomas Lamprecht
2026-11-01 21:34 ` [PATCH common 03/10] schema: add helpers to redact sensitive values Thomas Lamprecht
2026-11-01 21:34 ` Thomas Lamprecht [this message]
2026-11-01 21:34 ` [PATCH qemu-server 05/10] cloud-init: mark the password as sensitive Thomas Lamprecht
2026-11-01 21:34 ` [PATCH qemu-server 06/10] config: hide sensitive values of unparsable lines in warnings Thomas Lamprecht
2026-11-01 21:34 ` [PATCH network 07/10] sdn: ipam, dns: mark API tokens as sensitive Thomas Lamprecht
2026-11-01 21:34 ` [PATCH manager 08/10] report: redact sensitive values from guest, storage and SDN configs Thomas Lamprecht
2026-11-01 21:34 ` [PATCH manager 09/10] report: redact credentials in URLs, options and environment variables Thomas Lamprecht
2026-11-01 21:34 ` [PATCH manager 10/10] pvereport: man page: document which values get redacted Thomas Lamprecht

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261101213835.353604-5-t.lamprecht@proxmox.com \
    --to=t.lamprecht@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal