diff options
author | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2019-05-31 16:18:56 +0200 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2019-05-31 16:18:56 +0200 |
commit | 628432bf640ef1c20c39718dcbb508e4a56bf8b8 (patch) | |
tree | 48142da36f0a1e716424dcfe5aaa27c9c963df8a /security-tools/src/main | |
parent | b0c953ea8f0c6ad4e757797001de85639b3ccdda (diff) |
Add implementation of the 'vespa-security-env' tool
Diffstat (limited to 'security-tools/src/main')
4 files changed, 249 insertions, 1 deletions
diff --git a/security-tools/src/main/java/com/yahoo/vespa/security/tool/securityenv/CliOptions.java b/security-tools/src/main/java/com/yahoo/vespa/security/tool/securityenv/CliOptions.java new file mode 100644 index 00000000000..f3ec73236c4 --- /dev/null +++ b/security-tools/src/main/java/com/yahoo/vespa/security/tool/securityenv/CliOptions.java @@ -0,0 +1,69 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.security.tool.securityenv; + +import com.yahoo.security.tls.TransportSecurityUtils; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.Arrays; + +import static java.util.stream.Collectors.joining; + +/** + * Defines the program's command line parameters. + * + * @author bjorncs + */ +class CliOptions { + static final String SHELL_OPTION = "shell"; + static final String HELP_OPTION = "help"; + + private static final Options OPTIONS = new Options() + .addOption( + Option.builder("s") + .longOpt(SHELL_OPTION) + .hasArg(true) + .required(false) + .desc(String.format("Shell type. Shell type is auto-detected if option not present. Valid values: %s.", + Arrays.stream(UnixShell.values()) + .map(shell -> String.format("'%s'", shell.configName())) + .collect(joining(", ", "[", "]")))) + .build()) + .addOption(Option.builder("h") + .longOpt(HELP_OPTION) + .hasArg(false) + .required(false) + .desc("Show help") + .build()); + + static CommandLine parseCliArguments(String[] cliArgs) throws ParseException { + CommandLineParser parser = new DefaultParser(); + return parser.parse(OPTIONS, cliArgs); + } + + static void printHelp(PrintStream out) { + HelpFormatter formatter = new HelpFormatter(); + PrintWriter writer = new PrintWriter(out); + formatter.printHelp( + writer, + formatter.getWidth(), + "vespa-security-env <options>", + String.format("Generates shell commands that defines environments variables based on the content of %s.", + TransportSecurityUtils.CONFIG_FILE_ENVIRONMENT_VARIABLE), + OPTIONS, + formatter.getLeftPadding(), + formatter.getDescPadding(), + String.format("The output may include the following variables:\n%s\n", + Arrays.stream(OutputVariable.values()) + .map(variable -> String.format(" - '%s': %s", variable.variableName(), variable.description())) + .collect(joining("\n")))); + writer.flush(); + } +} diff --git a/security-tools/src/main/java/com/yahoo/vespa/security/tool/securityenv/Main.java b/security-tools/src/main/java/com/yahoo/vespa/security/tool/securityenv/Main.java index f57575b406a..74c08c2d602 100644 --- a/security-tools/src/main/java/com/yahoo/vespa/security/tool/securityenv/Main.java +++ b/security-tools/src/main/java/com/yahoo/vespa/security/tool/securityenv/Main.java @@ -1,11 +1,87 @@ // Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.security.tool.securityenv; +import com.yahoo.security.tls.MixedMode; +import com.yahoo.security.tls.TransportSecurityOptions; +import com.yahoo.security.tls.TransportSecurityUtils; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.ParseException; + +import java.io.PrintStream; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; + +import static com.yahoo.vespa.security.tool.securityenv.CliOptions.HELP_OPTION; +import static com.yahoo.vespa.security.tool.securityenv.CliOptions.SHELL_OPTION; + /** + * Implementation of the 'vespa-security-env' command line utility. + * * @author bjorncs */ public class Main { + + private final PrintStream stdOut; + private final PrintStream stdError; + + Main(PrintStream stdOut, PrintStream stdError) { + this.stdOut = stdOut; + this.stdError = stdError; + } + public static void main(String[] args) { - System.out.println("TODO implementation"); + Main program = new Main(System.out, System.err); + int statusCode = program.execute(args, System.getenv()); + System.exit(statusCode); + } + + int execute(String[] cliArgs, Map<String, String> envVars) { + boolean debugMode = envVars.containsKey("VESPA_DEBUG"); + try { + CommandLine arguments = CliOptions.parseCliArguments(cliArgs); + if (arguments.hasOption(HELP_OPTION)) { + CliOptions.printHelp(stdOut); + return 0; + } + UnixShell shell = arguments.hasOption(SHELL_OPTION) + ? UnixShell.fromConfigName(arguments.getOptionValue(SHELL_OPTION)) + : UnixShell.detect(envVars.get("SHELL")); + + Optional<TransportSecurityOptions> options = TransportSecurityUtils.getOptions(envVars); + if (options.isEmpty()) { + return 0; + } + Map<String, String> outputVariables = new TreeMap<>(); + options.get().getCaCertificatesFile() + .ifPresent(caCertFile -> addOutputVariable(outputVariables, OutputVariable.CA_CERTIFICATE, caCertFile.toString())); + MixedMode mixedMode = TransportSecurityUtils.getInsecureMixedMode(envVars); + if (mixedMode != MixedMode.PLAINTEXT_CLIENT_MIXED_SERVER) { + options.get().getCertificatesFile() + .ifPresent(certificateFile -> addOutputVariable(outputVariables, OutputVariable.CERTIFICATE, certificateFile.toString())); + options.get().getPrivateKeyFile() + .ifPresent(privateKeyFile -> addOutputVariable(outputVariables, OutputVariable.PRIVATE_KEY, privateKeyFile.toString())); + } + shell.writeOutputVariables(stdOut, outputVariables); + return 0; + } catch (ParseException e) { + return handleException("Failed to parse command line arguments: " + e.getMessage(), e, debugMode); + } catch (IllegalArgumentException e) { + return handleException("Invalid command line arguments: " + e.getMessage(), e, debugMode); + } catch (Exception e) { + return handleException("Failed to generate security environment variables: " + e.getMessage(), e, debugMode); + } + } + + private static void addOutputVariable(Map<String, String> outputVariables, OutputVariable variable, String value) { + outputVariables.put(variable.variableName(), value); + } + + private int handleException(String message, Exception exception, boolean debugMode) { + stdError.println(message); + if (debugMode) { + exception.printStackTrace(stdError); + } + return 1; } } diff --git a/security-tools/src/main/java/com/yahoo/vespa/security/tool/securityenv/OutputVariable.java b/security-tools/src/main/java/com/yahoo/vespa/security/tool/securityenv/OutputVariable.java new file mode 100644 index 00000000000..9cd4cc1fc67 --- /dev/null +++ b/security-tools/src/main/java/com/yahoo/vespa/security/tool/securityenv/OutputVariable.java @@ -0,0 +1,29 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.security.tool.securityenv; + +/** + * Define the possible environment variables that the program may output. + * + * @author bjorncs + */ +enum OutputVariable { + CA_CERTIFICATE("VESPA_TLS_CA_CERT", "Path to CA certificates file"), + CERTIFICATE("VESPA_TLS_CERT", "Path to certificate file"), + PRIVATE_KEY("VESPA_TLS_PRIVATE_KEY", "Path to private key file"); + + private final String variableName; + private final String description; + + OutputVariable(String variableName, String description) { + this.variableName = variableName; + this.description = description; + } + + String variableName() { + return variableName; + } + + String description() { + return description; + } +} diff --git a/security-tools/src/main/java/com/yahoo/vespa/security/tool/securityenv/UnixShell.java b/security-tools/src/main/java/com/yahoo/vespa/security/tool/securityenv/UnixShell.java new file mode 100644 index 00000000000..1b4a9696c69 --- /dev/null +++ b/security-tools/src/main/java/com/yahoo/vespa/security/tool/securityenv/UnixShell.java @@ -0,0 +1,74 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.security.tool.securityenv; + +import java.io.PrintStream; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * Definition of some unix shell variants and how to export environments variable for those supported. + * The output format is inspired by ssh-agent's output. + * + * @author bjorncs + */ +enum UnixShell { + BOURNE("bourne", List.of("bash", "sh")) { + @Override + void writeOutputVariables(PrintStream out, Map<String, String> outputVariables) { + outputVariables.forEach((name, value) -> { + out.print(name); + out.print('='); + out.print(value); // note: value is assumed to need no escaping + out.print("; export "); + out.print(name); + out.println(';'); + }); + } + }, + CSHELL("cshell", List.of("csh", "fish")) { + @Override + void writeOutputVariables(PrintStream out, Map<String, String> outputVariables) { + outputVariables.forEach((name, value) -> { + out.print("setenv "); + out.print(name); + out.print(' '); + out.print(value); // note: value is assumed to need no escaping + out.println(';'); + }); + } + }; + + private static final UnixShell DEFAULT = BOURNE; + + private final String configName; + private final List<String> knownShellBinaries; + + UnixShell(String configName, List<String> knownShellBinaries) { + this.configName = configName; + this.knownShellBinaries = knownShellBinaries; + } + + abstract void writeOutputVariables(PrintStream out, Map<String, String> outputVariables); + + String configName() { + return configName; + } + + static UnixShell fromConfigName(String configName) { + return Arrays.stream(values()) + .filter(shell -> shell.configName.equals(configName)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("Unknown shell: " + configName)); + } + + static UnixShell detect(String shellEnvVariable) { + if (shellEnvVariable == null || shellEnvVariable.isEmpty()) return DEFAULT; + int lastSlash = shellEnvVariable.lastIndexOf('/'); + String shellName = lastSlash != -1 ? shellEnvVariable.substring(lastSlash + 1) : shellEnvVariable; + return Arrays.stream(values()) + .filter(shell -> shell.knownShellBinaries.contains(shellName)) + .findAny() + .orElse(DEFAULT); + } +} |