Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read docker credentials stdout and stderr independently #8007

Merged
merged 9 commits into from
Oct 16, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.testcontainers.DockerClientFactory;
import org.zeroturnaround.exec.InvalidResultException;
import org.zeroturnaround.exec.ProcessExecutor;
import org.zeroturnaround.exec.stream.LogOutputStream;

import java.io.ByteArrayInputStream;
import java.io.File;
Expand Down Expand Up @@ -273,7 +274,7 @@ private AuthConfig runCredentialProvider(String hostName, String helperOrStoreNa
}

final String credentialProgramName = getCredentialProgramName(helperOrStoreName);
final String data;
final CredentialOutput data;

log.debug(
"Executing docker credential provider: {} to locate auth config for: {}",
Expand All @@ -283,37 +284,38 @@ private AuthConfig runCredentialProvider(String hostName, String helperOrStoreNa

try {
data = runCredentialProgram(hostName, credentialProgramName);
} catch (InvalidResultException e) {
final String responseErrorMsg = extractCredentialProviderErrorMessage(e);

if (!StringUtils.isBlank(responseErrorMsg)) {
String credentialsNotFoundMsg = getGenericCredentialsNotFoundMsg(credentialProgramName);
if (credentialsNotFoundMsg != null && credentialsNotFoundMsg.equals(responseErrorMsg)) {
log.info(
"Credential helper/store ({}) does not have credentials for {}",
if (data.getStderr() != null && !data.getStderr().isEmpty()) {
final String responseErrorMsg = data.getStderr();

if (!StringUtils.isBlank(responseErrorMsg)) {
String credentialsNotFoundMsg = getGenericCredentialsNotFoundMsg(credentialProgramName);
if (credentialsNotFoundMsg != null && credentialsNotFoundMsg.equals(responseErrorMsg)) {
log.info(
"Credential helper/store ({}) does not have credentials for {}",
credentialProgramName,
hostName
);

return null;
}

log.debug(
"Failure running docker credential helper/store ({}) with output '{}'",
credentialProgramName,
hostName
responseErrorMsg
);

return null;
} else {
log.debug("Failure running docker credential helper/store ({})", credentialProgramName);
}

log.debug(
"Failure running docker credential helper/store ({}) with output '{}'",
credentialProgramName,
responseErrorMsg
);
} else {
log.debug("Failure running docker credential helper/store ({})", credentialProgramName);
throw new InvalidResultException(data.getStderr(), null);
}

throw e;
} catch (Exception e) {
log.debug("Failure running docker credential helper/store ({})", credentialProgramName);
throw e;
}

final JsonNode helperResponse = OBJECT_MAPPER.readTree(data);
final JsonNode helperResponse = OBJECT_MAPPER.readTree(data.getStdout());
log.debug("Credential helper/store provided auth config for: {}", hostName);

final String username = helperResponse.at("/Username").asText();
Expand Down Expand Up @@ -363,55 +365,81 @@ private String discoverCredentialsHelperNotFoundMessage(String credentialHelperN

String credentialsNotFoundMsg = null;
try {
runCredentialProgram(notExistentFakeHostName, credentialHelperName);
CredentialOutput data = runCredentialProgram(notExistentFakeHostName, credentialHelperName);

// should not reach here
log.warn(
"Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response",
credentialHelperName
);
} catch (Exception e) {
if (e instanceof InvalidResultException) {
credentialsNotFoundMsg = extractCredentialProviderErrorMessage((InvalidResultException) e);
}
if (data.getStderr() != null && !data.getStderr().isEmpty()) {
credentialsNotFoundMsg = data.getStderr();

if (StringUtils.isBlank(credentialsNotFoundMsg)) {
log.warn(
"Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response. Exception message: {}",
credentialHelperName,
e.getMessage()
);
} else {
log.debug(
"Got credentials not found error message from docker credential helper - {}",
credentialsNotFoundMsg
);
}
} catch (Exception e) {
log.warn(
"Failure running docker credential helper ({}) with fake call, expected 'credentials not found' response. Exception message: {}",
credentialHelperName,
e.getMessage()
);
}

return credentialsNotFoundMsg;
}

private String extractCredentialProviderErrorMessage(InvalidResultException invalidResultEx) {
if (invalidResultEx.getResult() != null && invalidResultEx.getResult().hasOutput()) {
return invalidResultEx.getResult().outputString().trim();
}
return null;
}

private String runCredentialProgram(String hostName, String credentialHelperName)
throws InvalidResultException, InterruptedException, TimeoutException, IOException {
private CredentialOutput runCredentialProgram(String hostName, String credentialHelperName)
throws InterruptedException, TimeoutException, IOException {
String[] command = SystemUtils.IS_OS_WINDOWS
? new String[] { "cmd", "/c", credentialHelperName, "get" }
: new String[] { credentialHelperName, "get" };
return new ProcessExecutor()
.command(command)
.redirectInput(new ByteArrayInputStream(hostName.getBytes()))
.readOutput(true)
.exitValueNormal()
.timeout(30, TimeUnit.SECONDS)
.execute()
.outputUTF8()
.trim();

StringBuffer stdout = new StringBuffer();
StringBuffer stderr = new StringBuffer();

try {
new ProcessExecutor()
.command(command)
.redirectInput(new ByteArrayInputStream(hostName.getBytes()))
.redirectOutput(
new LogOutputStream() {
@Override
protected void processLine(String line) {
stdout.append(line).append(System.lineSeparator());
}
}
)
.redirectError(
new LogOutputStream() {
@Override
protected void processLine(String line) {
stderr.append(line).append(System.lineSeparator());
}
}
)
.exitValueNormal()
.timeout(30, TimeUnit.SECONDS)
.execute();
} catch (InvalidResultException e) {}

return new CredentialOutput(stdout.toString(), stderr.toString());
}

static class CredentialOutput {

private final String stdout;

private final String stderr;

public CredentialOutput(String stdout, String stderr) {
this.stdout = stdout.trim();
this.stderr = stderr.trim();
}

public String getStdout() {
return this.stdout;
}

public String getStderr() {
return this.stderr;
}
}
}
Loading