Resolve paths from the current working directory instead of the config directory (elastic/x-pack-elasticsearch#637)
This commit changes the resolution of the output and input files so that relative paths will be resolved from the current working directory instead of the x-pack config directory. relates elastic/x-pack-elasticsearch#621 Original commit: elastic/x-pack-elasticsearch@bbfd83c2d5
This commit is contained in:
parent
253340a597
commit
666e87c29b
|
@ -19,6 +19,8 @@ import org.elasticsearch.cli.EnvironmentAwareCommand;
|
||||||
import org.elasticsearch.cli.Terminal;
|
import org.elasticsearch.cli.Terminal;
|
||||||
import org.elasticsearch.common.ParseField;
|
import org.elasticsearch.common.ParseField;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
|
import org.elasticsearch.common.SuppressForbidden;
|
||||||
|
import org.elasticsearch.common.io.PathUtils;
|
||||||
import org.elasticsearch.common.network.InetAddresses;
|
import org.elasticsearch.common.network.InetAddresses;
|
||||||
import org.elasticsearch.common.util.set.Sets;
|
import org.elasticsearch.common.util.set.Sets;
|
||||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||||
|
@ -27,7 +29,6 @@ import org.elasticsearch.common.xcontent.ObjectParser;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.xpack.XPackPlugin;
|
|
||||||
|
|
||||||
import javax.security.auth.x500.X500Principal;
|
import javax.security.auth.x500.X500Principal;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -138,11 +139,11 @@ public class CertificateTool extends EnvironmentAwareCommand {
|
||||||
protected void execute(Terminal terminal, OptionSet options, Environment env) throws Exception {
|
protected void execute(Terminal terminal, OptionSet options, Environment env) throws Exception {
|
||||||
final boolean csrOnly = options.has(csrSpec);
|
final boolean csrOnly = options.has(csrSpec);
|
||||||
printIntro(terminal, csrOnly);
|
printIntro(terminal, csrOnly);
|
||||||
final Path outputFile = getOutputFile(terminal, outputPathSpec.value(options), env, csrOnly ? DEFAULT_CSR_FILE : DEFAULT_CERT_FILE);
|
final Path outputFile = getOutputFile(terminal, outputPathSpec.value(options), csrOnly ? DEFAULT_CSR_FILE : DEFAULT_CERT_FILE);
|
||||||
final String inputFile = inputFileSpec.value(options);
|
final String inputFile = inputFileSpec.value(options);
|
||||||
final int keysize = options.has(keysizeSpec) ? keysizeSpec.value(options) : DEFAULT_KEY_SIZE;
|
final int keysize = options.has(keysizeSpec) ? keysizeSpec.value(options) : DEFAULT_KEY_SIZE;
|
||||||
if (csrOnly) {
|
if (csrOnly) {
|
||||||
Collection<CertificateInformation> certificateInformations = getCertificateInformationList(terminal, inputFile, env);
|
Collection<CertificateInformation> certificateInformations = getCertificateInformationList(terminal, inputFile);
|
||||||
generateAndWriteCsrs(outputFile, certificateInformations, keysize);
|
generateAndWriteCsrs(outputFile, certificateInformations, keysize);
|
||||||
} else {
|
} else {
|
||||||
final String dn = options.has(caDnSpec) ? caDnSpec.value(options) : AUTO_GEN_CA_DN;
|
final String dn = options.has(caDnSpec) ? caDnSpec.value(options) : AUTO_GEN_CA_DN;
|
||||||
|
@ -151,7 +152,7 @@ public class CertificateTool extends EnvironmentAwareCommand {
|
||||||
final int days = options.hasArgument(daysSpec) ? daysSpec.value(options) : DEFAULT_DAYS;
|
final int days = options.hasArgument(daysSpec) ? daysSpec.value(options) : DEFAULT_DAYS;
|
||||||
CAInfo caInfo = getCAInfo(terminal, dn, caCertPathSpec.value(options), caKeyPathSpec.value(options), keyPass, prompt, env,
|
CAInfo caInfo = getCAInfo(terminal, dn, caCertPathSpec.value(options), caKeyPathSpec.value(options), keyPass, prompt, env,
|
||||||
keysize, days);
|
keysize, days);
|
||||||
Collection<CertificateInformation> certificateInformations = getCertificateInformationList(terminal, inputFile, env);
|
Collection<CertificateInformation> certificateInformations = getCertificateInformationList(terminal, inputFile);
|
||||||
generateAndWriteSignedCertificates(outputFile, certificateInformations, caInfo, keysize, days);
|
generateAndWriteSignedCertificates(outputFile, certificateInformations, caInfo, keysize, days);
|
||||||
}
|
}
|
||||||
printConclusion(terminal, csrOnly, outputFile);
|
printConclusion(terminal, csrOnly, outputFile);
|
||||||
|
@ -171,35 +172,38 @@ public class CertificateTool extends EnvironmentAwareCommand {
|
||||||
*
|
*
|
||||||
* @param terminal terminal to communicate with a user
|
* @param terminal terminal to communicate with a user
|
||||||
* @param outputPath user specified output file, may be {@code null}
|
* @param outputPath user specified output file, may be {@code null}
|
||||||
* @param env the environment for this tool to resolve files with
|
|
||||||
* @return a {@link Path} to the output file
|
* @return a {@link Path} to the output file
|
||||||
*/
|
*/
|
||||||
static Path getOutputFile(Terminal terminal, String outputPath, Environment env, String defaultFilename) throws IOException {
|
static Path getOutputFile(Terminal terminal, String outputPath, String defaultFilename) throws IOException {
|
||||||
Path file;
|
Path file;
|
||||||
if (outputPath != null) {
|
if (outputPath != null) {
|
||||||
file = XPackPlugin.resolveConfigFile(env, Strings.cleanPath(outputPath));
|
file = resolvePath(outputPath);
|
||||||
} else {
|
} else {
|
||||||
file = XPackPlugin.resolveConfigFile(env, defaultFilename);
|
file = resolvePath(defaultFilename);
|
||||||
String input = terminal.readText("Please enter the desired output file [" + file + "]: ");
|
String input = terminal.readText("Please enter the desired output file [" + file + "]: ");
|
||||||
if (input.isEmpty() == false) {
|
if (input.isEmpty() == false) {
|
||||||
file = XPackPlugin.resolveConfigFile(env, Strings.cleanPath(input));
|
file = resolvePath(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressForbidden(reason = "resolve paths against CWD for a CLI tool")
|
||||||
|
private static Path resolvePath(String pathStr) {
|
||||||
|
return PathUtils.get(Strings.cleanPath(pathStr)).toAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method handles the collection of information about each instance that is necessary to generate a certificate. The user may
|
* This method handles the collection of information about each instance that is necessary to generate a certificate. The user may
|
||||||
* be prompted or the information can be gathered from a file
|
* be prompted or the information can be gathered from a file
|
||||||
* @param terminal the terminal to use for user interaction
|
* @param terminal the terminal to use for user interaction
|
||||||
* @param inputFile an optional file that will be used to load the instance information
|
* @param inputFile an optional file that will be used to load the instance information
|
||||||
* @param env the environment for this tool to resolve files with
|
|
||||||
* @return a {@link Collection} of {@link CertificateInformation} that represents each instance
|
* @return a {@link Collection} of {@link CertificateInformation} that represents each instance
|
||||||
*/
|
*/
|
||||||
static Collection<CertificateInformation> getCertificateInformationList(Terminal terminal, String inputFile, Environment env)
|
static Collection<CertificateInformation> getCertificateInformationList(Terminal terminal, String inputFile)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
if (inputFile != null) {
|
if (inputFile != null) {
|
||||||
return parseFile(XPackPlugin.resolveConfigFile(env, inputFile));
|
return parseFile(resolvePath(inputFile));
|
||||||
}
|
}
|
||||||
Map<String, CertificateInformation> map = new HashMap<>();
|
Map<String, CertificateInformation> map = new HashMap<>();
|
||||||
boolean done = false;
|
boolean done = false;
|
||||||
|
@ -307,13 +311,14 @@ public class CertificateTool extends EnvironmentAwareCommand {
|
||||||
Environment env, int keysize, int days) throws Exception {
|
Environment env, int keysize, int days) throws Exception {
|
||||||
if (caCertPath != null) {
|
if (caCertPath != null) {
|
||||||
assert caKeyPath != null;
|
assert caKeyPath != null;
|
||||||
Certificate[] certificates = CertUtils.readCertificates(Collections.singletonList(caCertPath), env);
|
final String resolvedCaCertPath = resolvePath(caCertPath).toString();
|
||||||
|
Certificate[] certificates = CertUtils.readCertificates(Collections.singletonList(resolvedCaCertPath), env);
|
||||||
if (certificates.length != 1) {
|
if (certificates.length != 1) {
|
||||||
throw new IllegalArgumentException("expected a single certificate in file [" + caCertPath + "] but found [" +
|
throw new IllegalArgumentException("expected a single certificate in file [" + caCertPath + "] but found [" +
|
||||||
certificates.length + "]");
|
certificates.length + "]");
|
||||||
}
|
}
|
||||||
Certificate caCert = certificates[0];
|
Certificate caCert = certificates[0];
|
||||||
PrivateKey privateKey = readPrivateKey(caKeyPath, keyPass, terminal, env, prompt);
|
PrivateKey privateKey = readPrivateKey(caKeyPath, keyPass, terminal, prompt);
|
||||||
return new CAInfo((X509Certificate) caCert, privateKey);
|
return new CAInfo((X509Certificate) caCert, privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,14 +509,13 @@ public class CertificateTool extends EnvironmentAwareCommand {
|
||||||
* @param path the path to the private key
|
* @param path the path to the private key
|
||||||
* @param password the password provided by the user or {@code null}
|
* @param password the password provided by the user or {@code null}
|
||||||
* @param terminal the terminal to use for user interaction
|
* @param terminal the terminal to use for user interaction
|
||||||
* @param env the environment to resolve files from
|
|
||||||
* @param prompt whether to prompt the user or not
|
* @param prompt whether to prompt the user or not
|
||||||
* @return the {@link PrivateKey} that was read from the file
|
* @return the {@link PrivateKey} that was read from the file
|
||||||
*/
|
*/
|
||||||
private static PrivateKey readPrivateKey(String path, char[] password, Terminal terminal, Environment env, boolean prompt)
|
private static PrivateKey readPrivateKey(String path, char[] password, Terminal terminal, boolean prompt)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
AtomicReference<char[]> passwordReference = new AtomicReference<>(password);
|
AtomicReference<char[]> passwordReference = new AtomicReference<>(password);
|
||||||
try (Reader reader = Files.newBufferedReader(XPackPlugin.resolveConfigFile(env, path), StandardCharsets.UTF_8)) {
|
try (Reader reader = Files.newBufferedReader(resolvePath(path), StandardCharsets.UTF_8)) {
|
||||||
return CertUtils.readPrivateKey(reader, () -> {
|
return CertUtils.readPrivateKey(reader, () -> {
|
||||||
if (password != null || prompt == false) {
|
if (password != null || prompt == false) {
|
||||||
return password;
|
return password;
|
||||||
|
|
|
@ -22,11 +22,12 @@ import org.bouncycastle.openssl.PEMParser;
|
||||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
|
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
|
||||||
import org.elasticsearch.cli.MockTerminal;
|
import org.elasticsearch.cli.MockTerminal;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
|
import org.elasticsearch.common.SuppressForbidden;
|
||||||
|
import org.elasticsearch.common.io.PathUtils;
|
||||||
import org.elasticsearch.common.network.NetworkAddress;
|
import org.elasticsearch.common.network.NetworkAddress;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.xpack.XPackPlugin;
|
|
||||||
import org.elasticsearch.xpack.ssl.CertificateTool.CAInfo;
|
import org.elasticsearch.xpack.ssl.CertificateTool.CAInfo;
|
||||||
import org.elasticsearch.xpack.ssl.CertificateTool.CertificateInformation;
|
import org.elasticsearch.xpack.ssl.CertificateTool.CertificateInformation;
|
||||||
import org.elasticsearch.xpack.ssl.CertificateTool.Name;
|
import org.elasticsearch.xpack.ssl.CertificateTool.Name;
|
||||||
|
@ -63,7 +64,6 @@ import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,13 +89,12 @@ public class CertificateToolTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testOutputDirectory() throws Exception {
|
public void testOutputDirectory() throws Exception {
|
||||||
Environment env = new Environment(Settings.builder().put("path.home", createTempDir()).build());
|
|
||||||
Path outputDir = createTempDir();
|
Path outputDir = createTempDir();
|
||||||
Path outputFile = outputDir.resolve("certs.zip");
|
Path outputFile = outputDir.resolve("certs.zip");
|
||||||
MockTerminal terminal = new MockTerminal();
|
MockTerminal terminal = new MockTerminal();
|
||||||
|
|
||||||
// test with a user provided dir
|
// test with a user provided dir
|
||||||
Path resolvedOutputFile = CertificateTool.getOutputFile(terminal, outputFile.toString(), env, null);
|
Path resolvedOutputFile = CertificateTool.getOutputFile(terminal, outputFile.toString(), null);
|
||||||
assertEquals(outputFile, resolvedOutputFile);
|
assertEquals(outputFile, resolvedOutputFile);
|
||||||
assertTrue(terminal.getOutput().isEmpty());
|
assertTrue(terminal.getOutput().isEmpty());
|
||||||
|
|
||||||
|
@ -103,15 +102,15 @@ public class CertificateToolTests extends ESTestCase {
|
||||||
Path userPromptedOutputFile = outputDir.resolve("csr");
|
Path userPromptedOutputFile = outputDir.resolve("csr");
|
||||||
assertFalse(Files.exists(userPromptedOutputFile));
|
assertFalse(Files.exists(userPromptedOutputFile));
|
||||||
terminal.addTextInput(userPromptedOutputFile.toString());
|
terminal.addTextInput(userPromptedOutputFile.toString());
|
||||||
resolvedOutputFile = CertificateTool.getOutputFile(terminal, null, env, "out.zip");
|
resolvedOutputFile = CertificateTool.getOutputFile(terminal, null, "out.zip");
|
||||||
assertEquals(userPromptedOutputFile, resolvedOutputFile);
|
assertEquals(userPromptedOutputFile, resolvedOutputFile);
|
||||||
assertTrue(terminal.getOutput().isEmpty());
|
assertTrue(terminal.getOutput().isEmpty());
|
||||||
|
|
||||||
// test with empty user input
|
// test with empty user input
|
||||||
String defaultFilename = randomAlphaOfLengthBetween(1, 10);
|
String defaultFilename = randomAlphaOfLengthBetween(1, 10);
|
||||||
Path expectedDefaultPath = XPackPlugin.resolveConfigFile(env, defaultFilename);
|
Path expectedDefaultPath = resolvePath(defaultFilename);
|
||||||
terminal.addTextInput("");
|
terminal.addTextInput("");
|
||||||
resolvedOutputFile = CertificateTool.getOutputFile(terminal, null, env, defaultFilename);
|
resolvedOutputFile = CertificateTool.getOutputFile(terminal, null, defaultFilename);
|
||||||
assertEquals(expectedDefaultPath, resolvedOutputFile);
|
assertEquals(expectedDefaultPath, resolvedOutputFile);
|
||||||
assertTrue(terminal.getOutput().isEmpty());
|
assertTrue(terminal.getOutput().isEmpty());
|
||||||
}
|
}
|
||||||
|
@ -150,8 +149,7 @@ public class CertificateToolTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Collection<CertificateInformation> certInfos = CertificateTool.getCertificateInformationList(terminal, null,
|
Collection<CertificateInformation> certInfos = CertificateTool.getCertificateInformationList(terminal, null);
|
||||||
new Environment(Settings.builder().put("path.home", createTempDir()).build()));
|
|
||||||
logger.info("certificate tool output:\n{}", terminal.getOutput());
|
logger.info("certificate tool output:\n{}", terminal.getOutput());
|
||||||
assertEquals(numberOfInstances, certInfos.size());
|
assertEquals(numberOfInstances, certInfos.size());
|
||||||
for (CertificateInformation certInfo : certInfos) {
|
for (CertificateInformation certInfo : certInfos) {
|
||||||
|
@ -487,4 +485,9 @@ public class CertificateToolTests extends ESTestCase {
|
||||||
|
|
||||||
return Files.write(path, instances, StandardCharsets.UTF_8);
|
return Files.write(path, instances, StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressForbidden(reason = "resolve paths against CWD for a CLI tool")
|
||||||
|
private static Path resolvePath(String path) {
|
||||||
|
return PathUtils.get(path).toAbsolutePath();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue