From 6116b461511b99d06fe33ac02dfd7d6e90b41d26 Mon Sep 17 00:00:00 2001 From: rs-eliatra Date: Tue, 22 Feb 2022 21:28:57 +0100 Subject: [PATCH] TranposrtClient removal from SecurityAdmin Signed-off-by: rs-eliatra --- .../security/OpenSearchSecurityPlugin.java | 5 +- .../ConfigUpdateNodeResponse.java | 14 +- .../configupdate/ConfigUpdateResponse.java | 18 +- .../security/filter/SecurityRestFilter.java | 6 +- .../rest/SecurityConfigUpdateAction.java | 82 + .../security/rest/SecurityWhoAmIAction.java | 121 ++ .../security/support/WildcardMatcher.java | 2 +- .../security/tools/SecurityAdmin.java | 1757 +++++++++-------- .../InitializationIntegrationTests.java | 1 - .../SecurityAdminIEndpointsTests.java | 109 + .../SecurityAdminInvalidConfigsTests.java | 31 +- .../security/SecurityAdminMigrationTests.java | 4 +- .../security/SecurityAdminTests.java | 158 +- 13 files changed, 1456 insertions(+), 852 deletions(-) create mode 100644 src/main/java/org/opensearch/security/rest/SecurityConfigUpdateAction.java create mode 100644 src/main/java/org/opensearch/security/rest/SecurityWhoAmIAction.java create mode 100644 src/test/java/org/opensearch/security/SecurityAdminIEndpointsTests.java diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 6e42c73b05..95f7d5d33b 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -57,6 +57,8 @@ import org.opensearch.security.dlic.rest.api.SecurityRestApiActions; import org.opensearch.security.filter.SecurityRestFilter; import org.opensearch.security.http.SecurityHttpServerTransport; +import org.opensearch.security.rest.SecurityConfigUpdateAction; +import org.opensearch.security.rest.SecurityWhoAmIAction; import org.opensearch.security.ssl.OpenSearchSecuritySSLPlugin; import org.opensearch.security.ssl.rest.SecuritySSLReloadCertsAction; import org.opensearch.security.ssl.rest.SecuritySSLCertsInfoAction; @@ -459,7 +461,8 @@ public List getRestHandlers(Settings settings, RestController restC handlers.add(new DashboardsInfoAction(settings, restController, Objects.requireNonNull(evaluator), Objects.requireNonNull(threadPool))); handlers.add(new TenantInfoAction(settings, restController, Objects.requireNonNull(evaluator), Objects.requireNonNull(threadPool), Objects.requireNonNull(cs), Objects.requireNonNull(adminDns), Objects.requireNonNull(cr))); - + handlers.add(new SecurityConfigUpdateAction(settings, restController,Objects.requireNonNull(threadPool), adminDns, configPath, principalExtractor)); + handlers.add(new SecurityWhoAmIAction(settings ,restController,Objects.requireNonNull(threadPool), adminDns, configPath, principalExtractor)); if (sslCertReloadEnabled) { handlers.add(new SecuritySSLReloadCertsAction(settings, restController, sks, Objects.requireNonNull(threadPool), Objects.requireNonNull(adminDns))); } diff --git a/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateNodeResponse.java b/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateNodeResponse.java index 3c68740cd8..e9c8f088e4 100644 --- a/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateNodeResponse.java +++ b/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateNodeResponse.java @@ -37,8 +37,10 @@ import org.opensearch.cluster.node.DiscoveryNode; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.xcontent.ToXContentObject; +import org.opensearch.common.xcontent.XContentBuilder; -public class ConfigUpdateNodeResponse extends BaseNodeResponse { +public class ConfigUpdateNodeResponse extends BaseNodeResponse implements ToXContentObject { private String[] updatedConfigTypes; private String message; @@ -78,4 +80,14 @@ public void writeTo(StreamOutput out) throws IOException { public String toString() { return "ConfigUpdateNodeResponse [updatedConfigTypes=" + Arrays.toString(updatedConfigTypes) + ", message=" + message + "]"; } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + builder.field("updated_config_types", updatedConfigTypes); + builder.field("updated_config_size", updatedConfigTypes == null ? 0: updatedConfigTypes.length); + builder.field("message", message); + builder.endObject(); + return builder; + } } diff --git a/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateResponse.java b/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateResponse.java index bb38593d7a..6ed7cf4154 100644 --- a/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateResponse.java +++ b/src/main/java/org/opensearch/security/action/configupdate/ConfigUpdateResponse.java @@ -38,13 +38,15 @@ import org.opensearch.cluster.ClusterName; import org.opensearch.common.io.stream.StreamInput; import org.opensearch.common.io.stream.StreamOutput; +import org.opensearch.common.xcontent.ToXContentObject; +import org.opensearch.common.xcontent.XContentBuilder; -public class ConfigUpdateResponse extends BaseNodesResponse { +public class ConfigUpdateResponse extends BaseNodesResponse implements ToXContentObject { public ConfigUpdateResponse(StreamInput in) throws IOException { super(in); } - + public ConfigUpdateResponse(final ClusterName clusterName, List nodes, List failures) { super(clusterName, nodes, failures); } @@ -58,4 +60,16 @@ public List readNodesFrom(final StreamInput in) throws public void writeNodesTo(final StreamOutput out, List nodes) throws IOException { out.writeList(nodes); } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject("configupdate_response"); + builder.field("nodes", getNodesMap()); + builder.field("node_size", getNodes().size()); + builder.field("has_failures", hasFailures()); + builder.field("failures_size", failures().size()); + builder.endObject(); + + return builder; + } } diff --git a/src/main/java/org/opensearch/security/filter/SecurityRestFilter.java b/src/main/java/org/opensearch/security/filter/SecurityRestFilter.java index 800ac13a14..be3bf44129 100644 --- a/src/main/java/org/opensearch/security/filter/SecurityRestFilter.java +++ b/src/main/java/org/opensearch/security/filter/SecurityRestFilter.java @@ -83,6 +83,8 @@ public class SecurityRestFilter { private WhitelistingSettings whitelistingSettings; private static final String HEALTH_SUFFIX = "health"; + private static final String WHO_AM_I_SUFFIX = "whoami"; + private static final String REGEX_PATH_PREFIX = "/("+ LEGACY_OPENDISTRO_PREFIX + "|" + PLUGINS_PREFIX + ")/" +"(.*)"; private static final Pattern PATTERN_PATH_PREFIX = Pattern.compile(REGEX_PATH_PREFIX); @@ -184,7 +186,9 @@ private boolean checkAndAuthenticateRequest(RestRequest request, RestChannel cha Matcher matcher = PATTERN_PATH_PREFIX.matcher(request.path()); final String suffix = matcher.matches() ? matcher.group(2) : null; - if(request.method() != Method.OPTIONS && !(HEALTH_SUFFIX.equals(suffix))) { + if(request.method() != Method.OPTIONS + && !(HEALTH_SUFFIX.equals(suffix)) + && !(WHO_AM_I_SUFFIX.equals(suffix))) { if (!registry.authenticate(request, channel, threadContext)) { // another roundtrip org.apache.logging.log4j.ThreadContext.remove("user"); diff --git a/src/main/java/org/opensearch/security/rest/SecurityConfigUpdateAction.java b/src/main/java/org/opensearch/security/rest/SecurityConfigUpdateAction.java new file mode 100644 index 0000000000..4e1ac87266 --- /dev/null +++ b/src/main/java/org/opensearch/security/rest/SecurityConfigUpdateAction.java @@ -0,0 +1,82 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.security.rest; + +import com.google.common.collect.ImmutableList; +import org.opensearch.client.node.NodeClient; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.rest.*; +import org.opensearch.rest.action.RestActions.NodesResponseRestListener; +import org.opensearch.security.action.configupdate.ConfigUpdateAction; +import org.opensearch.security.action.configupdate.ConfigUpdateRequest; +import org.opensearch.security.configuration.AdminDNs; +import org.opensearch.security.ssl.transport.PrincipalExtractor; +import org.opensearch.security.ssl.util.SSLRequestHelper; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.security.user.User; +import org.opensearch.threadpool.ThreadPool; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +import static org.opensearch.rest.RestRequest.Method.PUT; +import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix; + +public class SecurityConfigUpdateAction extends BaseRestHandler { + + private static final List routes = addRoutesPrefix(ImmutableList.of( + new Route(PUT, "/configupdate")), + "/_plugins/_security"); + + private final ThreadContext threadContext; + private final AdminDNs adminDns; + private final Settings settings; + private final Path configPath; + private final PrincipalExtractor principalExtractor; + + public SecurityConfigUpdateAction(final Settings settings, final RestController controller, final ThreadPool threadPool, final AdminDNs adminDns, + Path configPath, PrincipalExtractor principalExtractor) { + super(); + this.threadContext = threadPool.getThreadContext(); + this.adminDns = adminDns; + this.settings = settings; + this.configPath = configPath; + this.principalExtractor = principalExtractor; + } + + @Override public List routes() { + return routes; + } + + @Override protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + String[] configTypes = request.paramAsStringArrayOrEmptyIfAll("config_types"); + + SSLRequestHelper.SSLInfo sslInfo = SSLRequestHelper.getSSLInfo(settings, configPath, request, principalExtractor); + + if (sslInfo == null) { + return channel -> channel.sendResponse(new BytesRestResponse(RestStatus.FORBIDDEN, "")); + } + + final User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); + + //only allowed for admins + if (user == null || !adminDns.isAdmin(user)) { + return channel -> channel.sendResponse(new BytesRestResponse(RestStatus.FORBIDDEN, "")); + } else { + ConfigUpdateRequest configUpdateRequest = new ConfigUpdateRequest(configTypes); + return channel -> { + client.execute(ConfigUpdateAction.INSTANCE, configUpdateRequest, new NodesResponseRestListener<>(channel)); + }; + } + } + + @Override public String getName() { + return "Security config update"; + } + +} diff --git a/src/main/java/org/opensearch/security/rest/SecurityWhoAmIAction.java b/src/main/java/org/opensearch/security/rest/SecurityWhoAmIAction.java new file mode 100644 index 0000000000..917b81f8d3 --- /dev/null +++ b/src/main/java/org/opensearch/security/rest/SecurityWhoAmIAction.java @@ -0,0 +1,121 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.security.rest; + +import com.google.common.collect.ImmutableList; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.client.node.NodeClient; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.xcontent.XContentBuilder; +import org.opensearch.rest.BaseRestHandler; +import org.opensearch.rest.BytesRestResponse; +import org.opensearch.rest.RestChannel; +import org.opensearch.rest.RestController; +import org.opensearch.rest.RestRequest; +import org.opensearch.rest.RestStatus; +import org.opensearch.security.configuration.AdminDNs; +import org.opensearch.security.ssl.transport.PrincipalExtractor; +import org.opensearch.security.ssl.util.SSLRequestHelper; +import org.opensearch.security.ssl.util.SSLRequestHelper.SSLInfo; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.security.support.WildcardMatcher; +import org.opensearch.threadpool.ThreadPool; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; + +import static org.opensearch.rest.RestRequest.Method.GET; +import static org.opensearch.rest.RestRequest.Method.POST; +import static org.opensearch.security.dlic.rest.support.Utils.addRoutesPrefix; + + +public class SecurityWhoAmIAction extends BaseRestHandler { + + private static final List routes = addRoutesPrefix(ImmutableList.of( + new Route(GET, "/whoami"), + new Route(POST, "/whoami")), + "/_plugins/_security"); + + private final Logger log = LogManager.getLogger(this.getClass()); + private final AdminDNs adminDns; + private final Settings settings; + private final Path configPath; + private final PrincipalExtractor principalExtractor; + private final List nodesDn ; + + public SecurityWhoAmIAction(final Settings settings, final RestController controller, + final ThreadPool threadPool, final AdminDNs adminDns, Path configPath, PrincipalExtractor principalExtractor) { + super(); + this.adminDns = adminDns; + this.settings = settings; + this.configPath = configPath; + this.principalExtractor = principalExtractor; + + nodesDn = settings.getAsList(ConfigConstants.SECURITY_NODES_DN, Collections.emptyList()); + } + + @Override + public List routes() { + return routes; + } + + @Override + protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + return new RestChannelConsumer() { + + @Override + public void accept(RestChannel channel) throws Exception { + XContentBuilder builder = channel.newBuilder(); + BytesRestResponse response = null; + + try { + + SSLInfo sslInfo = SSLRequestHelper.getSSLInfo(settings, configPath, request, principalExtractor); + + if(sslInfo == null) { + response = new BytesRestResponse(RestStatus.FORBIDDEN, "No security data"); + } else { + + final String dn = sslInfo.getPrincipal(); + final boolean isAdmin = adminDns.isAdminDN(dn); + final boolean isNodeCertificateRequest = dn != null && WildcardMatcher.from(nodesDn, true).matchAny(dn); + + builder.startObject(); + builder.field("dn", dn); + builder.field("is_admin", isAdmin); + builder.field("is_node_certificate_request", isNodeCertificateRequest); + builder.endObject(); + + response = new BytesRestResponse(RestStatus.OK, builder); + + } + } catch (final Exception e1) { + log.error(e1.toString(), e1); + builder = channel.newBuilder(); + builder.startObject(); + builder.field("error", e1.toString()); + builder.endObject(); + response = new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, builder); + } finally { + if (builder != null) { + builder.close(); + } + } + + channel.sendResponse(response); + } + }; + } + + @Override + public String getName() { + return "Search Guard Who am i"; + } + +} diff --git a/src/main/java/org/opensearch/security/support/WildcardMatcher.java b/src/main/java/org/opensearch/security/support/WildcardMatcher.java index 7cbda07753..a62ed2567e 100644 --- a/src/main/java/org/opensearch/security/support/WildcardMatcher.java +++ b/src/main/java/org/opensearch/security/support/WildcardMatcher.java @@ -252,7 +252,7 @@ public boolean matchAny(Collection candidates) { return matchAny(candidates.stream()); } - public boolean matchAny(String[] candidates) { + public boolean matchAny(String... candidates) { return matchAny(Arrays.stream(candidates)); } diff --git a/src/main/java/org/opensearch/security/tools/SecurityAdmin.java b/src/main/java/org/opensearch/security/tools/SecurityAdmin.java index a80f60c560..3964c9db93 100644 --- a/src/main/java/org/opensearch/security/tools/SecurityAdmin.java +++ b/src/main/java/org/opensearch/security/tools/SecurityAdmin.java @@ -30,30 +30,13 @@ package org.opensearch.security.tools; -import java.io.ByteArrayInputStream; -import java.io.Console; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.Reader; -import java.io.Writer; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.nio.charset.StandardCharsets; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import org.opensearch.security.OpenSearchSecurityPlugin; -import org.opensearch.security.auditlog.config.AuditConfig; -import org.opensearch.security.securityconf.impl.NodesDn; -import org.opensearch.security.securityconf.impl.WhitelistingSettings; -import org.opensearch.security.ssl.OpenSearchSecuritySSLPlugin; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.base.Charsets; +import com.google.common.base.Joiner; +import com.google.common.collect.Iterators; +import com.google.common.io.ByteSource; +import com.google.common.io.CharStreams; +import com.google.common.io.Files; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; @@ -61,41 +44,42 @@ import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; -import org.opensearch.OpenSearchException; +import org.apache.http.HttpHost; +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.ssl.SSLContexts; import org.opensearch.ExceptionsHelper; +import org.opensearch.OpenSearchException; +import org.opensearch.OpenSearchStatusException; import org.opensearch.Version; import org.opensearch.action.admin.cluster.health.ClusterHealthRequest; import org.opensearch.action.admin.cluster.health.ClusterHealthResponse; -import org.opensearch.action.admin.cluster.node.info.NodesInfoRequest; -import org.opensearch.action.admin.cluster.node.info.NodesInfoResponse; -import org.opensearch.action.admin.cluster.node.info.PluginsAndModules; -import org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest; -import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse; import org.opensearch.action.admin.cluster.settings.ClusterUpdateSettingsRequest; -import org.opensearch.action.admin.cluster.tasks.PendingClusterTasksRequest; -import org.opensearch.action.admin.cluster.tasks.PendingClusterTasksResponse; import org.opensearch.action.admin.indices.create.CreateIndexRequest; import org.opensearch.action.admin.indices.delete.DeleteIndexRequest; import org.opensearch.action.admin.indices.get.GetIndexRequest; import org.opensearch.action.admin.indices.get.GetIndexRequest.Feature; import org.opensearch.action.admin.indices.get.GetIndexResponse; import org.opensearch.action.admin.indices.settings.put.UpdateSettingsRequest; -import org.opensearch.action.admin.indices.stats.IndicesStatsRequest; -import org.opensearch.action.admin.indices.stats.IndicesStatsResponse; import org.opensearch.action.get.GetRequest; import org.opensearch.action.get.GetResponse; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.support.WriteRequest.RefreshPolicy; import org.opensearch.action.support.master.AcknowledgedResponse; -import org.opensearch.client.Client; +import org.opensearch.client.Request; +import org.opensearch.client.RequestOptions; +import org.opensearch.client.Response; +import org.opensearch.client.RestClient; +import org.opensearch.client.RestClientBuilder; +import org.opensearch.client.RestHighLevelClient; import org.opensearch.client.transport.NoNodeAvailableException; -import org.opensearch.client.transport.TransportClient; import org.opensearch.cluster.health.ClusterHealthStatus; import org.opensearch.common.Strings; import org.opensearch.common.bytes.BytesReference; import org.opensearch.common.collect.Tuple; import org.opensearch.common.settings.Settings; -import org.opensearch.common.transport.TransportAddress; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.xcontent.NamedXContentRegistry; import org.opensearch.common.xcontent.XContentBuilder; @@ -104,28 +88,14 @@ import org.opensearch.common.xcontent.XContentType; import org.opensearch.common.xcontent.json.JsonXContent; import org.opensearch.index.IndexNotFoundException; -import org.opensearch.plugins.Plugin; -import org.opensearch.plugins.PluginInfo; -import org.opensearch.security.action.configupdate.ConfigUpdateAction; -import org.opensearch.security.action.configupdate.ConfigUpdateNodeResponse; -import org.opensearch.security.action.configupdate.ConfigUpdateRequest; -import org.opensearch.security.action.configupdate.ConfigUpdateResponse; -import org.opensearch.security.action.whoami.WhoAmIAction; -import org.opensearch.security.action.whoami.WhoAmIRequest; -import org.opensearch.security.action.whoami.WhoAmIResponse; -import org.opensearch.security.securityconf.Migration; -import org.opensearch.security.ssl.util.ExceptionUtils; -import org.opensearch.security.ssl.util.SSLConfigConstants; -import org.opensearch.security.support.ConfigConstants; -import org.opensearch.security.support.ConfigHelper; -import org.opensearch.security.support.SecurityJsonNode; -import org.opensearch.security.support.SecurityUtils; -import org.opensearch.transport.Netty4Plugin; - -import com.fasterxml.jackson.databind.JsonNode; +import org.opensearch.rest.RestStatus; import org.opensearch.security.DefaultObjectMapper; +import org.opensearch.security.auditlog.config.AuditConfig; +import org.opensearch.security.securityconf.Migration; import org.opensearch.security.securityconf.impl.CType; +import org.opensearch.security.securityconf.impl.NodesDn; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; +import org.opensearch.security.securityconf.impl.WhitelistingSettings; import org.opensearch.security.securityconf.impl.v6.RoleMappingsV6; import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7; import org.opensearch.security.securityconf.impl.v7.ConfigV7; @@ -133,10 +103,41 @@ import org.opensearch.security.securityconf.impl.v7.RoleMappingsV7; import org.opensearch.security.securityconf.impl.v7.RoleV7; import org.opensearch.security.securityconf.impl.v7.TenantV7; -import com.google.common.io.CharStreams; -import com.google.common.io.Files; +import org.opensearch.security.ssl.util.ExceptionUtils; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.security.support.ConfigHelper; +import org.opensearch.security.support.PemKeyReader; +import org.opensearch.security.support.SecurityJsonNode; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import java.io.ByteArrayInputStream; +import java.io.Console; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.Writer; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; import static org.opensearch.common.xcontent.DeprecationHandler.THROW_UNSUPPORTED_OPERATION; +import static org.opensearch.security.support.SecurityUtils.replaceEnvVars; @SuppressWarnings("deprecation") public class SecurityAdmin { @@ -213,7 +214,6 @@ public static int execute(final String[] args) throws Exception { options.addOption(Option.builder("r").longOpt("retrieve").desc("retrieve current config").build()); options.addOption(Option.builder("f").longOpt("file").hasArg().argName("file").desc("file").build()); options.addOption(Option.builder("t").longOpt("type").hasArg().argName("file-type").desc("file-type").build()); - options.addOption(Option.builder("tsalias").longOpt("truststore-alias").hasArg().argName("alias").desc("Truststore alias").build()); options.addOption(Option.builder("ksalias").longOpt("keystore-alias").hasArg().argName("alias").desc("Keystore alias").build()); options.addOption(Option.builder("ec").longOpt("enabled-ciphers").hasArg().argName("cipers").desc("Comma separated list of enabled TLS ciphers").build()); options.addOption(Option.builder("ep").longOpt("enabled-protocols").hasArg().argName("protocols").desc("Comma separated list of enabled TLS protocols").build()); @@ -275,7 +275,6 @@ public static int execute(final String[] args) throws Exception { String type = null; boolean retrieve = false; String ksAlias = null; - String tsAlias = null; String[] enabledProtocols = new String[0]; String[] enabledCiphers = new String[0]; Integer updateSettings = null; @@ -342,7 +341,6 @@ public static int execute(final String[] args) throws Exception { type = line.getOptionValue("t", type); retrieve = line.hasOption("r"); ksAlias = line.getOptionValue("ksalias", ksAlias); - tsAlias = line.getOptionValue("tsalias", tsAlias); index = line.getOptionValue("i", index); String enabledCiphersString = line.getOptionValue("ec", null); @@ -408,7 +406,6 @@ public static int execute(final String[] args) throws Exception { return -1; } - if(validateConfig != null) { System.out.println("Validate configuration for Version "+validateConfig.intValue()); return validateConfig(cd, file, type, validateConfig.intValue()); @@ -446,98 +443,54 @@ public static int execute(final String[] args) throws Exception { } System.out.println(" ... done"); - - final Settings.Builder settingsBuilder = Settings - .builder() - .put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_ENFORCE_HOSTNAME_VERIFICATION, !nhnv) - .put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_ENFORCE_HOSTNAME_VERIFICATION_RESOLVE_HOST_NAME, !nrhn) - .put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_ENABLED, true) - .put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_ENABLE_OPENSSL_IF_AVAILABLE, OpenSearchSecuritySSLPlugin.OPENSSL_SUPPORTED && useOpenSSLIfAvailable) - .putList(SSLConfigConstants.SECURITY_SSL_TRANSPORT_ENABLED_CIPHERS, enabledCiphers) - .putList(SSLConfigConstants.SECURITY_SSL_TRANSPORT_ENABLED_PROTOCOLS, enabledProtocols) - - .put("cluster.name", clustername) - .put("client.transport.ignore_cluster_name", icl) - .put("client.transport.sniff", sniff); - - if(ksAlias != null) { - settingsBuilder.put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_KEYSTORE_ALIAS, ksAlias); - } - - if(tsAlias != null) { - settingsBuilder.put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_TRUSTSTORE_ALIAS, tsAlias); - } - + if(ks != null) { - settingsBuilder.put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_KEYSTORE_FILEPATH, ks); - settingsBuilder.put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_KEYSTORE_TYPE, kst==null?(ks.endsWith(".jks")?"JKS":"PKCS12"):kst); - if(kspass == null && promptForPassword) { kspass = promptForPassword("Keystore", "kspass", OPENDISTRO_SECURITY_KS_PASS); } - - if(kspass != null) { - settingsBuilder.put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_KEYSTORE_PASSWORD, kspass); - } } if(ts != null) { - settingsBuilder.put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_TRUSTSTORE_FILEPATH, ts); - settingsBuilder.put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_TRUSTSTORE_TYPE, tst==null?(ts.endsWith(".jks")?"JKS":"PKCS12"):tst); - if(tspass == null && promptForPassword) { tspass = promptForPassword("Truststore", "tspass", OPENDISTRO_SECURITY_TS_PASS); } - - if(tspass != null) { - settingsBuilder.put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_TRUSTSTORE_PASSWORD, tspass); - } } - - if(cacert != null) { - settingsBuilder.put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_PEMTRUSTEDCAS_FILEPATH, cacert); - } - - if(cert != null) { - settingsBuilder.put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_PEMCERT_FILEPATH, cert); - } - + if(key != null) { - settingsBuilder.put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_PEMKEY_FILEPATH, key); - + if(keypass == null && promptForPassword) { keypass = promptForPassword("Pemkey", "keypass", OPENDISTRO_SECURITY_KEYPASS); } - - if(keypass != null) { - settingsBuilder.put(SSLConfigConstants.SECURITY_SSL_TRANSPORT_PEMKEY_PASSWORD, keypass); - } + } - Settings settings = settingsBuilder.build(); + final SSLContext sslContext = sslContext(ks, kspass, ts, tspass, cacert, cert, key, keypass, ksAlias); - try (@SuppressWarnings("resource") - TransportClient tc = new TransportClientImpl(settings, asCollection(Netty4Plugin.class, OpenSearchSecurityPlugin.class)) - .addTransportAddress(new TransportAddress(new InetSocketAddress(hostname, port)))) { + try (RestHighLevelClient restHighLevelClient = getRestHighLevelClient(sslContext, nhnv, enabledProtocols, enabledCiphers, hostname, port)) { - - final WhoAmIResponse whoAmIRes = tc.execute(WhoAmIAction.INSTANCE, new WhoAmIRequest()).actionGet(); - System.out.println("Connected as "+whoAmIRes.getDn()); + Response whoAmIRes = restHighLevelClient.getLowLevelClient().performRequest(new Request("GET", "/_plugins/_security/whoami")); + if (whoAmIRes.getStatusLine().getStatusCode() != 200) { + System.out.println("Unable to check whether cluster is sane because return code was " + whoAmIRes.getStatusLine()); + return (-1); + } - if(!whoAmIRes.isAdmin()) { - - System.out.println("ERR: "+whoAmIRes.getDn()+" is not an admin user"); - - if(!whoAmIRes.isNodeCertificateRequest()) { - System.out.println("Seems you use a client certificate but this one is not registered as admin_dn"); - System.out.println("Make sure opensearch.yml on all nodes contains:"); + JsonNode whoAmIResNode = DefaultObjectMapper.objectMapper.readTree(whoAmIRes.getEntity().getContent()); + System.out.println("Connected as " + whoAmIResNode.get("dn")); + + if (!whoAmIResNode.get("is_admin").asBoolean()) { + + System.out.println("ERR: " + whoAmIResNode.get("dn") + " is not an admin user"); + + if (!whoAmIResNode.get("is_node_certificate_request").asBoolean()) { + System.out.println("Seems you use a client certificate but this one is not registered as admin_dn"); + System.out.println("Make sure opensearch.yml on all nodes contains:"); System.out.println("plugins.security.authcz.admin_dn:"+System.lineSeparator()+ - " - \""+whoAmIRes.getDn()+"\""); + " - \"" + whoAmIResNode.get("dn") + "\""); } else { System.out.println("Seems you use a node certificate. This is not permitted, you have to use a client certificate and register it as admin_dn in opensearch.yml"); } return (-1); - } else if(whoAmIRes.isNodeCertificateRequest()) { + } else if (whoAmIResNode.get("is_node_certificate_request").asBoolean()) { System.out.println("ERR: Seems you use a node certificate which is also an admin certificate"); System.out.println(" That may have worked with older OpenSearch Security versions but it indicates"); System.out.println(" a configuration error and is therefore forbidden now."); @@ -548,7 +501,7 @@ public static int execute(final String[] args) throws Exception { } try { - if(issueWarnings(tc) != 0) { + if(issueWarnings(restHighLevelClient) != 0) { return (-1); } } catch (Exception e1) { @@ -556,180 +509,210 @@ public static int execute(final String[] args) throws Exception { throw e1; } - if(updateSettings != null) { - Settings indexSettings = Settings.builder().put("index.number_of_replicas", updateSettings).build(); - ConfigUpdateResponse res = tc.execute(ConfigUpdateAction.INSTANCE, new ConfigUpdateRequest(getTypes(true))).actionGet(); - if(res.hasFailures()) { - System.out.println("ERR: Unabe to reload config due to "+res.failures()); - } - final AcknowledgedResponse response = tc.admin().indices().updateSettings((new UpdateSettingsRequest(index).settings(indexSettings))).actionGet(); - System.out.println("Reload config on all nodes"); - System.out.println("Update number of replicas to "+(updateSettings) +" with result: "+response.isAcknowledged()); - return ((response.isAcknowledged() && !res.hasFailures())?0:-1); - } - - if(reload) { - ConfigUpdateResponse res = tc.execute(ConfigUpdateAction.INSTANCE, new ConfigUpdateRequest(getTypes(false))).actionGet(); - if(res.hasFailures()) { - System.out.println("ERR: Unabe to reload config due to "+res.failures()); - return -1; - } - System.out.println("Reload config on all nodes"); - return 0; - } - - if(si) { - return (0); - } - - if(whoami) { - System.out.println(whoAmIRes.toString()); - return (0); - } - - - if(replicaAutoExpand != null) { - Settings indexSettings = Settings.builder() - .put("index.auto_expand_replicas", replicaAutoExpand?"0-all":"false") - .build(); - ConfigUpdateResponse res = tc.execute(ConfigUpdateAction.INSTANCE, new ConfigUpdateRequest(getTypes(false))).actionGet(); - if(res.hasFailures()) { - System.out.println("ERR: Unabe to reload config due to "+res.failures()); - } - final AcknowledgedResponse response = tc.admin().indices().updateSettings((new UpdateSettingsRequest(index).settings(indexSettings))).actionGet(); - System.out.println("Reload config on all nodes"); - System.out.println("Auto-expand replicas "+(replicaAutoExpand?"enabled":"disabled")); - return ((response.isAcknowledged() && !res.hasFailures())?0:-1); - } - - if(enableShardAllocation) { - final boolean successful = tc.admin().cluster() - .updateSettings(new ClusterUpdateSettingsRequest() - .transientSettings(ENABLE_ALL_ALLOCATIONS_SETTINGS) - .persistentSettings(ENABLE_ALL_ALLOCATIONS_SETTINGS)) - .actionGet() - .isAcknowledged(); - - if(successful) { - System.out.println("Persistent and transient shard allocation enabled"); + if (updateSettings != null) { + Settings indexSettings = Settings.builder().put("index.number_of_replicas", updateSettings).build(); + Response res = restHighLevelClient.getLowLevelClient() + .performRequest(new Request("PUT", "/_plugins/_security/configupdate?config_types=" + Joiner.on(",").join(getTypes(true)))); + + if (res.getStatusLine().getStatusCode() != 200) { + System.out.println("Unable to reload configuration because return code was " + res.getStatusLine()); + return (-1); + } + + JsonNode resNode = DefaultObjectMapper.objectMapper.readTree(res.getEntity().getContent()); + + if (resNode.get("configupdate_response").get("has_failures").asBoolean()) { + System.out.println("ERR: Unable to reload config due to " + responseToString(res, false) + "/" + resNode); + } + final AcknowledgedResponse response = restHighLevelClient.indices().putSettings((new UpdateSettingsRequest(index).settings(indexSettings)), RequestOptions.DEFAULT); + System.out.println("Reload config on all nodes"); + System.out.println("Update number of replicas to " + (updateSettings) + " with result: " + response.isAcknowledged()); + return ((response.isAcknowledged() && !resNode.get("configupdate_response").get("has_failures").asBoolean()) ? 0 : -1); + } + + if (reload) { + Response res = restHighLevelClient.getLowLevelClient() + .performRequest(new Request("PUT", "/_plugins/_security/configupdate?config_types=" + Joiner.on(",").join(getTypes(false)))); + + if (res.getStatusLine().getStatusCode() != 200) { + System.out.println("Unable to reload configuration because return code was " + res.getStatusLine()); + return (-1); + } + + JsonNode resNode = DefaultObjectMapper.objectMapper.readTree(res.getEntity().getContent()); + if (resNode.get("configupdate_response").get("has_failures").asBoolean()) { + System.out.println("ERR: Unable to reload config due to " + responseToString(res, false) + "/" + resNode); + return -1; + } + System.out.println("Reload config on all nodes"); + return 0; + } + + if (si) { + return (0); + } + + if (whoami) { + System.out.println(whoAmIResNode.toPrettyString()); + return (0); + } + + + if (replicaAutoExpand != null) { + Settings indexSettings = Settings.builder() + .put("index.auto_expand_replicas", replicaAutoExpand ? "0-all" : "false") + .build(); + Response res = restHighLevelClient.getLowLevelClient().performRequest(new Request("PUT", "/_plugins/_security/configupdate?config_types=" + Joiner.on(",").join(getTypes(false)))); + + if (res.getStatusLine().getStatusCode() != 200) { + System.out.println("Unable to reload configuration because return code was " + whoAmIRes.getStatusLine()); + return (-1); + } + + JsonNode resNode = DefaultObjectMapper.objectMapper.readTree(res.getEntity().getContent()); + + if (resNode.get("configupdate_response").get("has_failures").asBoolean()) { + System.out.println("ERR: Unable to reload config due to " + responseToString(res, false) + "/" + resNode); + } + final AcknowledgedResponse response = restHighLevelClient.indices().putSettings((new UpdateSettingsRequest(index).settings(indexSettings)), RequestOptions.DEFAULT); + System.out.println("Reload config on all nodes"); + System.out.println("Auto-expand replicas " + (replicaAutoExpand ? "enabled" : "disabled")); + return ((response.isAcknowledged() && !resNode.get("configupdate_response").get("has_failures").asBoolean()) ? 0 : -1); + } + + if (enableShardAllocation) { + final boolean successful = restHighLevelClient.cluster() + .putSettings(new ClusterUpdateSettingsRequest() + .transientSettings(ENABLE_ALL_ALLOCATIONS_SETTINGS) + .persistentSettings(ENABLE_ALL_ALLOCATIONS_SETTINGS), RequestOptions.DEFAULT) + .isAcknowledged(); + + if (successful) { + System.out.println("Persistent and transient shard allocation enabled"); + } else { + System.out.println("ERR: Unable to enable shard allocation"); + } + + return (successful ? 0 : -1); + } + + if (failFast) { + System.out.println("Fail-fast is activated"); + } + + if (diagnose) { + generateDiagnoseTrace(restHighLevelClient); + } + + System.out.println("Contacting opensearch cluster '" + clustername + "'" + (acceptRedCluster ? "" : " and wait for YELLOW clusterstate") + " ..."); + + ClusterHealthResponse chResponse = null; + + while (chResponse == null) { + try { + final ClusterHealthRequest chRequest = new ClusterHealthRequest().timeout(TimeValue.timeValueMinutes(5)); + if (!acceptRedCluster) { + chRequest.waitForYellowStatus(); + } + chResponse = restHighLevelClient.cluster().health(chRequest, RequestOptions.DEFAULT); + } catch (Exception e) { + + Throwable rootCause = ExceptionUtils.getRootCause(e); + + if (!failFast) { + System.out.println("Cannot retrieve cluster state due to: " + e.getMessage() + ". This is not an error, will keep on trying ..."); + System.out.println(" Root cause: " + rootCause + " (" + e.getClass().getName() + "/" + rootCause.getClass().getName() + ")"); + System.out.println(" * Try running securityadmin.sh with -icl (but no -cl) and -nhnv (If that works you need to check your clustername as well as hostnames in your TLS certificates)"); + System.out.println(" * Make sure that your keystore or PEM certificate is a client certificate (not a node certificate) and configured properly in opensearch.yml"); + System.out.println(" * If this is not working, try running securityadmin.sh with --diagnose and see diagnose trace log file)"); + System.out.println(" * Add --accept-red-cluster to allow securityadmin to operate on a red cluster."); + + } else { + System.out.println("ERR: Cannot retrieve cluster state due to: " + e.getMessage() + "."); + System.out.println(" Root cause: " + rootCause + " (" + e.getClass().getName() + "/" + rootCause.getClass().getName() + ")"); + System.out.println(" * Try running securityadmin.sh with -icl (but no -cl) and -nhnv (If that works you need to check your clustername as well as hostnames in your TLS certificates)"); + System.out.println(" * Make also sure that your keystore or PEM certificate is a client certificate (not a node certificate) and configured properly in opensearch.yml"); + System.out.println(" * If this is not working, try running securityadmin.sh with --diagnose and see diagnose trace log file)"); + System.out.println(" * Add --accept-red-cluster to allow securityadmin to operate on a red cluster."); + + return (-1); + } + + Thread.sleep(3000); + continue; + } + } + + final boolean timedOut = chResponse.isTimedOut(); + + if (!acceptRedCluster && timedOut) { + System.out.println("ERR: Timed out while waiting for a green or yellow cluster state."); + System.out.println(" * Try running securityadmin.sh with -icl (but no -cl) and -nhnv (If that works you need to check your clustername as well as hostnames in your TLS certificates)"); + System.out.println(" * Make also sure that your keystore or PEM certificate is a client certificate (not a node certificate) and configured properly in opensearch.yml"); + System.out.println(" * If this is not working, try running securityadmin.sh with --diagnose and see diagnose trace log file)"); + System.out.println(" * Add --accept-red-cluster to allow securityadmin to operate on a red cluster."); + return (-1); + } + + System.out.println("Clustername: " + chResponse.getClusterName()); + System.out.println("Clusterstate: " + chResponse.getStatus()); + System.out.println("Number of nodes: " + chResponse.getNumberOfNodes()); + System.out.println("Number of data nodes: " + chResponse.getNumberOfDataNodes()); + + + GetIndexResponse securityIndex = null; + try { + securityIndex = restHighLevelClient.indices().get(new GetIndexRequest().indices(index).addFeatures(Feature.MAPPINGS), RequestOptions.DEFAULT); + } catch (OpenSearchStatusException e1) { + if(e1.status() == RestStatus.NOT_FOUND) { + //ignore } else { - System.out.println("ERR: Unable to enable shard allocation"); - } - - return (successful?0:-1); - } - - if(failFast) { - System.out.println("Fail-fast is activated"); - } - - if(diagnose) { - generateDiagnoseTrace(tc); - } - - System.out.println("Contacting opensearch cluster '"+clustername+"'"+(acceptRedCluster?"":" and wait for YELLOW clusterstate")+" ..."); - - ClusterHealthResponse chr = null; - - while(chr == null) { - try { - final ClusterHealthRequest chrequest = new ClusterHealthRequest().timeout(TimeValue.timeValueMinutes(5)); - if(!acceptRedCluster) { - chrequest.waitForYellowStatus(); - } - chr = tc.admin().cluster().health(chrequest).actionGet(); - } catch (Exception e) { - - Throwable rootCause = ExceptionUtils.getRootCause(e); - - if(!failFast) { - System.out.println("Cannot retrieve cluster state due to: "+e.getMessage()+". This is not an error, will keep on trying ..."); - System.out.println(" Root cause: "+rootCause+" ("+e.getClass().getName()+"/"+rootCause.getClass().getName()+")"); - System.out.println(" * Try running securityadmin.sh with -icl (but no -cl) and -nhnv (If that works you need to check your clustername as well as hostnames in your TLS certificates)"); - System.out.println(" * Make sure that your keystore or PEM certificate is a client certificate (not a node certificate) and configured properly in opensearch.yml"); - System.out.println(" * If this is not working, try running securityadmin.sh with --diagnose and see diagnose trace log file)"); - System.out.println(" * Add --accept-red-cluster to allow securityadmin to operate on a red cluster."); - - } else { - System.out.println("ERR: Cannot retrieve cluster state due to: "+e.getMessage()+"."); - System.out.println(" Root cause: "+rootCause+" ("+e.getClass().getName()+"/"+rootCause.getClass().getName()+")"); - System.out.println(" * Try running securityadmin.sh with -icl (but no -cl) and -nhnv (If that works you need to check your clustername as well as hostnames in your TLS certificates)"); - System.out.println(" * Make also sure that your keystore or PEM certificate is a client certificate (not a node certificate) and configured properly in opensearch.yml"); - System.out.println(" * If this is not working, try running securityadmin.sh with --diagnose and see diagnose trace log file)"); - System.out.println(" * Add --accept-red-cluster to allow securityadmin to operate on a red cluster."); - - return (-1); - } - - Thread.sleep(3000); - continue; - } - } - - final boolean timedOut = chr.isTimedOut(); - - if (!acceptRedCluster && timedOut) { - System.out.println("ERR: Timed out while waiting for a green or yellow cluster state."); - System.out.println(" * Try running securityadmin.sh with -icl (but no -cl) and -nhnv (If that works you need to check your clustername as well as hostnames in your TLS certificates)"); - System.out.println(" * Make also sure that your keystore or PEM certificate is a client certificate (not a node certificate) and configured properly in opensearch.yml"); - System.out.println(" * If this is not working, try running securityadmin.sh with --diagnose and see diagnose trace log file)"); - System.out.println(" * Add --accept-red-cluster to allow securityadmin to operate on a red cluster."); - return (-1); - } - - System.out.println("Clustername: "+chr.getClusterName()); - System.out.println("Clusterstate: "+chr.getStatus()); - System.out.println("Number of nodes: "+chr.getNumberOfNodes()); - System.out.println("Number of data nodes: "+chr.getNumberOfDataNodes()); - - GetIndexResponse securityIndex = null; - try { - securityIndex = tc.admin().indices().getIndex(new GetIndexRequest().indices(index).addFeatures(Feature.MAPPINGS)).actionGet(); - } catch (IndexNotFoundException e1) { - //ignore - } - final boolean indexExists = securityIndex != null; - - final NodesInfoResponse nodesInfo = tc.admin().cluster().nodesInfo(new NodesInfoRequest()).actionGet(); - - if(deleteConfigIndex) { - return deleteConfigIndex(tc, index, indexExists); - } - - if (!indexExists) { - System.out.print(index +" index does not exists, attempt to create it ... "); - final int created = createConfigIndex(tc, index, explicitReplicas); - if(created != 0) { - return created; - } - - } else { - System.out.println(index+" index already exists, so we do not need to create one."); - - try { - ClusterHealthResponse chrsg = tc.admin().cluster().health(new ClusterHealthRequest(index)).actionGet(); - - if (chrsg.isTimedOut()) { - System.out.println("ERR: Timed out while waiting for "+index+" index state."); - } - - if (chrsg.getStatus() == ClusterHealthStatus.RED) { - System.out.println("ERR: "+index+" index state is RED."); - } - - if (chrsg.getStatus() == ClusterHealthStatus.YELLOW) { - System.out.println("INFO: "+index+" index state is YELLOW, it seems you miss some replicas"); - } - - } catch (Exception e) { - if(!failFast) { - System.out.println("Cannot retrieve "+index+" index state state due to "+e.getMessage()+". This is not an error, will keep on trying ..."); - } else { - System.out.println("ERR: Cannot retrieve "+index+" index state state due to "+e.getMessage()+"."); - return (-1); - } + System.out.println("Unable to get index because return code was " + e1.status().getStatus()); + return (-1); } - } + } + final boolean indexExists = securityIndex != null; + + int expectedNodeCount = restHighLevelClient.cluster().health(new ClusterHealthRequest(), RequestOptions.DEFAULT).getNumberOfNodes(); + + if (deleteConfigIndex) { + return deleteConfigIndex(restHighLevelClient, index, indexExists); + } + + if (!indexExists) { + System.out.print(index + " index does not exists, attempt to create it ... "); + final int created = createConfigIndex(restHighLevelClient, index, explicitReplicas); + if (created != 0) { + return created; + } + + } else { + System.out.println(index + " index already exists, so we do not need to create one."); + + try { + ClusterHealthResponse clusterHealthResponse = restHighLevelClient.cluster().health(new ClusterHealthRequest(index), RequestOptions.DEFAULT); + + if (clusterHealthResponse.isTimedOut()) { + System.out.println("ERR: Timed out while waiting for " + index + " index state."); + } + + if (clusterHealthResponse.getStatus() == ClusterHealthStatus.RED) { + System.out.println("ERR: " + index + " index state is RED."); + } + + if (clusterHealthResponse.getStatus() == ClusterHealthStatus.YELLOW) { + System.out.println("INFO: " + index + " index state is YELLOW, it seems you miss some replicas"); + } + + } catch (Exception e) { + if (!failFast) { + System.out.println("Cannot retrieve " + index + " index state state due to " + e.getMessage() + ". This is not an error, will keep on trying ..."); + } else { + System.out.println("ERR: Cannot retrieve " + index + " index state state due to " + e.getMessage() + "."); + return (-1); + } + } + } final boolean createLegacyMode = !indexExists && CREATE_AS_LEGACY; @@ -745,29 +728,29 @@ public static int execute(final String[] args) throws Exception { if(legacy) { System.out.println("Legacy index '"+index+"' (ES 6) detected (or forced). You should migrate the configuration!"); } - + if(retrieve) { String date = DATE_FORMAT.format(new Date()); - - boolean success = retrieveFile(tc, cd+"config_"+date+".yml", index, "config", legacy); - success = retrieveFile(tc, cd+"roles_"+date+".yml", index, "roles", legacy) && success; - success = retrieveFile(tc, cd+"roles_mapping_"+date+".yml", index, "rolesmapping", legacy) && success; - success = retrieveFile(tc, cd+"internal_users_"+date+".yml", index, "internalusers", legacy) && success; - success = retrieveFile(tc, cd+"action_groups_"+date+".yml", index, "actiongroups", legacy) && success; - success = retrieveFile(tc, cd+"audit_"+date+".yml", index, "audit", legacy) && success; + + boolean success = retrieveFile(restHighLevelClient, cd + "config_" + date + ".yml", index, "config", legacy); + success = retrieveFile(restHighLevelClient, cd + "roles_" + date + ".yml", index, "roles", legacy) && success; + success = retrieveFile(restHighLevelClient, cd + "roles_mapping_" + date + ".yml", index, "rolesmapping", legacy) && success; + success = retrieveFile(restHighLevelClient, cd + "internal_users_" + date + ".yml", index, "internalusers", legacy) && success; + success = retrieveFile(restHighLevelClient, cd + "action_groups_" + date + ".yml", index, "actiongroups", legacy) && success; + success = retrieveFile(restHighLevelClient, cd + "audit_" + date + ".yml", index, "audit", legacy) && success; if(!legacy) { - success = retrieveFile(tc, cd+"security_tenants_"+date+".yml", index, "tenants", legacy) && success; + success = retrieveFile(restHighLevelClient, cd + "security_tenants_" + date + ".yml", index, "tenants", legacy) && success; } final boolean populateFileIfEmpty = true; - success = retrieveFile(tc, cd+"nodes_dn_"+date+".yml", index, "nodesdn", legacy, populateFileIfEmpty) && success; - success = retrieveFile(tc, cd+"whitelist_"+date+".yml", index, "whitelist", legacy, populateFileIfEmpty) && success; + success = retrieveFile(restHighLevelClient, cd+"nodes_dn_"+date+".yml", index, "nodesdn", legacy, populateFileIfEmpty) && success; + success = retrieveFile(restHighLevelClient, cd+"whitelist_"+date+".yml", index, "whitelist", legacy, populateFileIfEmpty) && success; return (success?0:-1); } if(backup != null) { - return backup(tc, index, new File(backup), legacy); + return backup(restHighLevelClient, index, new File(backup), legacy); } if(migrate != null) { @@ -775,7 +758,7 @@ public static int execute(final String[] args) throws Exception { System.out.println("ERR: Seems cluster is already migrated"); return -1; } - return migrate(tc, index, new File(migrate), nodesInfo, resolveEnvVars); + return migrate(restHighLevelClient, index, new File(migrate), expectedNodeCount, resolveEnvVars); } boolean isCdAbs = new File(cd).isAbsolute(); @@ -798,437 +781,447 @@ public static int execute(final String[] args) throws Exception { return (-1); } - boolean success = uploadFile(tc, file, index, type, legacy, resolveEnvVars); + boolean success = uploadFile(restHighLevelClient, file, index, type, legacy, resolveEnvVars); if(!success) { System.out.println("ERR: cannot upload configuration, see errors above"); return -1; } - ConfigUpdateResponse cur = tc.execute(ConfigUpdateAction.INSTANCE, new ConfigUpdateRequest(new String[]{type})).actionGet(); - - success = checkConfigUpdateResponse(cur, nodesInfo, 1) && success; - + Response cur = restHighLevelClient.getLowLevelClient().performRequest(new Request("PUT", "/_plugins/_security/configupdate?config_types=" + type)); + success = checkConfigUpdateResponse(cur, expectedNodeCount, 1) && success; + System.out.println("Done with "+(success?"success":"failures")); return (success?0:-1); } - return upload(tc, index, cd, legacy, nodesInfo, resolveEnvVars); - } - // TODO audit changes to .opendistro_security index - } - - private static boolean checkConfigUpdateResponse(ConfigUpdateResponse response, NodesInfoResponse nir, int expectedConfigCount) { - - final int expectedNodeCount = nir.getNodes().size(); - - if(response.hasFailures()) { - System.out.println("FAIL: "+response.failures().size()+" nodes reported failures. First failure is "+response.failures().get(0)); - } - - boolean success = response.getNodes().size() == expectedNodeCount; - if(!success) { - System.out.println("FAIL: Expected "+expectedNodeCount+" nodes to return response, but got "+response.getNodes().size()); - } - - for(String nodeId: response.getNodesMap().keySet()) { - ConfigUpdateNodeResponse node = response.getNodesMap().get(nodeId); - boolean successNode = (node.getUpdatedConfigTypes() != null && node.getUpdatedConfigTypes().length == expectedConfigCount); - if(!successNode) { - System.out.println("FAIL: Expected "+expectedConfigCount+" config types for node "+nodeId+" but got "+node.getUpdatedConfigTypes().length+" ("+Arrays.toString(node.getUpdatedConfigTypes()) + ") due to: "+(node.getMessage()==null?"unknown reason":node.getMessage())); - } - - success = success && successNode; - } - - return success && !response.hasFailures(); - } - - private static boolean uploadFile(final Client tc, final String filepath, final String index, final String _id, final boolean legacy, boolean resolveEnvVars) { - return uploadFile(tc, filepath, index, _id, legacy, resolveEnvVars, false); - } - - private static boolean uploadFile(final Client tc, final String filepath, final String index, final String _id, final boolean legacy, boolean resolveEnvVars, - final boolean populateEmptyIfMissing) { - - String type = "_doc"; - String id = _id; - - if(legacy) { - type = "security"; - id = _id; - - try { - ConfigHelper.fromYamlFile(filepath, CType.fromString(_id), 1, 0, 0); - } catch (Exception e) { - System.out.println("ERR: Seems "+filepath+" is not in legacy format: "+e); - return false; - } - - } else { - try { - ConfigHelper.fromYamlFile(filepath, CType.fromString(_id), 2, 0, 0); - } catch (Exception e) { - System.out.println("ERR: Seems "+filepath+" is not in OpenSearch Security 7 format: "+e); - return false; - } - } - - System.out.println("Will update '"+type+"/" + id + "' with " + filepath+" "+(legacy?"(legacy mode)":"")); - - try (Reader reader = ConfigHelper.createFileOrStringReader(CType.fromString(_id), legacy ? 1 : 2, filepath, populateEmptyIfMissing)) { - final String content = CharStreams.toString(reader); - final String res = tc - .index(new IndexRequest(index).type(type).id(id).setRefreshPolicy(RefreshPolicy.IMMEDIATE) - .source(_id, readXContent(resolveEnvVars? SecurityUtils.replaceEnvVars(content, Settings.EMPTY):content, XContentType.YAML))).actionGet().getId(); - - if (id.equals(res)) { - System.out.println(" SUCC: Configuration for '" + _id + "' created or updated"); - return true; - } else { - System.out.println(" FAIL: Configuration for '" + _id - + "' failed for unknown reasons. Please consult the OpenSearch logfile."); - } - } catch (Exception e) { - System.out.println(" FAIL: Configuration for '" + _id + "' failed because of " + e.toString()); + return upload(restHighLevelClient, index, cd, legacy, expectedNodeCount, resolveEnvVars); } - - return false; } - private static boolean retrieveFile(final Client tc, final String filepath, final String index, final String _id, final boolean legacy) { - return retrieveFile(tc, filepath, index, _id, legacy, false); - } - - private static boolean retrieveFile(final Client tc, final String filepath, final String index, final String _id, final boolean legacy, final boolean populateFileIfEmpty) { - - String type = "_doc"; - String id = _id; - - if(legacy) { - type = "security"; - id = _id; - - } - - System.out.println("Will retrieve '"+type+"/" +id+"' into "+filepath+" "+(legacy?"(legacy mode)":"")); - try (Writer writer = new FileWriter(filepath)) { - - final GetResponse response = tc.get(new GetRequest(index).type(type).id(id).refresh(true).realtime(false)).actionGet(); - - boolean isEmpty = !response.isExists() || response.isSourceEmpty(); - String yaml; - if (isEmpty) { - if (populateFileIfEmpty) { - yaml = ConfigHelper.createEmptySdcYaml(CType.fromString(_id), legacy ? 1 : 2); - } else { - System.out.println(" FAIL: Configuration for '"+_id+"' failed because of empty source"); - return false; - } - } else { - yaml = convertToYaml(_id, response.getSourceAsBytesRef(), true); + private static boolean checkConfigUpdateResponse(Response response, int expectedNodeCount, int expectedConfigCount) throws IOException { - if (null == yaml) { - System.out.println("ERR: YML conversion error for " + _id); - return false; + if (response.getStatusLine().getStatusCode() != 200) { + System.out.println("Unable to check configupdate response because return code was " + response.getStatusLine()); + } - } - - if (legacy) { - try { - ConfigHelper.fromYamlString(yaml, CType.fromString(_id), 1, 0, 0); - } catch (Exception e) { - System.out.println("ERR: Seems " + _id + " from cluster is not in legacy format: " + e); - return false; - } - } else { - try { - ConfigHelper.fromYamlString(yaml, CType.fromString(_id), 2, 0, 0); - } catch (Exception e) { - System.out.println("ERR: Seems " + _id + " from cluster is not in SG 7 format: " + e); - return false; - } - } - } - - writer.write(yaml); - System.out.println(" SUCC: Configuration for '"+_id+"' stored in "+filepath); - return true; - } catch (Exception e) { - System.out.println(" FAIL: Get configuration for '"+_id+"' failed because of "+e.toString()); - } - - return false; - } - - private static BytesReference readXContent(final String content, final XContentType xContentType) throws IOException { - BytesReference retVal; - XContentParser parser = null; - try { - parser = XContentFactory.xContent(xContentType).createParser(NamedXContentRegistry.EMPTY, THROW_UNSUPPORTED_OPERATION, content); - parser.nextToken(); - final XContentBuilder builder = XContentFactory.jsonBuilder(); - builder.copyCurrentStructure(parser); - retVal = BytesReference.bytes(builder); - } finally { - if (parser != null) { - parser.close(); - } - } - - //validate - return retVal; - } - - private static String convertToYaml(String type, BytesReference bytes, boolean prettyPrint) throws IOException { - - try (XContentParser parser = JsonXContent.jsonXContent.createParser(NamedXContentRegistry.EMPTY, THROW_UNSUPPORTED_OPERATION, bytes.streamInput())) { - parser.nextToken(); - parser.nextToken(); - - if(!type.equals((parser.currentName()))) { - return null; - } - - parser.nextToken(); - - XContentBuilder builder = XContentFactory.yamlBuilder(); - if (prettyPrint) { - builder.prettyPrint(); - } - builder.rawValue(new ByteArrayInputStream(parser.binaryValue()), XContentType.YAML); - return Strings.toString(builder); - } - } + JsonNode resNode = DefaultObjectMapper.objectMapper.readTree(response.getEntity().getContent()); - protected static class TransportClientImpl extends TransportClient { + if (resNode.at("/configupdate_response/has_failures").asBoolean()) { + System.out.println("FAIL: " + resNode.at("/configupdate_response/failures_size").asInt() + " nodes reported failures. Failure is " + responseToString(response, false) + "/" + resNode); + } - public TransportClientImpl(Settings settings, Collection> plugins) { - super(settings, plugins); - } - public TransportClientImpl(Settings settings, Settings defaultSettings, Collection> plugins) { - super(settings, defaultSettings, plugins, null); - } - } - - @SafeVarargs - protected static Collection> asCollection(Class... plugins) { - return Arrays.asList(plugins); - } + boolean success = resNode.at("/configupdate_response/node_size").asInt() == expectedNodeCount; + if (!success) { + System.out.println("FAIL: Expected " + expectedNodeCount + " nodes to return response, but got " + resNode.at("/configupdate_response/node_size").asInt()); + } - protected static void generateDiagnoseTrace(final Client tc) { - - final String date = DATE_FORMAT.format(new Date()); - - final StringBuilder sb = new StringBuilder(); - sb.append("Diagnostic securityadmin trace"+System.lineSeparator()); - sb.append("OpenSearch client version: "+Version.CURRENT+System.lineSeparator()); - sb.append("Client properties: "+System.getProperties()+System.lineSeparator()); - sb.append(date+System.lineSeparator()); - sb.append(System.lineSeparator()); - - try { - sb.append("Who am i:"+System.lineSeparator()); - final WhoAmIResponse whoAmIRes = tc.execute(WhoAmIAction.INSTANCE, new WhoAmIRequest()).actionGet(); - sb.append(Strings.toString(whoAmIRes,true, true)); - } catch (Exception e1) { - sb.append(ExceptionsHelper.stackTrace(e1)); - } - - try { - sb.append("ClusterHealthRequest:"+System.lineSeparator()); - ClusterHealthResponse nir = tc.admin().cluster().health(new ClusterHealthRequest()).actionGet(); - sb.append(Strings.toString(nir,true, true)); - } catch (Exception e1) { - sb.append(ExceptionsHelper.stackTrace(e1)); - } - - try { - sb.append(System.lineSeparator()+"NodesInfoResponse:"+System.lineSeparator()); - NodesInfoResponse nir = tc.admin().cluster().nodesInfo(new NodesInfoRequest()).actionGet(); - sb.append(Strings.toString(nir,true, true)); - } catch (Exception e1) { - sb.append(ExceptionsHelper.stackTrace(e1)); - } - - try { - sb.append(System.lineSeparator()+"NodesStatsRequest:"+System.lineSeparator()); - NodesStatsResponse nir = tc.admin().cluster().nodesStats(new NodesStatsRequest()).actionGet(); - sb.append(Strings.toString(nir,true, true)); - } catch (Exception e1) { - sb.append(ExceptionsHelper.stackTrace(e1)); - } - - try { - sb.append(System.lineSeparator()+"PendingClusterTasksRequest:"+System.lineSeparator()); - PendingClusterTasksResponse nir = tc.admin().cluster().pendingClusterTasks(new PendingClusterTasksRequest()).actionGet(); - sb.append(Strings.toString(nir,true, true)); - } catch (Exception e1) { - sb.append(ExceptionsHelper.stackTrace(e1)); - } - - try { - sb.append(System.lineSeparator()+"IndicesStatsRequest:"+System.lineSeparator()); - IndicesStatsResponse nir = tc.admin().indices().stats(new IndicesStatsRequest()).actionGet(); - sb.append(Strings.toString(nir, true, true)); - } catch (Exception e1) { - sb.append(ExceptionsHelper.stackTrace(e1)); - } - - try { - File dfile = new File("securityadmin_diag_trace_"+date+".txt"); - Files.asCharSink(dfile, StandardCharsets.UTF_8).write(sb); - System.out.println("Diagnostic trace written to: "+dfile.getAbsolutePath()); - } catch (Exception e1) { - System.out.println("ERR: cannot write diag trace file due to "+e1); - } - } - - private static void validate(CommandLine line) throws ParseException{ - - if(line.hasOption("ts") && line.hasOption("cacert")) { - System.out.println("WARN: It makes no sense to specify -ts as well as -cacert"); - } - - if(line.hasOption("ks") && line.hasOption("cert")) { - System.out.println("WARN: It makes no sense to specify -ks as well as -cert"); - } - - if(line.hasOption("ks") && line.hasOption("key")) { - System.out.println("WARN: It makes no sense to specify -ks as well as -key"); - } - - if(line.hasOption("cd") && line.hasOption("rl")) { - System.out.println("WARN: It makes no sense to specify -cd as well as -r"); - } - - if(line.hasOption("cd") && line.hasOption("f")) { - System.out.println("WARN: It makes no sense to specify -cd as well as -f"); - } + for (JsonNode n : resNode.at("/configupdate_response/nodes")) { + boolean successNode = (n.get("updated_config_types") != null && n.get("updated_config_size").asInt() == expectedConfigCount); - if(line.hasOption("cn") && line.hasOption("icl")) { - throw new ParseException("Only set one of -cn or -icl"); - } + if (!successNode) { + System.out.println("FAIL: Expected " + expectedConfigCount + " config types for node " + n + " but got " + n.get("updated_config_size").asInt() + " (" + n.get("updated_config_types") + ") due to: " + (n.get("message") == null ? "unknown reason" : n.get("message"))); + } else { + System.out.println("SUCC: Expected " + expectedConfigCount + " config types for node " + n + " is " + n.get("updated_config_size").asInt() + " (" + n.get("updated_config_types") + ") due to: " + (n.get("message") == null ? "unknown reason" : n.get("message"))); - if(line.hasOption("vc") && !line.hasOption("cd") && !line.hasOption("f")) { - throw new ParseException("Specify at least -cd or -f together with vc"); - } + } - if(!line.hasOption("vc") && !line.hasOption("ks") && !line.hasOption("cert") /*&& !line.hasOption("simple-auth")*/) { - throw new ParseException("Specify at least -ks or -cert"); - } - - if(!line.hasOption("vc") && !line.hasOption("mo") - && !line.hasOption("ts") && !line.hasOption("cacert")) { - throw new ParseException("Specify at least -ts or -cacert"); - } - - //TODO add more validation rules - } - - private static String promptForPassword(String passwordName, String commandLineOption, String envVarName) throws Exception { - final Console console = System.console(); - if(console == null) { - throw new Exception("Cannot allocate a console. Set env var "+envVarName+" or "+commandLineOption+" on commandline in that case"); - } - return new String(console.readPassword("[%s]", passwordName+" password:")); - } - - private static int issueWarnings(Client tc) { - NodesInfoResponse nir = tc.admin().cluster().nodesInfo(new NodesInfoRequest()).actionGet(); - Version maxVersion = nir.getNodes().stream().max((n1,n2) -> n1.getVersion().compareTo(n2.getVersion())).get().getVersion(); - Version minVersion = nir.getNodes().stream().min((n1,n2) -> n1.getVersion().compareTo(n2.getVersion())).get().getVersion(); - - if(!maxVersion.equals(minVersion)) { - System.out.println("ERR: Your cluster consists of different node versions. It is not allowed to run securityadmin against a mixed cluster."); - System.out.println(" Minimum node version is "+minVersion.toString()); - System.out.println(" Maximum node version is "+maxVersion.toString()); - if(!ALLOW_MIXED) { - return -1; - } - - } else { - System.out.println("OpenSearch Version: "+minVersion.toString()); - } - - if(nir.getNodes().size() > 0) { - List pluginInfos = nir.getNodes().get(0).getInfo(PluginsAndModules.class).getPluginInfos(); - String securityVersion = pluginInfos.stream().filter(p->p.getClassname().equals("org.opensearch.security.OpenSearchSecurityPlugin")).map(p->p.getVersion()).findFirst().orElse(""); - System.out.println("OpenSearch Security Version: "+securityVersion); - } - - return 0; - } - - private static int deleteConfigIndex(TransportClient tc, String index, boolean indexExists) { - boolean success = true; - - if(indexExists) { - success = tc.admin().indices().delete(new DeleteIndexRequest(index)).actionGet().isAcknowledged(); - System.out.print("Deleted index '"+index+"'"); - } else { - System.out.print("No index '"+index+"' exists, so no need to delete it"); - } - - return (success?0:-1); - } - - private static int createConfigIndex(TransportClient tc, String index, String explicitReplicas) { - Map indexSettings = new HashMap<>(); - indexSettings.put("index.number_of_shards", 1); - - if(explicitReplicas != null) { - if(explicitReplicas.contains("-")) { - indexSettings.put("index.auto_expand_replicas", explicitReplicas); - } else { - indexSettings.put("index.number_of_replicas", Integer.parseInt(explicitReplicas)); - } - } else { - indexSettings.put("index.auto_expand_replicas", "0-all"); + success = success && successNode; + } + + return success && !resNode.at("/configupdate_response/has_failures").asBoolean(); + } + + private static boolean uploadFile(final RestHighLevelClient restHighLevelClient, final String filepath, final String index, final String _id, final boolean legacy, boolean resolveEnvVars) { + return uploadFile(restHighLevelClient, filepath, index, _id, legacy, resolveEnvVars, false); + } + + private static boolean uploadFile(final RestHighLevelClient restHighLevelClient, final String filepath, final String index, final String _id, final boolean legacy, boolean resolveEnvVars, + final boolean populateEmptyIfMissing) { + + String type = "_doc"; + String id = _id; + + if (legacy) { + type = "security"; + id = _id; + + try { + ConfigHelper.fromYamlFile(filepath, CType.fromString(_id), 1, 0, 0); + } catch (Exception e) { + System.out.println("ERR: Seems " + filepath + " is not in legacy format: " + e); + return false; + } + + } else { + try { + ConfigHelper.fromYamlFile(filepath, CType.fromString(_id), 2, 0, 0); + } catch (Exception e) { + System.out.println("ERR: Seems " + filepath + " is not in OpenSearch Security 7 format: " + e); + return false; + } + } + + System.out.println("Will update '" + type + "/" + id + "' with " + filepath + " " + (legacy ? "(legacy mode)" : "")); + + try (Reader reader = ConfigHelper.createFileOrStringReader(CType.fromString(_id), legacy ? 1 : 2, filepath, populateEmptyIfMissing)) { + final String content = CharStreams.toString(reader); + final String res = restHighLevelClient + .index(new IndexRequest(index).type(type).id(id).setRefreshPolicy(RefreshPolicy.IMMEDIATE) + .source(_id, readXContent(resolveEnvVars ? replaceEnvVars(content, Settings.EMPTY) : content, XContentType.YAML)), RequestOptions.DEFAULT).getId(); + + + if (id.equals(res)) { + System.out.println(" SUCC: Configuration for '" + _id + "' created or updated"); + return true; + } else { + System.out.println(" FAIL: Configuration for '" + _id + + "' failed for unknown reasons. Please consult the OpenSearch logfile."); + } + } catch (Exception e) { + System.out.println(" FAIL: Configuration for '" + _id + "' failed because of " + e.toString()); + } + + return false; + } + + private static boolean retrieveFile(final RestHighLevelClient restHighLevelClient, final String filepath, final String index, final String _id, final boolean legacy) { + return retrieveFile(restHighLevelClient, filepath, index, _id, legacy, false); + } + + private static boolean retrieveFile(final RestHighLevelClient restHighLevelClient, final String filepath, final String index, final String _id, final boolean legacy, final boolean populateFileIfEmpty) { + String type = "_doc"; + String id = _id; + + if (legacy) { + type = "security"; + id = _id; + + } + + System.out.println("Will retrieve '" + type + "/" + id + "' into " + filepath + " " + (legacy ? "(legacy mode)" : "")); + try (Writer writer = new FileWriter(filepath)) { + + final GetResponse response = restHighLevelClient.get(new GetRequest(index).type(type).id(id).refresh(true).realtime(false), RequestOptions.DEFAULT); + + boolean isEmpty = !response.isExists() || response.isSourceEmpty(); + String yaml; + if (isEmpty) { + if (populateFileIfEmpty) { + yaml = ConfigHelper.createEmptySdcYaml(CType.fromString(_id), legacy ? 1 : 2); + } else { + System.out.println(" FAIL: Configuration for '" + _id + "' failed because of empty source"); + return false; + } + } else { + yaml = convertToYaml(_id, response.getSourceAsBytesRef(), true); + + if (null == yaml) { + System.out.println("ERR: YML conversion error for " + _id); + return false; + + } + + if (legacy) { + try { + ConfigHelper.fromYamlString(yaml, CType.fromString(_id), 1, 0, 0); + } catch (Exception e) { + System.out.println("ERR: Seems " + _id + " from cluster is not in legacy format: " + e); + return false; + } + } else { + try { + ConfigHelper.fromYamlString(yaml, CType.fromString(_id), 2, 0, 0); + } catch (Exception e) { + System.out.println("ERR: Seems " + _id + " from cluster is not in 7 format: " + e); + return false; + } + } + } + + writer.write(yaml); + System.out.println(" SUCC: Configuration for '" + _id + "' stored in " + filepath); + return true; + } catch (Exception e) { + System.out.println(" FAIL: Get configuration for '" + _id + "' failed because of " + e.toString()); + } + + return false; + } + + private static BytesReference readXContent(final String content, final XContentType xContentType) throws IOException { + BytesReference retVal; + XContentParser parser = null; + try { + parser = XContentFactory.xContent(xContentType).createParser(NamedXContentRegistry.EMPTY, THROW_UNSUPPORTED_OPERATION, content); + parser.nextToken(); + final XContentBuilder builder = XContentFactory.jsonBuilder(); + builder.copyCurrentStructure(parser); + retVal = BytesReference.bytes(builder); + } finally { + if (parser != null) { + parser.close(); + } + } + + //validate + return retVal; + } + + private static String convertToYaml(String type, BytesReference bytes, boolean prettyPrint) throws IOException { + + try (XContentParser parser = JsonXContent.jsonXContent.createParser(NamedXContentRegistry.EMPTY, THROW_UNSUPPORTED_OPERATION, bytes.streamInput())) { + parser.nextToken(); + parser.nextToken(); + + if (!type.equals((parser.currentName()))) { + return null; + } + + parser.nextToken(); + + XContentBuilder builder = XContentFactory.yamlBuilder(); + if (prettyPrint) { + builder.prettyPrint(); + } + builder.rawValue(new ByteArrayInputStream(parser.binaryValue()), XContentType.YAML); + return Strings.toString(builder); + } + } + + protected static void generateDiagnoseTrace(final RestHighLevelClient restHighLevelClient) { + + final String date = DATE_FORMAT.format(new Date()); + + final StringBuilder sb = new StringBuilder(); + sb.append("Diagnostic securityadmin trace" + System.lineSeparator()); + sb.append("OpenSearch client version: " + Version.CURRENT + System.lineSeparator()); + sb.append("Client properties: " + System.getProperties() + System.lineSeparator()); + sb.append(date + System.lineSeparator()); + sb.append(System.lineSeparator()); + + try { + sb.append("Who am i:" + System.lineSeparator()); + final Response whoAmIRes = restHighLevelClient.getLowLevelClient().performRequest(new Request("GET", "/_plugins/_security/whoami")); + sb.append(responseToString(whoAmIRes, true)); + } catch (Exception e1) { + sb.append(ExceptionsHelper.stackTrace(e1)); + } + + try { + sb.append("ClusterHealthRequest:" + System.lineSeparator()); + ClusterHealthResponse nir = restHighLevelClient.cluster().health(new ClusterHealthRequest(), RequestOptions.DEFAULT); + sb.append(Strings.toString(nir, true, true)); + } catch (Exception e1) { + sb.append(ExceptionsHelper.stackTrace(e1)); + } + + try { + sb.append(System.lineSeparator() + "NodesInfoResponse:" + System.lineSeparator()); + Response nir = restHighLevelClient.getLowLevelClient().performRequest(new Request("GET", "/_nodes")); + sb.append(responseToString(nir, true)); + } catch (Exception e1) { + sb.append(ExceptionsHelper.stackTrace(e1)); + } + + try { + sb.append(System.lineSeparator() + "NodesStatsRequest:" + System.lineSeparator()); + Response nir = restHighLevelClient.getLowLevelClient().performRequest(new Request("GET", "/_nodes/stats")); + sb.append(responseToString(nir, true)); + } catch (Exception e1) { + sb.append(ExceptionsHelper.stackTrace(e1)); + } + + try { + sb.append(System.lineSeparator() + "PendingClusterTasksRequest:" + System.lineSeparator()); + Response nir = restHighLevelClient.getLowLevelClient().performRequest(new Request("GET", "/_cluster/pending_tasks")); + sb.append(responseToString(nir, true)); + } catch (Exception e1) { + sb.append(ExceptionsHelper.stackTrace(e1)); + } + + try { + sb.append(System.lineSeparator() + "IndicesStatsRequest:" + System.lineSeparator()); + Response nir = restHighLevelClient.getLowLevelClient().performRequest(new Request("GET", "/_stats")); + sb.append(responseToString(nir, true)); + } catch (Exception e1) { + sb.append(ExceptionsHelper.stackTrace(e1)); + } + + try { + File dfile = new File("securityadmin_diag_trace_" + date + ".txt"); + Files.asCharSink(dfile, StandardCharsets.UTF_8).write(sb); + System.out.println("Diagnostic trace written to: " + dfile.getAbsolutePath()); + } catch (Exception e1) { + System.out.println("ERR: cannot write diag trace file due to " + e1); + } + } + + private static void validate(CommandLine line) throws ParseException { + + if (line.hasOption("ts") && line.hasOption("cacert")) { + System.out.println("WARN: It makes no sense to specify -ts as well as -cacert"); + } + + if (line.hasOption("ks") && line.hasOption("cert")) { + System.out.println("WARN: It makes no sense to specify -ks as well as -cert"); + } + + if (line.hasOption("ks") && line.hasOption("key")) { + System.out.println("WARN: It makes no sense to specify -ks as well as -key"); + } + + if (line.hasOption("cd") && line.hasOption("rl")) { + System.out.println("WARN: It makes no sense to specify -cd as well as -r"); + } + + if (line.hasOption("cd") && line.hasOption("f")) { + System.out.println("WARN: It makes no sense to specify -cd as well as -f"); + } + + if (line.hasOption("cn") && line.hasOption("icl")) { + throw new ParseException("Only set one of -cn or -icl"); + } + + if (line.hasOption("vc") && !line.hasOption("cd") && !line.hasOption("f")) { + throw new ParseException("Specify at least -cd or -f together with vc"); + } + + if (!line.hasOption("vc") && !line.hasOption("ks") && !line.hasOption("cert") /*&& !line.hasOption("simple-auth")*/) { + throw new ParseException("Specify at least -ks or -cert"); + } + + if (!line.hasOption("vc") && !line.hasOption("mo") + && !line.hasOption("ts") && !line.hasOption("cacert")) { + throw new ParseException("Specify at least -ts or -cacert"); + } + + //TODO add more validation rules + } + + private static String promptForPassword(String passwordName, String commandLineOption, String envVarName) throws Exception { + final Console console = System.console(); + if (console == null) { + throw new Exception("Cannot allocate a console. Set env var " + envVarName + " or " + commandLineOption + " on commandline in that case"); + } + return new String(console.readPassword("[%s]", passwordName + " password:")); + } + + private static int issueWarnings(RestHighLevelClient restHighLevelClient) throws IOException { + Response res = restHighLevelClient.getLowLevelClient().performRequest(new Request("GET", "/_nodes")); + + if (res.getStatusLine().getStatusCode() != 200) { + System.out.println("Unable to get nodes " + res.getStatusLine()); + return -1; + } + + JsonNode resNode = DefaultObjectMapper.objectMapper.readTree(res.getEntity().getContent()); + + int nodeCount = Iterators.size(resNode.at("/nodes").iterator()); + + if (nodeCount > 0) { + + JsonNode[] nodeVersions = Iterators.toArray(resNode.at("/nodes").iterator(), JsonNode.class); + + Version maxVersion = Version.fromString(Arrays.stream(nodeVersions).max((n1, n2) -> Version.fromString(n1.asText()).compareTo(Version.fromString(n2.asText()))).get().asText()); + Version minVersion = Version.fromString(Arrays.stream(nodeVersions).min((n1, n2) -> Version.fromString(n1.asText()).compareTo(Version.fromString(n2.asText()))).get().asText()); + + if (!maxVersion.equals(minVersion)) { + System.out.println("ERR: Your cluster consists of different node versions. It is not allowed to run securityadmin against a mixed cluster."); + System.out.println(" Minimum node version is " + minVersion.toString()); + System.out.println(" Maximum node version is " + maxVersion.toString()); + if (!ALLOW_MIXED) { + return -1; + } + + } else { + System.out.println("OpenSearch Version: " + minVersion.toString()); + } + + + for (JsonNode n : nodeVersions[0].get("plugins")) { + if ("org.opensearch.security.OpenSearchSecurityPlugin".equals(n.get("name").asText())) { + System.out.println("OpenSearch Security Version: " + n.get("version")); + break; + } + } + + } else { + System.out.println("ERR: Your cluster consists of zero nodes"); + } + + return 0; + } + + private static int deleteConfigIndex(RestHighLevelClient restHighLevelClient, String index, boolean indexExists) throws IOException { + boolean success = true; + + if (indexExists) { + success = restHighLevelClient.indices().delete(new DeleteIndexRequest(index), RequestOptions.DEFAULT).isAcknowledged(); + System.out.print("Deleted index '" + index + "'"); + } else { + System.out.print("No index '" + index + "' exists, so no need to delete it"); + } + + return (success ? 0 : -1); + } + + private static int createConfigIndex(RestHighLevelClient restHighLevelClient, String index, String explicitReplicas) throws IOException { + Map indexSettings = new HashMap<>(); + indexSettings.put("index.number_of_shards", 1); + + if (explicitReplicas != null) { + if (explicitReplicas.contains("-")) { + indexSettings.put("index.auto_expand_replicas", explicitReplicas); + } else { + indexSettings.put("index.number_of_replicas", Integer.parseInt(explicitReplicas)); + } + } else { + indexSettings.put("index.auto_expand_replicas", "0-all"); + } + + final boolean indexCreated = restHighLevelClient.indices().create(new CreateIndexRequest(index) + .settings(indexSettings), RequestOptions.DEFAULT) + .isAcknowledged(); + + if (indexCreated) { + System.out.println("done (" + (explicitReplicas != null ? explicitReplicas : "0-all") + " replicas)"); + return 0; + } else { + System.out.println("failed!"); + System.out.println("FAIL: Unable to create the " + index + " index. See opensearch logs for more details"); + return (-1); + } + } + + private static int backup(RestHighLevelClient tc, String index, File backupDir, boolean legacy) { + backupDir.mkdirs(); + + boolean success = retrieveFile(tc, backupDir.getAbsolutePath() + "/config.yml", index, "config", legacy); + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/roles.yml", index, "roles", legacy) && success; + + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/roles_mapping.yml", index, "rolesmapping", legacy) && success; + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/internal_users.yml", index, "internalusers", legacy) && success; + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/action_groups.yml", index, "actiongroups", legacy) && success; + + if (!legacy) { + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/tenants.yml", index, "tenants", legacy) && success; } + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/nodes_dn.yml", index, "nodesdn", legacy, true) && success; + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/whitelist.yml", index, "whitelist", legacy, true) && success; + success = retrieveFile(tc, backupDir.getAbsolutePath() + "/audit.yml", index, "audit", legacy) && success; - final boolean indexCreated = tc.admin().indices().create(new CreateIndexRequest(index) - .settings(indexSettings)) - .actionGet().isAcknowledged(); + return success ? 0 : -1; + } - if (indexCreated) { - System.out.println("done ("+(explicitReplicas!=null?explicitReplicas:"0-all")+" replicas)"); - return 0; - } else { - System.out.println("failed!"); - System.out.println("FAIL: Unable to create the "+index+" index. See opensearch logs for more details"); - return (-1); - } - } - - private static int backup(TransportClient tc, String index, File backupDir, boolean legacy) { - backupDir.mkdirs(); - - boolean success = retrieveFile(tc, backupDir.getAbsolutePath()+"/config.yml", index, "config", legacy); - success = retrieveFile(tc, backupDir.getAbsolutePath()+"/roles.yml", index, "roles", legacy) && success; - - success = retrieveFile(tc, backupDir.getAbsolutePath()+"/roles_mapping.yml", index, "rolesmapping", legacy) && success; - success = retrieveFile(tc, backupDir.getAbsolutePath()+"/internal_users.yml", index, "internalusers", legacy) && success; - success = retrieveFile(tc, backupDir.getAbsolutePath()+"/action_groups.yml", index, "actiongroups", legacy) && success; - - if(!legacy) { - success = retrieveFile(tc, backupDir.getAbsolutePath()+"/tenants.yml", index, "tenants", legacy) && success; - } - success = retrieveFile(tc, backupDir.getAbsolutePath()+"/nodes_dn.yml", index, "nodesdn", legacy, true) && success; - success = retrieveFile(tc, backupDir.getAbsolutePath()+"/whitelist.yml", index, "whitelist", legacy, true) && success; - success = retrieveFile(tc, backupDir.getAbsolutePath() + "/audit.yml", index, "audit", legacy) && success; + private static int upload(RestHighLevelClient tc, String index, String cd, boolean legacy, int expectedNodeCount, boolean resolveEnvVars) throws IOException { + boolean success = uploadFile(tc, cd + "config.yml", index, "config", legacy, resolveEnvVars); + success = uploadFile(tc, cd + "roles.yml", index, "roles", legacy, resolveEnvVars) && success; + success = uploadFile(tc, cd + "roles_mapping.yml", index, "rolesmapping", legacy, resolveEnvVars) && success; - return success?0:-1; - } - - private static int upload(TransportClient tc, String index, String cd, boolean legacy, NodesInfoResponse nodesInfo, boolean resolveEnvVars) { - boolean success = uploadFile(tc, cd+"config.yml", index, "config", legacy, resolveEnvVars); - success = uploadFile(tc, cd+"roles.yml", index, "roles", legacy, resolveEnvVars) && success; - success = uploadFile(tc, cd+"roles_mapping.yml", index, "rolesmapping", legacy, resolveEnvVars) && success; - - success = uploadFile(tc, cd+"internal_users.yml", index, "internalusers", legacy, resolveEnvVars) && success; - success = uploadFile(tc, cd+"action_groups.yml", index, "actiongroups", legacy, resolveEnvVars) && success; + success = uploadFile(tc, cd + "internal_users.yml", index, "internalusers", legacy, resolveEnvVars) && success; + success = uploadFile(tc, cd + "action_groups.yml", index, "actiongroups", legacy, resolveEnvVars) && success; if(!legacy) { @@ -1241,35 +1234,34 @@ private static int upload(TransportClient tc, String index, String cd, boolean l success = uploadFile(tc, cd + "audit.yml", index, "audit", legacy, resolveEnvVars) && success; } - if(!success) { - System.out.println("ERR: cannot upload configuration, see errors above"); - return -1; - } - - ConfigUpdateResponse cur = tc.execute(ConfigUpdateAction.INSTANCE, new ConfigUpdateRequest(getTypes(legacy))).actionGet(); + if (!success) { + System.out.println("ERR: cannot upload configuration, see errors above"); + return -1; + } - success = checkConfigUpdateResponse(cur, nodesInfo, getTypes(legacy).length) && success; + Response cur = tc.getLowLevelClient().performRequest(new Request("PUT", "/_plugins/_security/configupdate?config_types=" + Joiner.on(",").join(getTypes((legacy))))); + success = checkConfigUpdateResponse(cur, expectedNodeCount, getTypes(legacy).length) && success; + + System.out.println("Done with " + (success ? "success" : "failures")); + return (success ? 0 : -1); + } + + private static int migrate(RestHighLevelClient tc, String index, File backupDir, int expectedNodeCount, boolean resolveEnvVars) throws IOException { - System.out.println("Done with "+(success?"success":"failures")); - return (success?0:-1); - } - - private static int migrate(TransportClient tc, String index, File backupDir, NodesInfoResponse nodesInfo, boolean resolveEnvVars) { - System.out.println("== Migration started =="); System.out.println("======================="); - - System.out.println("-> Backup current configuration to "+backupDir.getAbsolutePath()); - - if(backup(tc, index, backupDir, true) != 0) { + + System.out.println("-> Backup current configuration to " + backupDir.getAbsolutePath()); + + if (backup(tc, index, backupDir, true) != 0) { return -1; } System.out.println(" done"); - - File v7Dir = new File(backupDir,"v7"); + + File v7Dir = new File(backupDir, "v7"); v7Dir.mkdirs(); - + try { System.out.println("-> Migrate configuration to new format and store it here: "+v7Dir.getAbsolutePath()); @@ -1306,18 +1298,18 @@ private static int migrate(TransportClient tc, String index, File backupDir, Nod } System.out.println(" done"); - - System.out.println("-> Delete old "+index+" index"); + + System.out.println("-> Delete old " + index + " index"); deleteConfigIndex(tc, index, true); System.out.println(" done"); - + System.out.println("-> Upload new configuration into OpenSearch cluster"); - int uploadResult = upload(tc, index, v7Dir.getAbsolutePath()+"/", false, nodesInfo, resolveEnvVars); + int uploadResult = upload(tc, index, v7Dir.getAbsolutePath() + "/", false, expectedNodeCount, resolveEnvVars); - if(uploadResult == 0) { + if (uploadResult == 0) { System.out.println(" done"); - }else { + } else { System.out.println(" ERR: unable to upload"); } @@ -1333,61 +1325,220 @@ private static String readTypeFromFile(File file) throws IOException { return new SecurityJsonNode(jsonNode).get("_meta").get("type").asString(); } - private static int validateConfig(String cd, String file, String type, int version) { - if (file != null) { - try { - - if(type == null) { - type = readTypeFromFile(new File(file)); - } - - if(type == null) { - System.out.println("ERR: Unable to read type from "+file); - return -1; - } - - ConfigHelper.fromYamlFile(file, CType.fromString(type), version==7?2:1, 0, 0); - return 0; - } catch (Exception e) { - System.out.println("ERR: Seems "+file+" is not in SG "+version+" format: "+e); - return -1; - } - } else if(cd != null) { - boolean success = validateConfigFile(cd+"action_groups.yml", CType.ACTIONGROUPS, version); - success = validateConfigFile(cd+"internal_users.yml", CType.INTERNALUSERS, version) && success; - success = validateConfigFile(cd+"roles.yml", CType.ROLES, version) && success; - success = validateConfigFile(cd+"roles_mapping.yml", CType.ROLESMAPPING, version) && success; - success = validateConfigFile(cd+"config.yml", CType.CONFIG, version) && success; - - if(new File(cd+"tenants.yml").exists() && version != 6) { - success = validateConfigFile(cd+"tenants.yml", CType.TENANTS, version) && success; - } - if (new File(cd+"audit.yml").exists()) { - success = validateConfigFile(cd+"audit.yml", CType.AUDIT, version) && success; - } - - return success?0:-1; - - } - - return -1; - } - - private static boolean validateConfigFile(String file, CType cType, int version) { - try { - ConfigHelper.fromYamlFile(file, cType, version==7?2:1, 0, 0); - System.out.println(file+" OK" ); - return true; - } catch (Exception e) { - System.out.println("ERR: Seems "+file+" is not in SG "+version+" format: "+e); - return false; - } - } - - private static String[] getTypes(boolean legacy) { - if(legacy) { - return new String[]{"config","roles","rolesmapping","internalusers","actiongroups","nodesdn", "audit"}; - } - return CType.lcStringValues().toArray(new String[0]); - } + private static int validateConfig(String cd, String file, String type, int version) { + if (file != null) { + try { + + if (type == null) { + type = readTypeFromFile(new File(file)); + } + + if (type == null) { + System.out.println("ERR: Unable to read type from " + file); + return -1; + } + + ConfigHelper.fromYamlFile(file, CType.fromString(type), version == 7 ? 2 : 1, 0, 0); + return 0; + } catch (Exception e) { + System.out.println("ERR: Seems " + file + " is not in " + version + " format: " + e); + return -1; + } + } else if (cd != null) { + boolean success = validateConfigFile(cd + "action_groups.yml", CType.ACTIONGROUPS, version); + success = validateConfigFile(cd + "internal_users.yml", CType.INTERNALUSERS, version) && success; + success = validateConfigFile(cd + "roles.yml", CType.ROLES, version) && success; + success = validateConfigFile(cd + "roles_mapping.yml", CType.ROLESMAPPING, version) && success; + success = validateConfigFile(cd + "config.yml", CType.CONFIG, version) && success; + + if (new File(cd + "tenants.yml").exists() && version != 6) { + success = validateConfigFile(cd + "tenants.yml", CType.TENANTS, version) && success; + } + if (new File(cd + "audit.yml").exists()) { + success = validateConfigFile(cd + "audit.yml", CType.AUDIT, version) && success; + } + + return success ? 0 : -1; + + } + + return -1; + } + + private static boolean validateConfigFile(String file, CType cType, int version) { + try { + ConfigHelper.fromYamlFile(file, cType, version == 7 ? 2 : 1, 0, 0); + System.out.println(file + " OK"); + return true; + } catch (Exception e) { + System.out.println("ERR: Seems " + file + " is not in " + version + " format: " + e); + return false; + } + } + + private static String[] getTypes(boolean legacy) { + if (legacy) { + return new String[]{"config", "roles", "rolesmapping", "internalusers", "actiongroups", "nodesdn", "audit"}; + } + return CType.lcStringValues().toArray(new String[0]); + } + + + private static RestHighLevelClient getRestHighLevelClient(SSLContext sslContext, + boolean nhnv, + String[] enabledProtocols, + String[] enabledCiphers, + String hostname, + int port) { + + final HostnameVerifier hnv = !nhnv ? new DefaultHostnameVerifier() : NoopHostnameVerifier.INSTANCE; + + String[] supportedProtocols = enabledProtocols.length > 0 ? enabledProtocols : null; + String[] supportedCipherSuites = enabledCiphers.length > 0 ? enabledCiphers : null; + + HttpHost httpHost = new HttpHost(hostname, port, "https"); + + RestClientBuilder restClientBuilder = RestClient.builder(httpHost) + .setHttpClientConfigCallback( + builder -> builder.setSSLStrategy( + new SSLIOSessionStrategy( + sslContext, + supportedProtocols, + supportedCipherSuites, + hnv + ) + ) + ); + return new RestHighLevelClient(restClientBuilder); + } + + + private static SSLContext sslContext( + String ks, + String kspass, + String ts, + String tspass, + + String cacert, + String cert, + String key, + String keypass, + String ksAlias) throws Exception { + + KeyStore trustStore = null; + KeyStore keyStore = null; + + if (ks != null) { + File keyStoreFile = Paths.get(ks).toFile(); + String keyStoreFileName = keyStoreFile.getName(); + + String type = getType(keyStoreFileName); + + keyStore = KeyStore.getInstance(type.toUpperCase()); + keyStore.load(new FileInputStream(keyStoreFile), kspass == null ? null : kspass.toCharArray()); + } + + if (ts != null) { + File trustStoreFile = Paths.get(ts).toFile(); + String trustStoreFileName = trustStoreFile.getName(); + + String type = getType(trustStoreFileName); + + trustStore = KeyStore.getInstance(type.toUpperCase()); + trustStore.load(new FileInputStream(trustStoreFile), tspass == null ? null : tspass.toCharArray()); + } + + if (cacert != null) { + File caCertFile = Paths.get(cacert).toFile(); + try (FileInputStream in = new FileInputStream(caCertFile)) { + X509Certificate[] certificates = PemKeyReader.loadCertificatesFromStream(in); + trustStore = PemKeyReader.toTruststore("al", certificates); + } catch (FileNotFoundException e) { + throw new IllegalArgumentException("Could not find certificate file " + caCertFile, e); + } catch (IOException | CertificateException e) { + throw new IllegalArgumentException("Error while reading certificate file " + caCertFile, e); + } + } + + if (cert != null && key != null) { + File certFile = Paths.get(cert).toFile(); + X509Certificate[] certificates; + PrivateKey privateKey; + try (FileInputStream in = new FileInputStream(certFile)) { + certificates = PemKeyReader.loadCertificatesFromStream(in); + } catch (FileNotFoundException e) { + throw new IllegalArgumentException("Could not find certificate file " + certFile, e); + } catch (IOException | CertificateException e) { + throw new IllegalArgumentException("Error while reading certificate file " + certFile, e); + } + + File keyFile = Paths.get(key).toFile(); + try (FileInputStream in = new FileInputStream(keyFile)) { + privateKey = PemKeyReader.toPrivateKey(in, keypass); + } catch (FileNotFoundException e) { + throw new IllegalArgumentException("Could not find certificate key file " + keyFile, e); + } catch (IOException e) { + throw new IllegalArgumentException("Error while reading certificate key file " + keyFile, e); + } + + keyStore = PemKeyReader.toKeystore(ksAlias, keypass == null ? null : keypass.toCharArray(), certificates, privateKey); + } + + //todo just temporary! + String pass = cert != null ? keypass : kspass; + + final SSLContextBuilder sslContextBuilder = SSLContexts.custom(); + + + if (trustStore != null) { + sslContextBuilder.loadTrustMaterial(trustStore, null); + } + + if (keyStore != null) { + sslContextBuilder.loadKeyMaterial(keyStore, pass.toCharArray(), (aliases, socket) -> { + if (aliases == null || aliases.isEmpty()) { + return ksAlias; + } + + if (ksAlias == null || ksAlias.isEmpty()) { + return aliases.keySet().iterator().next(); + } + + return ksAlias; + }); + } + return sslContextBuilder.build(); + } + + private static String getType(String trustStoreFileName) { + if (trustStoreFileName.endsWith(".jks")) { + return "JKS"; + } else if (trustStoreFileName.endsWith(".pfx") || trustStoreFileName.endsWith(".p12")) { + return "PKCS12"; + } else { + throw new IllegalArgumentException("Unknwon file type: " + trustStoreFileName); + } + } + + private static String responseToString(Response response, boolean prettyJson) { + ByteSource byteSource = new ByteSource() { + @Override + public InputStream openStream() throws IOException { + return response.getEntity().getContent(); + } + }; + + try { + String value = byteSource.asCharSource(Charsets.UTF_8).read(); + + if (prettyJson) { + return DefaultObjectMapper.objectMapper.readTree(value).toPrettyString(); + } + + return value; + } catch (Exception e) { + e.printStackTrace(); + return "ERR: Unable to handle response due to " + e; + } + } } diff --git a/src/test/java/org/opensearch/security/InitializationIntegrationTests.java b/src/test/java/org/opensearch/security/InitializationIntegrationTests.java index 82b5f73238..b99c5ca6b1 100644 --- a/src/test/java/org/opensearch/security/InitializationIntegrationTests.java +++ b/src/test/java/org/opensearch/security/InitializationIntegrationTests.java @@ -46,7 +46,6 @@ import org.opensearch.common.transport.TransportAddress; import org.junit.Assert; import org.junit.Test; - import org.opensearch.security.test.helper.rest.RestHelper.HttpResponse; import org.opensearch.security.action.configupdate.ConfigUpdateAction; import org.opensearch.security.action.configupdate.ConfigUpdateRequest; diff --git a/src/test/java/org/opensearch/security/SecurityAdminIEndpointsTests.java b/src/test/java/org/opensearch/security/SecurityAdminIEndpointsTests.java new file mode 100644 index 0000000000..1ac171d4d1 --- /dev/null +++ b/src/test/java/org/opensearch/security/SecurityAdminIEndpointsTests.java @@ -0,0 +1,109 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.security; + +import org.apache.http.HttpStatus; +import org.junit.Assert; +import org.junit.Test; +import org.opensearch.common.settings.Settings; +import org.opensearch.security.test.SingleClusterTest; +import org.opensearch.security.test.helper.file.FileHelper; +import org.opensearch.security.test.helper.rest.RestHelper; + +public class SecurityAdminIEndpointsTests extends SingleClusterTest { + + @Test public void testNoSSL() throws Exception { + final Settings settings = Settings.builder().put("plugins.security.ssl.http.enabled", false).build(); + setup(settings); + final RestHelper rh = nonSslRestHelper(); + + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, + rh.executePutRequest("_plugins/_security/configupdate?config_types=roles", "{}", encodeBasicHeader("nagilum", "nagilum")) + .getStatusCode()); + Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, rh.executePutRequest("_plugins/_security/configupdate", "").getStatusCode()); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, + rh.executePutRequest("_plugins/_security/configupdate?config_types=xxx", "", encodeBasicHeader("nagilum", "nagilum")) + .getStatusCode()); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, rh.executeGetRequest("_plugins/_security/whoami").getStatusCode()); + } + + @Test public void testEndpoints() throws Exception { + final Settings settings = Settings.builder().put("plugins.security.ssl.http.enabled", true) + .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) + .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) + .putList("plugins.security.nodes_dn", "CN=node-*.example.com,OU=SSL,O=Test,L=Test,C=DE").build(); + setup(settings); + final RestHelper rh = restHelper(); + rh.enableHTTPClientSSL = true; + rh.trustHTTPServerCertificate = true; + rh.sendAdminCertificate = false; + + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, + rh.executePutRequest("_plugins/_security/configupdate?config_types=roles", "{}", encodeBasicHeader("nagilum", "nagilum")) + .getStatusCode()); + Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, rh.executePutRequest("_plugins/_security/configupdate", "").getStatusCode()); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, + rh.executePutRequest("_plugins/_security/configupdate?config_types=xxx", "", encodeBasicHeader("nagilum", "nagilum")) + .getStatusCode()); + + RestHelper.HttpResponse res; + Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("_plugins/_security/whoami")).getStatusCode()); + + assertContains(res, "*\"dn\":null*"); + + rh.sendAdminCertificate = true; + + Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("_plugins/_security/whoami")).getStatusCode()); + + assertContains(res, "*\"dn\":\"CN=node-0.example.com*"); + assertContains(res, "*\"is_admin\":false*"); + assertContains(res, "*\"is_node_certificate_request\":true*"); + + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, + rh.executePutRequest("_plugins/_security/configupdate?config_types=roles", "{}", encodeBasicHeader("nagilum", "nagilum")) + .getStatusCode()); + Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, rh.executePutRequest("_plugins/_security/configupdate", "").getStatusCode()); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, + rh.executePutRequest("_plugins/_security/configupdate?config_types=xxx", "", encodeBasicHeader("nagilum", "nagilum")) + .getStatusCode()); + + rh.keystore = "spock-keystore.jks"; + + Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("_plugins/_security/whoami")).getStatusCode()); + + assertContains(res, "*\"dn\":\"CN=spock*"); + assertContains(res, "*\"is_admin\":false*"); + assertContains(res, "*\"is_node_certificate_request\":false*"); + + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, + rh.executePutRequest("_plugins/_security/configupdate?config_types=roles", "{}", encodeBasicHeader("nagilum", "nagilum")) + .getStatusCode()); + Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, rh.executePutRequest("_plugins/_security/configupdate", "").getStatusCode()); + Assert.assertEquals(HttpStatus.SC_FORBIDDEN, + rh.executePutRequest("_plugins/_security/configupdate?config_types=xxx", "", encodeBasicHeader("nagilum", "nagilum")) + .getStatusCode()); + + rh.keystore = "kirk-keystore.jks"; + + Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("_plugins/_security/whoami")).getStatusCode()); + + assertContains(res, "*\"dn\":\"CN=kirk*"); + assertContains(res, "*\"is_admin\":true*"); + assertContains(res, "*\"is_node_certificate_request\":false*"); + + Assert.assertEquals(HttpStatus.SC_OK, + rh.executePutRequest("_plugins/_security/configupdate?config_types=roles", "{}", encodeBasicHeader("nagilum", "nagilum")) + .getStatusCode()); + Assert.assertEquals(HttpStatus.SC_BAD_REQUEST, rh.executePutRequest("_plugins/_security/configupdate", "").getStatusCode()); + Assert.assertEquals(HttpStatus.SC_OK, rh.executePutRequest("_plugins/_security/configupdate?config_types=roles", "").getStatusCode()); + + Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executePutRequest("_plugins/_security/configupdate?config_types=unknown_xxx", "", + encodeBasicHeader("nagilum", "nagilum"))).getStatusCode()); + assertContains(res, "*\"successful\":0*failed_node_exception*"); + + } + +} \ No newline at end of file diff --git a/src/test/java/org/opensearch/security/SecurityAdminInvalidConfigsTests.java b/src/test/java/org/opensearch/security/SecurityAdminInvalidConfigsTests.java index bdbad4fdb5..f8a6834c6f 100644 --- a/src/test/java/org/opensearch/security/SecurityAdminInvalidConfigsTests.java +++ b/src/test/java/org/opensearch/security/SecurityAdminInvalidConfigsTests.java @@ -34,6 +34,7 @@ import java.util.ArrayList; import java.util.List; +import org.opensearch.common.settings.Settings; import org.opensearch.security.tools.SecurityAdmin; import org.apache.http.HttpStatus; import org.junit.Assert; @@ -47,7 +48,12 @@ public class SecurityAdminInvalidConfigsTests extends SingleClusterTest { @Test public void testSecurityAdminDuplicateKey() throws Exception { - setup(); + final Settings settings = Settings.builder() + .put("plugins.security.ssl.http.enabled",true) + .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) + .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) + .build(); + setup(settings); final String prefix = getResourceFolder()==null?"":getResourceFolder()+"/"; @@ -57,7 +63,7 @@ public void testSecurityAdminDuplicateKey() throws Exception { argsAsList.add("-ks"); argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"kirk-keystore.jks").toFile().getAbsolutePath()); argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.nodePort)); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); argsAsList.add("-cn"); argsAsList.add(clusterInfo.clustername); argsAsList.add("-cd"); @@ -68,7 +74,7 @@ public void testSecurityAdminDuplicateKey() throws Exception { int returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); Assert.assertNotEquals(0, returnCode); - RestHelper rh = nonSslRestHelper(); + RestHelper rh = restHelper(); Assert.assertEquals(HttpStatus.SC_OK, (rh.executeGetRequest("_opendistro/_security/health?pretty")).getStatusCode()); Assert.assertEquals(HttpStatus.SC_OK, rh.executeGetRequest("_opendistro/_security/authinfo?pretty", encodeBasicHeader("nagilum", "nagilum")).getStatusCode()); @@ -87,7 +93,7 @@ public void testSecurityAdminDuplicateKeyReload() throws Exception { argsAsList.add("-ks"); argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"kirk-keystore.jks").toFile().getAbsolutePath()); argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.nodePort)); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); argsAsList.add("-cn"); argsAsList.add(clusterInfo.clustername); argsAsList.add("-rl"); @@ -97,7 +103,7 @@ public void testSecurityAdminDuplicateKeyReload() throws Exception { int returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); Assert.assertEquals(0, returnCode); - RestHelper rh = nonSslRestHelper(); + RestHelper rh = restHelper(); Assert.assertEquals(HttpStatus.SC_OK, (rh.executeGetRequest("_opendistro/_security/health?pretty")).getStatusCode()); Assert.assertEquals(HttpStatus.SC_OK, rh.executeGetRequest("_opendistro/_security/authinfo?pretty", encodeBasicHeader("nagilum", "nagilum")).getStatusCode()); @@ -106,7 +112,12 @@ public void testSecurityAdminDuplicateKeyReload() throws Exception { @Test public void testSecurityAdminDuplicateKeySingleFile() throws Exception { - setup(); + final Settings settings = Settings.builder() + .put("plugins.security.ssl.http.enabled",true) + .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) + .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) + .build(); + setup(settings); final String prefix = getResourceFolder()==null?"":getResourceFolder()+"/"; @@ -116,7 +127,7 @@ public void testSecurityAdminDuplicateKeySingleFile() throws Exception { argsAsList.add("-ks"); argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"kirk-keystore.jks").toFile().getAbsolutePath()); argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.nodePort)); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); argsAsList.add("-cn"); argsAsList.add(clusterInfo.clustername); argsAsList.add("-f"); @@ -129,7 +140,7 @@ public void testSecurityAdminDuplicateKeySingleFile() throws Exception { int returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); Assert.assertNotEquals(0, returnCode); - RestHelper rh = nonSslRestHelper(); + RestHelper rh = restHelper(); Assert.assertEquals(HttpStatus.SC_OK, (rh.executeGetRequest("_opendistro/_security/health?pretty")).getStatusCode()); Assert.assertEquals(HttpStatus.SC_OK, rh.executeGetRequest("_opendistro/_security/authinfo?pretty", encodeBasicHeader("nagilum", "nagilum")).getStatusCode()); @@ -148,7 +159,7 @@ public void testSecurityAdminDuplicateKeyReloadSingleFile() throws Exception { argsAsList.add("-ks"); argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"kirk-keystore.jks").toFile().getAbsolutePath()); argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.nodePort)); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); argsAsList.add("-cn"); argsAsList.add(clusterInfo.clustername); argsAsList.add("-rl"); @@ -158,7 +169,7 @@ public void testSecurityAdminDuplicateKeyReloadSingleFile() throws Exception { int returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); Assert.assertEquals(0, returnCode); - RestHelper rh = nonSslRestHelper(); + RestHelper rh = restHelper(); Assert.assertEquals(HttpStatus.SC_OK, (rh.executeGetRequest("_opendistro/_security/health?pretty")).getStatusCode()); Assert.assertEquals(HttpStatus.SC_OK, rh.executeGetRequest("_opendistro/_security/authinfo?pretty", encodeBasicHeader("nagilum", "nagilum")).getStatusCode()); diff --git a/src/test/java/org/opensearch/security/SecurityAdminMigrationTests.java b/src/test/java/org/opensearch/security/SecurityAdminMigrationTests.java index e7865ad122..c0a3ad00d8 100644 --- a/src/test/java/org/opensearch/security/SecurityAdminMigrationTests.java +++ b/src/test/java/org/opensearch/security/SecurityAdminMigrationTests.java @@ -60,7 +60,7 @@ public void testSecurityMigrate() throws Exception { argsAsList.add("-ks"); argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"kirk-keystore.jks").toFile().getAbsolutePath()); argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.nodePort)); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); argsAsList.add("-cn"); argsAsList.add(clusterInfo.clustername); argsAsList.add("-migrate"); @@ -107,7 +107,7 @@ public void testSecurityMigrate2() throws Exception { argsAsList.add("-ks"); argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"kirk-keystore.jks").toFile().getAbsolutePath()); argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.nodePort)); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); argsAsList.add("-cn"); argsAsList.add(clusterInfo.clustername); argsAsList.add("-cd"); diff --git a/src/test/java/org/opensearch/security/SecurityAdminTests.java b/src/test/java/org/opensearch/security/SecurityAdminTests.java index 062c927771..42f02842b5 100644 --- a/src/test/java/org/opensearch/security/SecurityAdminTests.java +++ b/src/test/java/org/opensearch/security/SecurityAdminTests.java @@ -38,7 +38,12 @@ public class SecurityAdminTests extends SingleClusterTest { @Test public void testSecurityAdmin() throws Exception { - setup(Settings.EMPTY, null, Settings.EMPTY, false); + final Settings settings = Settings.builder() + .put("plugins.security.ssl.http.enabled",true) + .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) + .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) + .build(); + setup(Settings.EMPTY, null, settings, false); final String prefix = getResourceFolder()==null?"":getResourceFolder()+"/"; @@ -48,7 +53,7 @@ public void testSecurityAdmin() throws Exception { argsAsList.add("-ks"); argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"kirk-keystore.jks").toFile().getAbsolutePath()); argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.nodePort)); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); argsAsList.add("-cn"); argsAsList.add(clusterInfo.clustername); argsAsList.add("-cd"); @@ -59,15 +64,89 @@ public void testSecurityAdmin() throws Exception { int returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); Assert.assertEquals(0, returnCode); - RestHelper rh = nonSslRestHelper(); - HttpResponse res; - - Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("_opendistro/_security/health?pretty")).getStatusCode()); + RestHelper rh = restHelper(); + + Assert.assertEquals(HttpStatus.SC_OK, (rh.executeGetRequest("_opendistro/_security/health?pretty")).getStatusCode()); } - + + @Test + public void testSgAdminInvalidCert() throws Exception { + final Settings settings = Settings.builder() + .put("plugins.security.ssl.http.enabled",true) + .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) + .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) + .build(); + setup(Settings.EMPTY, null, settings, false); + + final String prefix = getResourceFolder()==null?"":getResourceFolder()+"/"; + + List argsAsList = new ArrayList<>(); + argsAsList.add("-ts"); + argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"truststore.jks").toFile().getAbsolutePath()); + argsAsList.add("-ks"); + argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"kirk-keystore.jks").toFile().getAbsolutePath()); + argsAsList.add("-p"); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); + argsAsList.add("-cn"); + argsAsList.add(clusterInfo.clustername); + argsAsList.add("-cd"); + argsAsList.add(new File("src/test/resources/").getAbsolutePath()); + argsAsList.add("-nhnv"); + + int returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); + Assert.assertEquals(0, returnCode); + + RestHelper rh = restHelper(); + + Assert.assertEquals(HttpStatus.SC_OK, (rh.executeGetRequest("_plugins/_security/health?pretty")).getStatusCode()); + + argsAsList = new ArrayList<>(); + argsAsList.add("-ts"); + argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"truststore.jks").toFile().getAbsolutePath()); + argsAsList.add("-ks"); + argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"spock-keystore.jks").toFile().getAbsolutePath()); + argsAsList.add("-p"); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); + argsAsList.add("-cn"); + argsAsList.add(clusterInfo.clustername); + argsAsList.add("-cd"); + argsAsList.add(new File("src/test/resources/").getAbsolutePath()); + argsAsList.add("--diagnose"); + argsAsList.add("-nhnv"); + + + returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); + Assert.assertEquals(-1, returnCode); + + Assert.assertEquals(HttpStatus.SC_OK, (rh.executeGetRequest("_plugins/_security/health?pretty")).getStatusCode()); + + argsAsList = new ArrayList<>(); + argsAsList.add("-ts"); + argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"truststore.jks").toFile().getAbsolutePath()); + argsAsList.add("-ks"); + argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"node-0-keystore.jks").toFile().getAbsolutePath()); + argsAsList.add("-p"); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); + argsAsList.add("-cn"); + argsAsList.add(clusterInfo.clustername); + argsAsList.add("-cd"); + argsAsList.add(new File("src/test/resources/").getAbsolutePath()); + argsAsList.add("-nhnv"); + + returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); + Assert.assertEquals(-1, returnCode); + + Assert.assertEquals(HttpStatus.SC_OK, (rh.executeGetRequest("_plugins/_security/health?pretty")).getStatusCode()); + } + @Test public void testSecurityAdminV6Update() throws Exception { - setup(Settings.EMPTY, null, Settings.EMPTY, false); + final Settings settings = Settings.builder() + .put("plugins.security.ssl.http.enabled",true) + .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) + .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) + .build(); + setup(Settings.EMPTY, null, settings, false); final String prefix = getResourceFolder()==null?"":getResourceFolder()+"/"; @@ -77,7 +156,7 @@ public void testSecurityAdminV6Update() throws Exception { argsAsList.add("-ks"); argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"kirk-keystore.jks").toFile().getAbsolutePath()); argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.nodePort)); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); argsAsList.add("-cn"); argsAsList.add(clusterInfo.clustername); argsAsList.add("-cd"); @@ -88,10 +167,9 @@ public void testSecurityAdminV6Update() throws Exception { int returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); Assert.assertNotEquals(0, returnCode); - RestHelper rh = nonSslRestHelper(); - HttpResponse res; - - Assert.assertEquals(HttpStatus.SC_SERVICE_UNAVAILABLE, (res = rh.executeGetRequest("_opendistro/_security/health?pretty")).getStatusCode()); + RestHelper rh = restHelper(); + + Assert.assertEquals(HttpStatus.SC_SERVICE_UNAVAILABLE, rh.executeGetRequest("_opendistro/_security/health?pretty").getStatusCode()); //System.out.println(res.getBody()); //assertContains(res, "*UP*"); //assertContains(res, "*strict*"); @@ -100,7 +178,12 @@ public void testSecurityAdminV6Update() throws Exception { @Test public void testSecurityAdminRegularUpdate() throws Exception { - setup(Settings.EMPTY, new DynamicSecurityConfig(), Settings.EMPTY, true); + final Settings settings = Settings.builder() + .put("plugins.security.ssl.http.enabled",true) + .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) + .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) + .build(); + setup(Settings.EMPTY, null, settings, true); final String prefix = getResourceFolder()==null?"":getResourceFolder()+"/"; @@ -110,7 +193,7 @@ public void testSecurityAdminRegularUpdate() throws Exception { argsAsList.add("-ks"); argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"kirk-keystore.jks").toFile().getAbsolutePath()); argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.nodePort)); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); argsAsList.add("-cn"); argsAsList.add(clusterInfo.clustername); argsAsList.add("-cd"); @@ -121,7 +204,7 @@ public void testSecurityAdminRegularUpdate() throws Exception { int returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); Assert.assertEquals(0, returnCode); - RestHelper rh = nonSslRestHelper(); + RestHelper rh = restHelper(); HttpResponse res; Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("_opendistro/_security/health?pretty")).getStatusCode()); @@ -133,7 +216,12 @@ public void testSecurityAdminRegularUpdate() throws Exception { @Test public void testSecurityAdminSingularV7Updates() throws Exception { - setup(Settings.EMPTY, new DynamicSecurityConfig(), Settings.EMPTY, true); + final Settings settings = Settings.builder() + .put("plugins.security.ssl.http.enabled",true) + .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) + .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) + .build(); + setup(Settings.EMPTY, new DynamicSecurityConfig(), settings, true); final String prefix = getResourceFolder()==null?"":getResourceFolder()+"/"; @@ -143,7 +231,7 @@ public void testSecurityAdminSingularV7Updates() throws Exception { argsAsList.add("-ks"); argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"kirk-keystore.jks").toFile().getAbsolutePath()); argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.nodePort)); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); argsAsList.add("-cn"); argsAsList.add(clusterInfo.clustername); argsAsList.add("-f"); @@ -162,7 +250,7 @@ public void testSecurityAdminSingularV7Updates() throws Exception { argsAsList.add("-ks"); argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"kirk-keystore.jks").toFile().getAbsolutePath()); argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.nodePort)); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); argsAsList.add("-cn"); argsAsList.add(clusterInfo.clustername); argsAsList.add("-f"); @@ -181,7 +269,7 @@ public void testSecurityAdminSingularV7Updates() throws Exception { argsAsList.add("-ks"); argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"kirk-keystore.jks").toFile().getAbsolutePath()); argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.nodePort)); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); argsAsList.add("-cn"); argsAsList.add(clusterInfo.clustername); argsAsList.add("-f"); @@ -194,7 +282,7 @@ public void testSecurityAdminSingularV7Updates() throws Exception { returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); Assert.assertEquals(0, returnCode); - RestHelper rh = nonSslRestHelper(); + RestHelper rh = restHelper(); HttpResponse res; Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("_opendistro/_security/health?pretty")).getStatusCode()); @@ -206,8 +294,13 @@ public void testSecurityAdminSingularV7Updates() throws Exception { @Test public void testSecurityAdminSingularV6Updates() throws Exception { - setup(Settings.EMPTY, new DynamicSecurityConfig(), Settings.EMPTY, true); - + final Settings settings = Settings.builder() + .put("plugins.security.ssl.http.enabled",true) + .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) + .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) + .build(); + setup(Settings.EMPTY, new DynamicSecurityConfig(), settings, true); + final String prefix = getResourceFolder()==null?"":getResourceFolder()+"/"; List argsAsList = new ArrayList<>(); @@ -216,7 +309,7 @@ public void testSecurityAdminSingularV6Updates() throws Exception { argsAsList.add("-ks"); argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"kirk-keystore.jks").toFile().getAbsolutePath()); argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.nodePort)); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); argsAsList.add("-cn"); argsAsList.add(clusterInfo.clustername); argsAsList.add("-f"); @@ -229,7 +322,7 @@ public void testSecurityAdminSingularV6Updates() throws Exception { Assert.assertNotEquals(0, returnCode); - RestHelper rh = nonSslRestHelper(); + RestHelper rh = restHelper(); HttpResponse res; Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("_opendistro/_security/health?pretty")).getStatusCode()); @@ -241,8 +334,13 @@ public void testSecurityAdminSingularV6Updates() throws Exception { @Test public void testSecurityAdminInvalidYml() throws Exception { - setup(Settings.EMPTY, new DynamicSecurityConfig(), Settings.EMPTY, true); - + final Settings settings = Settings.builder() + .put("plugins.security.ssl.http.enabled",true) + .put("plugins.security.ssl.http.keystore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("node-0-keystore.jks")) + .put("plugins.security.ssl.http.truststore_filepath", FileHelper.getAbsoluteFilePathFromClassPath("truststore.jks")) + .build(); + setup(Settings.EMPTY, new DynamicSecurityConfig(), settings, true); + final String prefix = getResourceFolder()==null?"":getResourceFolder()+"/"; List argsAsList = new ArrayList<>(); @@ -251,7 +349,7 @@ public void testSecurityAdminInvalidYml() throws Exception { argsAsList.add("-ks"); argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"kirk-keystore.jks").toFile().getAbsolutePath()); argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.nodePort)); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); argsAsList.add("-cn"); argsAsList.add(clusterInfo.clustername); argsAsList.add("-f"); @@ -264,7 +362,7 @@ public void testSecurityAdminInvalidYml() throws Exception { int returnCode = SecurityAdmin.execute(argsAsList.toArray(new String[0])); Assert.assertNotEquals(0, returnCode); - RestHelper rh = nonSslRestHelper(); + RestHelper rh = restHelper(); HttpResponse res; Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("_opendistro/_security/health?pretty")).getStatusCode()); @@ -301,7 +399,7 @@ public void testSecurityAdminReloadInvalidConfig() throws Exception { argsAsList.add("-ks"); argsAsList.add(FileHelper.getAbsoluteFilePathFromClassPath(prefix+"kirk-keystore.jks").toFile().getAbsolutePath()); argsAsList.add("-p"); - argsAsList.add(String.valueOf(clusterInfo.nodePort)); + argsAsList.add(String.valueOf(clusterInfo.httpPort)); argsAsList.add("-cn"); argsAsList.add(clusterInfo.clustername); argsAsList.add("-rl");