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 manager 09/10] report: redact credentials in URLs, options and environment variables
Date: Sun,  1 Nov 2026 22:34:58 +0100	[thread overview]
Message-ID: <20261101213835.353604-10-t.lamprecht@proxmox.com> (raw)
In-Reply-To: <20261101213835.353604-1-t.lamprecht@proxmox.com>

Some credentials in the report are not covered by any schema: the
password in a URL, as used in APT sources or the HTTP proxy setting of
datacenter.cfg, mount options like password= in fstab, and container
environment variables like DB_PASSWORD, which the env property stores as
raw lxc keys. Replace passwords in URLs and values whose name suggests a
credential, in the whole report.

This is a heuristic. It also catches such options on command lines as
shown by top, but not values passed as separate arguments, nor the part
of a value after a space or comma, except for environment variables.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
---
 PVE/Report.pm       | 22 ++++++++++++-
 test/Makefile       |  6 +++-
 test/report_test.pl | 78 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 104 insertions(+), 2 deletions(-)
 create mode 100755 test/report_test.pl

diff --git a/PVE/Report.pm b/PVE/Report.pm
index 7182c091d..fa7fc4280 100644
--- a/PVE/Report.pm
+++ b/PVE/Report.pm
@@ -67,6 +67,26 @@ my sub redact_section_config {
     return $class->redact_raw_config($raw, $redaction_placeholder);
 }
 
+# names like password, db_pass, ADMIN_TOKEN, SECRET_KEY or api_key
+my $credential_name_re = qr/
+    (?<![a-z0-9])
+    (?: (?:[a-z0-9]+_)* (?:password2?|passwd|pass|secret|token) (?:_[a-z0-9]+)* | (?:[a-z0-9]+_)+ key )
+/ix;
+
+# credentials in URLs, like in APT sources or proxy settings, in container environment variables
+# and in other name=value pairs, like mount options
+sub redact_credentials {
+    my ($text) = @_;
+
+    $text =~
+        s{(?<![a-z0-9+.-])([a-z][a-z0-9+.-]*+://[^\s/:]*):[^\s/]+@}{$1:$redaction_placeholder\@}gi;
+    $text =~
+        s/^(lxc\.environment(?:\.\w+)?(?::|\s*=)\s*$credential_name_re=).+$/$1$redaction_placeholder/gm;
+    $text =~ s/($credential_name_re=)(?:,,|[^\s,])+/$1$redaction_placeholder/g;
+
+    return $text;
+}
+
 # command -v is the posix equivalent of 'which'
 my sub cmd_exists { system("command -v '$_[0]' > /dev/null 2>&1") == 0 }
 
@@ -276,7 +296,7 @@ sub generate {
         }
     }
 
-    return $report;
+    return redact_credentials($report);
 }
 
 1;
diff --git a/test/Makefile b/test/Makefile
index 2dfd20a41..91761579d 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -5,7 +5,7 @@ all:
 export PERLLIB=..
 
 .PHONY: check
-check: test-replication test-balloon test-vzdump test-osd test-ceph-health test-ceph-services test-ceph-auth test-ceph-key-migration test-ceph-lockbox-migration test-custom-cpu-models test-pvesh
+check: test-replication test-balloon test-vzdump test-osd test-ceph-health test-ceph-services test-ceph-auth test-ceph-key-migration test-ceph-lockbox-migration test-custom-cpu-models test-pvesh test-report
 
 .PHONY: test-balloon
 test-balloon:
@@ -61,6 +61,10 @@ test-custom-cpu-models:
 test-pvesh:
 	./pvesh_test.pl
 
+.PHONY: test-report
+test-report:
+	./report_test.pl
+
 .PHONY: install
 install:
 
diff --git a/test/report_test.pl b/test/report_test.pl
new file mode 100755
index 000000000..444e4344f
--- /dev/null
+++ b/test/report_test.pl
@@ -0,0 +1,78 @@
+#!/usr/bin/perl
+
+use v5.36;
+
+use lib ('.', '..');
+
+use Test::More;
+
+use PVE::Report;
+
+my $tests = [
+    [
+        'APT source with credentials',
+        'deb https://user:s3cr:et@deb.example.com/debian trixie main',
+        'deb https://user:<redacted>@deb.example.com/debian trixie main',
+    ],
+    [
+        'URL with an unencoded @ in the password',
+        'http_proxy: http://user:p@ss@proxy.example.com:3128/',
+        'http_proxy: http://user:<redacted>@proxy.example.com:3128/',
+    ],
+    [
+        'URL with an @ in the user name',
+        'http_proxy: http://user@corp.example:s3cret@proxy.example.com:3128/',
+        'http_proxy: http://user@corp.example:<redacted>@proxy.example.com:3128/',
+    ],
+    [
+        'proxy URL with port and without credentials',
+        'http_proxy: http://proxy.example.com:3128/',
+        'http_proxy: http://proxy.example.com:3128/',
+    ],
+    [
+        'URL with user only',
+        'URIs: https://user@deb.example.com/debian',
+        'URIs: https://user@deb.example.com/debian',
+    ],
+    [
+        'URL with user and port only',
+        'ssh://root@host.example:22/srv',
+        'ssh://root@host.example:22/srv',
+    ],
+    [
+        'CIFS mount options',
+        '//srv/share /mnt cifs username=foo,password=bar,uid=0 0 0',
+        '//srv/share /mnt cifs username=foo,password=<redacted>,uid=0 0 0',
+    ],
+    [
+        'CIFS password with an escaped comma',
+        'username=foo,password=a,,b,password2=new,uid=0',
+        'username=foo,password=<redacted>,password2=<redacted>,uid=0',
+    ],
+    [
+        'Ceph mount options',
+        'mon1:/ /mnt ceph name=admin,secret=AQB+abc==,secretfile=/etc/ceph/s 0 0',
+        'mon1:/ /mnt ceph name=admin,secret=<redacted>,secretfile=/etc/ceph/s 0 0',
+    ],
+    [
+        'command line options',
+        'tool --pass=hunter2 --bypass=1 --db-password=x --api_key=y passthrough=on',
+        'tool --pass=<redacted> --bypass=1 --db-password=<redacted> --api_key=<redacted> passthrough=on',
+    ],
+    [
+        'container environment',
+        "lxc.environment.runtime: ADMIN_TOKEN=\$argon2id\$v=19\$m=65540,t=3,p=4\$c2FsdA\$aGFzaA\n"
+            . "lxc.environment.runtime: DB_PASSWORD=correct horse\n"
+            . "lxc.environment.runtime: TZ=Europe/Vienna\n",
+        "lxc.environment.runtime: ADMIN_TOKEN=<redacted>\n"
+            . "lxc.environment.runtime: DB_PASSWORD=<redacted>\n"
+            . "lxc.environment.runtime: TZ=Europe/Vienna\n",
+    ],
+];
+
+for my $test ($tests->@*) {
+    my ($name, $in, $out) = $test->@*;
+    is(PVE::Report::redact_credentials($in), $out, $name);
+}
+
+done_testing();
-- 
2.47.3





  parent reply	other threads:[~2026-11-01 21:40 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 ` [PATCH common 04/10] section config: add redaction of sensitive values in raw configs Thomas Lamprecht
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 ` Thomas Lamprecht [this message]
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-10-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