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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.