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
next prev 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