2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-08-26 08:36:42 +00:00

fix(xcatd): redact secrets in the commands.log response

xcatd redacts the request in commands.log but appends the command response
verbatim. A command whose output holds a secret writes it in clear text.
Examples are tabdump passwd, gettab of a passwd column, and getcredentials.

Collect the response into a per-command buffer. Set a sensitive flag when the
command is getcredentials, an argument names a password, or the request was
redacted. When the command finishes, replace the whole buffer if the flag is
set or the buffer still holds password content, then append the buffer. A
connection can carry more than one command, so the buffer is finalized at the
next command's start and at the end of the connection.

The buffer holds the full response, so a secret split across several callbacks
is also redacted. A per-callback check cannot do this.

The word-content check is a fallback. The request classification is the main
signal. A secret with no password marker, such as the output of an xdsh cat of
a shadow file, is a pre-existing leak of the root-only log. It is out of scope.

Recovered from the lenovobuild branch. Reimplemented against master.
This commit is contained in:
Vinícius Ferrão
2026-08-18 17:01:26 -03:00
parent 91505a2141
commit eda5c35bba
+26 -1
View File
@@ -224,6 +224,8 @@ if ($tmp) {
}
my $cmdlog_alllog = "====================================================\n";
my $cmdlog_starttime=undef;
my $cmdlog_response_buffer = "";
my $cmdlog_response_sensitive = 0;
# ----used for command log end---------
my $enable_perf = $sitetab->getAttribs({'key'=>'enableperf'},'value');
@@ -2848,6 +2850,7 @@ sub service_connection {
}
# ----used for command log start----------
cmdlog_finalize_response();
$cmdlog_starttime = time();
my ($sec, $min, $hour, $mday, $mon, $year) = localtime($cmdlog_starttime);
$year += 1900;
@@ -2878,8 +2881,11 @@ sub service_connection {
}
}
# Replace passwords with 'x'
my $cmdlog_before_redact = $cmdlog_alllog;
$cmdlog_alllog = xCAT::xcatd->redact_password($cmdlog_alllog);
my $cmdlog_req_redacted = ($cmdlog_alllog ne $cmdlog_before_redact) ? 1 : 0;
$cmdlog_alllog .= "\n[Response]\n";
$cmdlog_response_sensitive = cmdlog_response_is_sensitive($req, $cmdlog_req_redacted);
# ----used for command log end----------
@@ -3064,6 +3070,7 @@ sub service_connection {
}
# ----used for command log start-------
cmdlog_finalize_response();
$cmdlog_alllog .= "[NumberNodes] $numofnodes \n";
my $reqhandletime = sprintf("%.3f", time()-$cmdlog_starttime);
$cmdlog_alllog .= "[ElapsedTime] $reqhandletime s\n";
@@ -3136,6 +3143,7 @@ sub relay_fds { # Relays file descriptors from pipes to children to the SSL sock
xCAT::MsgUtils->message("S", "Client abort requested");
# ----used for command log start-------
cmdlog_finalize_response();
$cmdlog_alllog .= "Client abort requested\n";
my $reqhandletime = sprintf("%.3f", time()-$cmdlog_starttime);
$cmdlog_alllog .= "[ElapsedTime] $reqhandletime s\n";
@@ -3606,10 +3614,27 @@ sub cmdlog_collectlog() {
}
}
}
$cmdlog_alllog .= $rsp_log;
$cmdlog_response_buffer .= $rsp_log;
return 0;
}
sub cmdlog_response_is_sensitive {
my ($req, $req_redacted) = @_;
return 1 if defined $req->{command}->[0] and $req->{command}->[0] eq 'getcredentials';
return 1 if join(' ', @{ $req->{arg} || [] }) =~ /passw/i;
return 1 if $req_redacted;
return 0;
}
sub cmdlog_finalize_response {
if ($cmdlog_response_sensitive or $cmdlog_response_buffer =~ /passw/i) {
$cmdlog_response_buffer = "*REDACTED*\n";
}
$cmdlog_alllog .= $cmdlog_response_buffer;
$cmdlog_response_buffer = "";
$cmdlog_response_sensitive = 0;
}
# --------------------------------------------------------------------------------
=head3 cmdlog_submitlog