IGNITE-10257 control.sh utility should request SSL keystore and truststore passwords if necessary - Fixes #5647.
Signed-off-by: Alexey Goncharuk <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/ece5869b Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/ece5869b Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/ece5869b Branch: refs/heads/ignite-10639 Commit: ece5869bb9e7ff638352f07b63192ec2867afb8a Parents: afe23b6 Author: Sergey Antonov <[email protected]> Authored: Tue Dec 18 17:48:48 2018 +0300 Committer: Alexey Goncharuk <[email protected]> Committed: Tue Dec 18 17:48:48 2018 +0300 ---------------------------------------------------------------------- .../internal/commandline/CommandHandler.java | 208 ++++++++++++------- 1 file changed, 129 insertions(+), 79 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/ece5869b/modules/core/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java index 44f652c..9923fb4 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/commandline/CommandHandler.java @@ -155,6 +155,7 @@ import static org.apache.ignite.internal.visor.baseline.VisorBaselineOperation.V import static org.apache.ignite.internal.visor.verify.VisorViewCacheCmd.CACHES; import static org.apache.ignite.internal.visor.verify.VisorViewCacheCmd.GROUPS; import static org.apache.ignite.internal.visor.verify.VisorViewCacheCmd.SEQ; +import static org.apache.ignite.ssl.SslContextFactory.DFLT_SSL_PROTOCOL; /** * Class that execute several commands passed via command line. @@ -367,14 +368,7 @@ public class CommandHandler { private static final String UTILITY_NAME = "control.sh"; /** Common options. */ - private static final String COMMON_OPTIONS = String.join(" ", op(CMD_HOST, "HOST_OR_IP"), op(CMD_PORT, "PORT"), - op(CMD_USER, "USER"), op(CMD_PASSWORD, "PASSWORD"), - op(CMD_PING_INTERVAL, "PING_INTERVAL"), op(CMD_PING_TIMEOUT, "PING_TIMEOUT"), - op(CMD_SSL_PROTOCOL, "SSL_PROTOCOL[, SSL_PROTOCOL_2, ...]"), - op(CMD_SSL_CIPHER_SUITES, "SSL_CIPHER_1[, SSL_CIPHER_2, ...]"), - op(CMD_SSL_KEY_ALGORITHM, "SSL_KEY_ALGORITHM"), - op(CMD_KEYSTORE_TYPE, "KEYSTORE_TYPE"), op(CMD_KEYSTORE, "KEYSTORE"), op(CMD_KEYSTORE_PASSWORD, "KEYSTORE_PASSWORD"), - op(CMD_TRUSTSTORE_TYPE, "TRUSTSTORE_TYPE"), op(CMD_TRUSTSTORE, "TRUSTSTORE"), op(CMD_TRUSTSTORE_PASSWORD, "TRUSTSTORE_PASSWORD")); + private static final String COMMON_OPTIONS = String.join(" ", getCommonOptions()); /** Utility name with common options. */ private static final String UTILITY_NAME_WITH_COMMON_OPTIONS = String.join(" ", UTILITY_NAME, COMMON_OPTIONS); @@ -401,6 +395,34 @@ public class CommandHandler { private final boolean enableExperimental = IgniteSystemProperties.getBoolean(IGNITE_ENABLE_EXPERIMENTAL_COMMAND, false); /** + * Creates list of common utility options. + * + * @return List of common utility options. + */ + private static List<String> getCommonOptions() { + List<String> list = new ArrayList<>(32); + + list.add(op(CMD_HOST, "HOST_OR_IP")); + list.add(op(CMD_PORT, "PORT")); + list.add(op(CMD_USER, "USER")); + list.add(op(CMD_PASSWORD, "PASSWORD")); + list.add(op(CMD_PING_INTERVAL, "PING_INTERVAL")); + list.add(op(CMD_PING_TIMEOUT, "PING_TIMEOUT")); + + list.add(op(CMD_SSL_PROTOCOL, "SSL_PROTOCOL[, SSL_PROTOCOL_2, ...]")); + list.add(op(CMD_SSL_CIPHER_SUITES, "SSL_CIPHER_1[, SSL_CIPHER_2, ...]")); + list.add(op(CMD_SSL_KEY_ALGORITHM, "SSL_KEY_ALGORITHM")); + list.add(op(CMD_KEYSTORE_TYPE, "KEYSTORE_TYPE")); + list.add(op(CMD_KEYSTORE, "KEYSTORE")); + list.add(op(CMD_KEYSTORE_PASSWORD, "KEYSTORE_PASSWORD")); + list.add(op(CMD_TRUSTSTORE_TYPE, "TRUSTSTORE_TYPE")); + list.add(op(CMD_TRUSTSTORE, "TRUSTSTORE")); + list.add(op(CMD_TRUSTSTORE_PASSWORD, "TRUSTSTORE_PASSWORD")); + + return list; + } + + /** * Output specified string to console. * * @param s String to output. @@ -649,7 +671,9 @@ public class CommandHandler { * @return List of hosts. */ private Stream<IgniteBiTuple<GridClientNode, String>> listHosts(GridClient client) throws GridClientException { - return client.compute().nodes(GridClientNode::connectable).stream() + return client.compute() + .nodes(GridClientNode::connectable) + .stream() .flatMap(node -> Stream.concat( node.tcpAddresses() == null ? Stream.empty() : node.tcpAddresses().stream(), node.tcpHostNames() == null ? Stream.empty() : node.tcpHostNames().stream() @@ -661,14 +685,17 @@ public class CommandHandler { * @param client Client. * @return List of hosts. */ - private Stream<IgniteBiTuple<GridClientNode, List<String>>> listHostsByClientNode(GridClient client) throws GridClientException { + private Stream<IgniteBiTuple<GridClientNode, List<String>>> listHostsByClientNode( + GridClient client + ) throws GridClientException { return client.compute().nodes(GridClientNode::connectable).stream() .map(node -> new IgniteBiTuple<>(node, Stream.concat( node.tcpAddresses() == null ? Stream.empty() : node.tcpAddresses().stream(), node.tcpHostNames() == null ? Stream.empty() : node.tcpHostNames().stream() ) - .map(addr -> addr + ":" + node.tcpPort()).collect(Collectors.toList()))); + .map(addr -> addr + ":" + node.tcpPort()).collect(Collectors.toList())) + ); } /** @@ -1835,7 +1862,7 @@ public class CommandHandler { VisorTxTaskArg txArgs = null; - String sslProtocol = SslContextFactory.DFLT_SSL_PROTOCOL; + String sslProtocol = DFLT_SSL_PROTOCOL; String sslCipherSuites = ""; @@ -2416,6 +2443,41 @@ public class CommandHandler { } /** + * Requests password from console with message. + * + * @param msg Message. + * @return Password. + */ + private char[] requestPasswordFromConsole(String msg) { + Console console = System.console(); + + if (console == null) + throw new UnsupportedOperationException("Failed to securely read password (console is unavailable): " + msg); + else + return console.readPassword(msg); + } + + /** + * Requests user data from console with message. + * + * @param msg Message. + * @return Input user data. + */ + private String requestDataFromConsole(String msg) { + Console console = System.console(); + + if (console != null) + return console.readLine(msg); + else { + Scanner scanner = new Scanner(System.in); + + log(msg); + + return scanner.nextLine(); + } + } + + /** * Check if raw arg is command or option. * * @return {@code true} If raw arg is command, overwise {@code false}. @@ -2542,6 +2604,52 @@ public class CommandHandler { .collect(Collectors.toList()); } + /** */ + private void printHelp() { + log("This utility can do the following commands:"); + + usage(i("Activate cluster:"), ACTIVATE); + usage(i("Deactivate cluster:"), DEACTIVATE, op(CMD_AUTO_CONFIRMATION)); + usage(i("Print current cluster state:"), STATE); + usage(i("Print cluster baseline topology:"), BASELINE); + usage(i("Add nodes into baseline topology:"), BASELINE, BASELINE_ADD, "consistentId1[,consistentId2,....,consistentIdN]", op(CMD_AUTO_CONFIRMATION)); + usage(i("Remove nodes from baseline topology:"), BASELINE, BASELINE_REMOVE, "consistentId1[,consistentId2,....,consistentIdN]", op(CMD_AUTO_CONFIRMATION)); + usage(i("Set baseline topology:"), BASELINE, BASELINE_SET, "consistentId1[,consistentId2,....,consistentIdN]", op(CMD_AUTO_CONFIRMATION)); + usage(i("Set baseline topology based on version:"), BASELINE, BASELINE_SET_VERSION + " topologyVersion", op(CMD_AUTO_CONFIRMATION)); + usage(i("List or kill transactions:"), TX, op(TX_XID, "XID"), op(TX_DURATION, "SECONDS"), op(TX_SIZE, "SIZE"), op(TX_LABEL, "PATTERN_REGEX"), op(or(TX_SERVERS, TX_CLIENTS)), op(TX_NODES, "consistentId1[,consistentId2,....,consistentIdN]"), op(TX_LIMIT, "NUMBER"), op(TX_ORDER, or("DURATION", "SIZE", CMD_TX_ORDER_START_TIME)), op(TX_KILL), op(CMD_AUTO_CONFIRMATION)); + + if (enableExperimental) { + usage(i("Print absolute paths of unused archived wal segments on each node:"), WAL, WAL_PRINT, "[consistentId1,consistentId2,....,consistentIdN]"); + usage(i("Delete unused archived wal segments on each node:"), WAL, WAL_DELETE, "[consistentId1,consistentId2,....,consistentIdN] ", op(CMD_AUTO_CONFIRMATION)); + } + + log(i("View caches information in a cluster. For more details type:")); + log(i(String.join(" ", UTILITY_NAME, CACHE.text(), HELP.text()), 2)); + nl(); + + log("By default commands affecting the cluster require interactive confirmation."); + log("Use " + CMD_AUTO_CONFIRMATION + " option to disable it."); + nl(); + + log("Default values:"); + log(i("HOST_OR_IP=" + DFLT_HOST, 2)); + log(i("PORT=" + DFLT_PORT, 2)); + log(i("PING_INTERVAL=" + DFLT_PING_INTERVAL, 2)); + log(i("PING_TIMEOUT=" + DFLT_PING_TIMEOUT, 2)); + log(i("SSL_PROTOCOL=" + DFLT_SSL_PROTOCOL, 2)); + log(i("SSL_KEY_ALGORITHM=" + SslContextFactory.DFLT_KEY_ALGORITHM, 2)); + log(i("KEY_STORE_TYPE=" + SslContextFactory.DFLT_STORE_TYPE, 2)); + log(i("TRUST_STORE_TYPE=" + SslContextFactory.DFLT_STORE_TYPE, 2)); + nl(); + + log("Exit codes:"); + log(i(EXIT_CODE_OK + " - successful execution.", 2)); + log(i(EXIT_CODE_INVALID_ARGUMENTS + " - invalid arguments.", 2)); + log(i(EXIT_CODE_CONNECTION_FAILED + " - connection failed.", 2)); + log(i(ERR_AUTHENTICATION_FAILED + " - authentication failed.", 2)); + log(i(EXIT_CODE_UNEXPECTED_ERROR + " - unexpected error.", 2)); + } + /** * Parse and execute command. * @@ -2556,48 +2664,7 @@ public class CommandHandler { try { if (F.isEmpty(rawArgs) || (rawArgs.size() == 1 && CMD_HELP.equalsIgnoreCase(rawArgs.get(0)))) { - log("This utility can do the following commands:"); - - usage(i("Activate cluster:"), ACTIVATE); - usage(i("Deactivate cluster:"), DEACTIVATE, op(CMD_AUTO_CONFIRMATION)); - usage(i("Print current cluster state:"), STATE); - usage(i("Print cluster baseline topology:"), BASELINE); - usage(i("Add nodes into baseline topology:"), BASELINE, BASELINE_ADD, "consistentId1[,consistentId2,....,consistentIdN]", op(CMD_AUTO_CONFIRMATION)); - usage(i("Remove nodes from baseline topology:"), BASELINE, BASELINE_REMOVE, "consistentId1[,consistentId2,....,consistentIdN]", op(CMD_AUTO_CONFIRMATION)); - usage(i("Set baseline topology:"), BASELINE, BASELINE_SET, "consistentId1[,consistentId2,....,consistentIdN]", op(CMD_AUTO_CONFIRMATION)); - usage(i("Set baseline topology based on version:"), BASELINE, BASELINE_SET_VERSION + " topologyVersion", op(CMD_AUTO_CONFIRMATION)); - usage(i("List or kill transactions:"), TX, op(TX_XID, "XID"), op(TX_DURATION, "SECONDS"), op(TX_SIZE, "SIZE"), op(TX_LABEL, "PATTERN_REGEX"), op(or(TX_SERVERS, TX_CLIENTS)), op(TX_NODES, "consistentId1[,consistentId2,....,consistentIdN]"), op(TX_LIMIT, "NUMBER"), op(TX_ORDER, or("DURATION", "SIZE", CMD_TX_ORDER_START_TIME)), op(TX_KILL), op(CMD_AUTO_CONFIRMATION)); - - if (enableExperimental) { - usage(i("Print absolute paths of unused archived wal segments on each node:"), WAL, WAL_PRINT, "[consistentId1,consistentId2,....,consistentIdN]"); - usage(i("Delete unused archived wal segments on each node:"), WAL, WAL_DELETE, "[consistentId1,consistentId2,....,consistentIdN] ", op(CMD_AUTO_CONFIRMATION)); - } - - log(i("View caches information in a cluster. For more details type:")); - log(i(String.join(" ", UTILITY_NAME, CACHE.text(), HELP.text()), 2)); - nl(); - - log("By default commands affecting the cluster require interactive confirmation."); - log("Use " + CMD_AUTO_CONFIRMATION + " option to disable it."); - nl(); - - log("Default values:"); - log(i("HOST_OR_IP=" + DFLT_HOST, 2)); - log(i("PORT=" + DFLT_PORT, 2)); - log(i("PING_INTERVAL=" + DFLT_PING_INTERVAL, 2)); - log(i("PING_TIMEOUT=" + DFLT_PING_TIMEOUT, 2)); - log(i("SSL_PROTOCOL=" + SslContextFactory.DFLT_SSL_PROTOCOL, 2)); - log(i("SSL_KEY_ALGORITHM=" + SslContextFactory.DFLT_KEY_ALGORITHM, 2)); - log(i("KEY_STORE_TYPE=" + SslContextFactory.DFLT_STORE_TYPE, 2)); - log(i("TRUST_STORE_TYPE=" + SslContextFactory.DFLT_STORE_TYPE, 2)); - nl(); - - log("Exit codes:"); - log(i(EXIT_CODE_OK + " - successful execution.", 2)); - log(i(EXIT_CODE_INVALID_ARGUMENTS + " - invalid arguments.", 2)); - log(i(EXIT_CODE_CONNECTION_FAILED + " - connection failed.", 2)); - log(i(ERR_AUTHENTICATION_FAILED + " - authentication failed.", 2)); - log(i(EXIT_CODE_UNEXPECTED_ERROR + " - unexpected error.", 2)); + printHelp(); return EXIT_CODE_OK; } @@ -2650,7 +2717,7 @@ public class CommandHandler { List<String> sslProtocols = split(args.sslProtocol(), ","); - String sslProtocol = F.isEmpty(sslProtocols) ? SslContextFactory.DFLT_SSL_PROTOCOL : sslProtocols.get(0); + String sslProtocol = F.isEmpty(sslProtocols) ? DFLT_SSL_PROTOCOL : sslProtocols.get(0); factory.setProtocol(sslProtocol); factory.setKeyAlgorithm(args.sslKeyAlgorithm()); @@ -2660,13 +2727,12 @@ public class CommandHandler { factory.setCipherSuites(split(args.getSslCipherSuites(), ",")); - if (args.sslKeyStorePath() == null) - throw new IllegalArgumentException("SSL key store location is not specified."); - factory.setKeyStoreFilePath(args.sslKeyStorePath()); if (args.sslKeyStorePassword() != null) factory.setKeyStorePassword(args.sslKeyStorePassword()); + else + factory.setKeyStorePassword(requestPasswordFromConsole("SSL keystore password: ")); factory.setKeyStoreType(args.sslKeyStoreType()); @@ -2677,6 +2743,8 @@ public class CommandHandler { if (args.sslTrustStorePassword() != null) factory.setTrustStorePassword(args.sslTrustStorePassword()); + else + factory.setTrustStorePassword(requestPasswordFromConsole("SSL truststore password: ")); factory.setTrustStoreType(args.sslTrustStoreType()); } @@ -2726,27 +2794,10 @@ public class CommandHandler { if (tryConnectMaxCount > 0 && isAuthError(e)) { log("Authentication error, try connection again."); - final Console console = System.console(); - - if (console != null) { - if (F.isEmpty(args.getUserName())) - args.setUserName(console.readLine("user: ")); + if (F.isEmpty(args.getUserName())) + args.setUserName(requestDataFromConsole("user: ")); - args.setPassword(new String(console.readPassword("password: "))); - } - else { - Scanner scanner = new Scanner(System.in); - - if (F.isEmpty(args.getUserName())) { - log("user: "); - - args.setUserName(scanner.next()); - } - - log("password: "); - - args.setPassword(scanner.next()); - } + args.setPassword(new String(requestPasswordFromConsole("password: "))); tryConnectAgain = true; @@ -2791,7 +2842,6 @@ public class CommandHandler { * * @return Last operation result; */ - @SuppressWarnings("unchecked") public <T> T getLastOperationResult() { return (T)lastOperationRes; }
