This commit is contained in:
Edward-x 2026-01-22 14:32:23 +00:00 committed by GitHub
commit 607bf640c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 95 additions and 42 deletions

View File

@ -37,7 +37,8 @@ public final class LibvirtUpdateHostPasswordCommandWrapper extends CommandWrappe
final String newPassword = command.getNewPassword(); final String newPassword = command.getNewPassword();
final Script script = libvirtUtilitiesHelper.buildScript(libvirtComputingResource.getUpdateHostPasswdPath()); final Script script = libvirtUtilitiesHelper.buildScript(libvirtComputingResource.getUpdateHostPasswdPath());
script.add(username, newPassword); script.add(username);
script.addSensitive(newPassword);
final String result = script.execute(); final String result = script.execute();
if (result != null) { if (result != null) {

View File

@ -45,9 +45,10 @@ public final class CitrixUpdateHostPasswordCommandWrapper extends CommandWrapper
Pair<Boolean, String> result; Pair<Boolean, String> result;
try { try {
logger.debug("Executing command in Host: " + cmdLine); logger.debug("Executing command in Host: " + xenServerUtilitiesHelper.buildCommandLine(SCRIPT_CMD_PATH,
VRScripts.UPDATE_HOST_PASSWD, username, "******"));
final String hostPassword = citrixResourceBase.getPwdFromQueue(); final String hostPassword = citrixResourceBase.getPwdFromQueue();
result = xenServerUtilitiesHelper.executeSshWrapper(hostIp, 22, username, null, hostPassword, cmdLine.toString()); result = xenServerUtilitiesHelper.executeSshWrapper(hostIp, 22, username, null, hostPassword, cmdLine);
} catch (final Exception e) { } catch (final Exception e) {
return new Answer(command, false, e.getMessage()); return new Answer(command, false, e.getMessage());
} }

View File

@ -30,8 +30,10 @@ import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -43,7 +45,6 @@ import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.cloudstack.utils.security.KeyStoreUtils;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -66,8 +67,8 @@ public class Script implements Callable<String> {
private static final int DEFAULT_TIMEOUT = 3600 * 1000; /* 1 hour */ private static final int DEFAULT_TIMEOUT = 3600 * 1000; /* 1 hour */
private volatile boolean _isTimeOut = false; private volatile boolean _isTimeOut = false;
private boolean _passwordCommand = false;
private boolean avoidLoggingCommand = false; private boolean avoidLoggingCommand = false;
private final Set<Integer> sensitiveArgIndices = new HashSet<>();
private static final ScheduledExecutorService s_executors = Executors.newScheduledThreadPool(10, new NamedThreadFactory("Script")); private static final ScheduledExecutorService s_executors = Executors.newScheduledThreadPool(10, new NamedThreadFactory("Script"));
@ -145,6 +146,11 @@ public class Script implements Callable<String> {
_command.add(param); _command.add(param);
} }
public void addSensitive(String param) {
_command.add(param);
sensitiveArgIndices.add(_command.size() - 1);
}
public Script set(String name, String value) { public Script set(String name, String value) {
_command.add(name); _command.add(name);
_command.add(value); _command.add(value);
@ -163,7 +169,7 @@ public class Script implements Callable<String> {
if (sanitizeViCmdParameter(cmd, builder) || sanitizeRbdFileFormatCmdParameter(cmd, builder)) { if (sanitizeViCmdParameter(cmd, builder) || sanitizeRbdFileFormatCmdParameter(cmd, builder)) {
continue; continue;
} }
if (obscureParam) { if (obscureParam || sensitiveArgIndices.contains(i)) {
builder.append("******").append(" "); builder.append("******").append(" ");
obscureParam = false; obscureParam = false;
} else { } else {
@ -172,7 +178,6 @@ public class Script implements Callable<String> {
if ("-y".equals(cmd) || "-z".equals(cmd)) { if ("-y".equals(cmd) || "-z".equals(cmd)) {
obscureParam = true; obscureParam = true;
_passwordCommand = true;
} }
} }
return builder.toString(); return builder.toString();
@ -240,8 +245,8 @@ public class Script implements Callable<String> {
public String execute(OutputInterpreter interpreter) { public String execute(OutputInterpreter interpreter) {
String[] command = _command.toArray(new String[_command.size()]); String[] command = _command.toArray(new String[_command.size()]);
String commandLine = buildCommandLine(command); String commandLine = buildCommandLine(command);
if (_logger.isDebugEnabled() && !avoidLoggingCommand) { if (_logger.isDebugEnabled() ) {
_logger.debug(String.format("Executing command [%s].", commandLine.split(KeyStoreUtils.KS_FILENAME)[0])); _logger.debug(String.format("Executing command [%s].", commandLine));
} }
try { try {
@ -263,48 +268,62 @@ public class Script implements Callable<String> {
_thread = Thread.currentThread(); _thread = Thread.currentThread();
ScheduledFuture<String> future = null; ScheduledFuture<String> future = null;
if (_timeout > 0) { if (_timeout > 0) {
_logger.trace(String.format("Scheduling the execution of command [%s] with a timeout of [%s] milliseconds.", commandLine, _timeout)); _logger.trace(String.format(
"Scheduling the execution of command [%s] with a timeout of [%s] milliseconds.",
commandLine, _timeout));
future = s_executors.schedule(this, _timeout, TimeUnit.MILLISECONDS); future = s_executors.schedule(this, _timeout, TimeUnit.MILLISECONDS);
} }
long processPid = _process.pid(); long processPid = _process.pid();
Task task = null; Task task = null;
if (interpreter != null && interpreter.drain()) { if (interpreter != null && interpreter.drain()) {
_logger.trace(String.format("Executing interpreting task of process [%s] for command [%s].", processPid, commandLine)); _logger.trace(String.format("Executing interpreting task of process [%s] for command [%s].",
processPid, commandLine));
task = new Task(interpreter, ir); task = new Task(interpreter, ir);
s_executors.execute(task); s_executors.execute(task);
} }
while (true) { while (true) {
_logger.trace(String.format("Attempting process [%s] execution for command [%s] with timeout [%s].", processPid, commandLine, _timeout)); _logger.trace(String.format("Attempting process [%s] execution for command [%s] with timeout [%s].",
processPid, commandLine, _timeout));
try { try {
if (_process.waitFor(_timeout, TimeUnit.MILLISECONDS)) { if (_process.waitFor(_timeout, TimeUnit.MILLISECONDS)) {
_logger.trace(String.format("Process [%s] execution for command [%s] completed within timeout period [%s].", processPid, commandLine, _logger.trace(String.format(
"Process [%s] execution for command [%s] completed within timeout period [%s].",
processPid, commandLine,
_timeout)); _timeout));
if (_process.exitValue() == 0) { if (_process.exitValue() == 0) {
_logger.debug(String.format("Successfully executed process [%s] for command [%s].", processPid, commandLine)); _logger.debug(String.format("Successfully executed process [%s] for command [%s].",
processPid, commandLine));
if (interpreter != null) { if (interpreter != null) {
if (interpreter.drain()) { if (interpreter.drain()) {
_logger.trace(String.format("Returning task result of process [%s] for command [%s].", processPid, commandLine)); _logger.trace(
String.format("Returning task result of process [%s] for command [%s].",
processPid, commandLine));
return task.getResult(); return task.getResult();
} }
_logger.trace(String.format("Returning interpretation of process [%s] for command [%s].", processPid, commandLine)); _logger.trace(
String.format("Returning interpretation of process [%s] for command [%s].",
processPid, commandLine));
return interpreter.interpret(ir); return interpreter.interpret(ir);
} else { } else {
// null return exitValue apparently // null return exitValue apparently
_logger.trace(String.format("Process [%s] for command [%s] exited with value [%s].", processPid, commandLine, _logger.trace(String.format("Process [%s] for command [%s] exited with value [%s].",
processPid, commandLine,
_process.exitValue())); _process.exitValue()));
return String.valueOf(_process.exitValue()); return String.valueOf(_process.exitValue());
} }
} else { } else {
_logger.warn(String.format("Execution of process [%s] for command [%s] failed.", processPid, commandLine)); _logger.warn(String.format("Execution of process [%s] for command [%s] failed.",
processPid, commandLine));
break; break;
} }
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
if (!_isTimeOut) { if (!_isTimeOut) {
_logger.debug(String.format("Exception [%s] occurred; however, it was not a timeout. Therefore, proceeding with the execution of process [%s] for command " _logger.debug(String.format(
+ "[%s].", e.getMessage(), processPid, commandLine), e); "Exception [%s] occurred; however, it was not a timeout. Therefore, proceeding with the execution of process [%s] for command [%s].",
e.getMessage(), processPid, commandLine), e);
continue; continue;
} }
} finally { } finally {
@ -317,18 +336,17 @@ public class Script implements Callable<String> {
TimedOutLogger log = new TimedOutLogger(_process); TimedOutLogger log = new TimedOutLogger(_process);
Task timedoutTask = new Task(log, ir); Task timedoutTask = new Task(log, ir);
_logger.trace(String.format("Running timed out task of process [%s] for command [%s].", processPid, commandLine)); _logger.trace(String.format("Running timed out task of process [%s] for command [%s].", processPid,
commandLine));
timedoutTask.run(); timedoutTask.run();
if (!_passwordCommand) { _logger.warn(String.format("Process [%s] for command [%s] timed out. Output is [%s].", processPid,
_logger.warn(String.format("Process [%s] for command [%s] timed out. Output is [%s].", processPid, commandLine, timedoutTask.getResult())); commandLine, timedoutTask.getResult()));
} else {
_logger.warn(String.format("Process [%s] for command [%s] timed out.", processPid, commandLine));
}
return ERR_TIMEOUT; return ERR_TIMEOUT;
} }
_logger.debug(String.format("Exit value of process [%s] for command [%s] is [%s].", processPid, commandLine, _process.exitValue())); _logger.debug(String.format("Exit value of process [%s] for command [%s] is [%s].", processPid,
commandLine, _process.exitValue()));
BufferedReader reader = new BufferedReader(new InputStreamReader(_process.getInputStream()), 128); BufferedReader reader = new BufferedReader(new InputStreamReader(_process.getInputStream()), 128);
@ -339,19 +357,24 @@ public class Script implements Callable<String> {
error = String.valueOf(_process.exitValue()); error = String.valueOf(_process.exitValue());
} }
_logger.warn(String.format("Process [%s] for command [%s] encountered the error: [%s].", processPid, commandLine, error)); _logger.warn(String.format("Process [%s] for command [%s] encountered the error: [%s].", processPid,
commandLine, error));
return error; return error;
} catch (SecurityException ex) { } catch (SecurityException ex) {
_logger.warn(String.format("Exception [%s] occurred. This may be due to an attempt of executing command [%s] as non root.", ex.getMessage(), commandLine), _logger.warn(String.format(
"Exception [%s] occurred. This may be due to an attempt of executing command [%s] as non root.",
ex.getMessage(), commandLine),
ex); ex);
return stackTraceAsString(ex); return stackTraceAsString(ex);
} catch (Exception ex) { } catch (Exception ex) {
_logger.warn(String.format("Exception [%s] occurred when attempting to run command [%s].", ex.getMessage(), commandLine), ex); _logger.warn(String.format("Exception [%s] occurred when attempting to run command [%s].",
ex.getMessage(), commandLine), ex);
return stackTraceAsString(ex); return stackTraceAsString(ex);
} finally { } finally {
if (_process != null) { if (_process != null) {
_logger.trace(String.format("Destroying process [%s] for command [%s].", _process.pid(), commandLine)); _logger.trace(
String.format("Destroying process [%s] for command [%s].", _process.pid(), commandLine));
IOUtils.closeQuietly(_process.getErrorStream()); IOUtils.closeQuietly(_process.getErrorStream());
IOUtils.closeQuietly(_process.getOutputStream()); IOUtils.closeQuietly(_process.getOutputStream());
IOUtils.closeQuietly(_process.getInputStream()); IOUtils.closeQuietly(_process.getInputStream());
@ -362,9 +385,10 @@ public class Script implements Callable<String> {
public String executeIgnoreExitValue(OutputInterpreter interpreter, int exitValue) { public String executeIgnoreExitValue(OutputInterpreter interpreter, int exitValue) {
String[] command = _command.toArray(new String[_command.size()]); String[] command = _command.toArray(new String[_command.size()]);
String commandLine = buildCommandLine(command);
if (_logger.isDebugEnabled()) { if (_logger.isDebugEnabled()) {
_logger.debug(String.format("Executing: %s", buildCommandLine(command).split(KeyStoreUtils.KS_FILENAME)[0])); _logger.debug(String.format("Executing: %s", commandLine));
} }
try { try {
@ -375,7 +399,7 @@ public class Script implements Callable<String> {
_process = pb.start(); _process = pb.start();
if (_process == null) { if (_process == null) {
_logger.warn(String.format("Unable to execute: %s", buildCommandLine(command))); _logger.warn(String.format("Unable to execute: %s", commandLine));
return String.format("Unable to execute the command: %s", command[0]); return String.format("Unable to execute the command: %s", command[0]);
} }
@ -439,11 +463,8 @@ public class Script implements Callable<String> {
Task timedoutTask = new Task(log, ir); Task timedoutTask = new Task(log, ir);
timedoutTask.run(); timedoutTask.run();
if (!_passwordCommand) { _logger.warn(String.format("Timed out: %s. Output is: %s", commandLine,
_logger.warn(String.format("Timed out: %s. Output is: %s", buildCommandLine(command), timedoutTask.getResult())); timedoutTask.getResult()));
} else {
_logger.warn(String.format("Timed out: %s", buildCommandLine(command)));
}
return ERR_TIMEOUT; return ERR_TIMEOUT;
} }
@ -467,7 +488,7 @@ public class Script implements Callable<String> {
_logger.warn("Security Exception....not running as root?", ex); _logger.warn("Security Exception....not running as root?", ex);
return stackTraceAsString(ex); return stackTraceAsString(ex);
} catch (Exception ex) { } catch (Exception ex) {
_logger.warn(String.format("Exception: %s", buildCommandLine(command)), ex); _logger.warn(String.format("Exception: %s", commandLine), ex);
return stackTraceAsString(ex); return stackTraceAsString(ex);
} finally { } finally {
if (_process != null) { if (_process != null) {
@ -516,9 +537,9 @@ public class Script implements Callable<String> {
} catch (Exception ex) { } catch (Exception ex) {
result = stackTraceAsString(ex); result = stackTraceAsString(ex);
} finally { } finally {
done = true; done = true;
notifyAll(); notifyAll();
IOUtils.closeQuietly(reader); IOUtils.closeQuietly(reader);
} }
} }
} }

View File

@ -78,4 +78,34 @@ public class ScriptTest {
String result = Script.getExecutableAbsolutePath("ls"); String result = Script.getExecutableAbsolutePath("ls");
Assert.assertTrue(List.of("/usr/bin/ls", "/bin/ls").contains(result)); Assert.assertTrue(List.of("/usr/bin/ls", "/bin/ls").contains(result));
} }
@Test
public void testBuildCommandLineWithSensitiveData() {
Script script = new Script("test.sh");
script.add("normal-arg");
script.addSensitive("sensitive-arg");
String commandLine = script.toString();
Assert.assertEquals("test.sh normal-arg ****** ", commandLine);
}
@Test
public void testBuildCommandLineWithMultipleSensitiveData() {
Script script = new Script("test.sh");
script.add("normal-arg");
script.addSensitive("sensitive-arg1");
script.add("another-normal-arg");
script.addSensitive("sensitive-arg2");
String commandLine = script.toString();
Assert.assertEquals("test.sh normal-arg ****** another-normal-arg ****** ", commandLine);
}
@Test
public void testBuildCommandLineWithLegacyPasswordOption() {
Script script = new Script("test.sh");
script.add("-y");
script.add("legacy-password");
String commandLine = script.toString();
Assert.assertEquals("test.sh -y ****** ", commandLine);
}
} }