From a8596de31fff2372fadd3f48dd97c6d674f2eb58 Mon Sep 17 00:00:00 2001 From: Tim Vernum Date: Thu, 31 Jan 2019 18:06:05 +1100 Subject: [PATCH] Introduce ssl settings to reindex from remote (#37527) Adds reindex.ssl.* settings for reindex from remote. This uses the ssl-config/ internal library to parse and load SSL configuration and files. This is applied when using the low level rest client to connect to a remote ES node Relates: #37287 Resolves: #29755 --- build.gradle | 3 +- modules/reindex/build.gradle | 6 + .../AbstractAsyncBulkByScrollAction.java | 11 +- .../reindex/AsyncDeleteByQueryAction.java | 10 +- .../index/reindex/ReindexPlugin.java | 24 +- .../index/reindex/ReindexSslConfig.java | 161 +++++++++++++ .../reindex/TransportDeleteByQueryAction.java | 2 +- .../index/reindex/TransportReindexAction.java | 21 +- .../reindex/TransportUpdateByQueryAction.java | 10 +- ...yncBulkByScrollActionMetadataTestCase.java | 2 +- ...AsyncBulkByScrollActionScriptTestCase.java | 4 +- .../reindex/AsyncBulkByScrollActionTests.java | 22 +- ...ReindexFromRemoteBuildRestClientTests.java | 16 +- .../index/reindex/ReindexMetadataTests.java | 4 +- .../reindex/ReindexRestClientSslTests.java | 213 ++++++++++++++++++ .../index/reindex/ReindexScriptTests.java | 13 +- .../reindex/UpdateByQueryMetadataTests.java | 2 +- .../reindex/UpdateByQueryWithScriptTests.java | 9 +- .../elasticsearch/index/reindex/README.txt | 25 ++ .../org/elasticsearch/index/reindex/ca.p12 | Bin 0 -> 2527 bytes .../org/elasticsearch/index/reindex/ca.pem | 25 ++ .../index/reindex/client/client.crt | 19 ++ .../index/reindex/client/client.key | 30 +++ .../elasticsearch/index/reindex/http/http.crt | 22 ++ .../elasticsearch/index/reindex/http/http.key | 30 +++ 25 files changed, 646 insertions(+), 38 deletions(-) create mode 100644 modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexSslConfig.java create mode 100644 modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRestClientSslTests.java create mode 100644 modules/reindex/src/test/resources/org/elasticsearch/index/reindex/README.txt create mode 100644 modules/reindex/src/test/resources/org/elasticsearch/index/reindex/ca.p12 create mode 100644 modules/reindex/src/test/resources/org/elasticsearch/index/reindex/ca.pem create mode 100644 modules/reindex/src/test/resources/org/elasticsearch/index/reindex/client/client.crt create mode 100644 modules/reindex/src/test/resources/org/elasticsearch/index/reindex/client/client.key create mode 100644 modules/reindex/src/test/resources/org/elasticsearch/index/reindex/http/http.crt create mode 100644 modules/reindex/src/test/resources/org/elasticsearch/index/reindex/http/http.key diff --git a/build.gradle b/build.gradle index 4bd211a12b3b0..e5bc1ab3ba986 100644 --- a/build.gradle +++ b/build.gradle @@ -201,7 +201,7 @@ allprojects { } /* Sets up the dependencies that we build as part of this project but - register as thought they were external to resolve internally. We register + register as though they were external to resolve internally. We register them as external dependencies so the build plugin that we use can be used to build elasticsearch plugins outside of the elasticsearch source tree. */ ext.projectSubstitutions = [ @@ -214,6 +214,7 @@ allprojects { "org.elasticsearch:elasticsearch-x-content:${version}": ':libs:x-content', "org.elasticsearch:elasticsearch-geo:${version}": ':libs:elasticsearch-geo', "org.elasticsearch:elasticsearch-secure-sm:${version}": ':libs:secure-sm', + "org.elasticsearch:elasticsearch-ssl-config:${version}": ':libs:elasticsearch-ssl-config', "org.elasticsearch.client:elasticsearch-rest-client:${version}": ':client:rest', "org.elasticsearch.client:elasticsearch-rest-client-sniffer:${version}": ':client:sniffer', "org.elasticsearch.client:elasticsearch-rest-high-level-client:${version}": ':client:rest-high-level', diff --git a/modules/reindex/build.gradle b/modules/reindex/build.gradle index d10d8c34a0c81..36f327a5b6c30 100644 --- a/modules/reindex/build.gradle +++ b/modules/reindex/build.gradle @@ -56,6 +56,7 @@ unitTest { dependencies { compile "org.elasticsearch.client:elasticsearch-rest-client:${version}" + compile "org.elasticsearch:elasticsearch-ssl-config:${version}" // for http - testing reindex from remote testCompile project(path: ':modules:transport-netty4', configuration: 'runtime') // for parent/child testing @@ -71,6 +72,11 @@ thirdPartyAudit.ignoreMissingClasses ( 'org.apache.log.Logger', ) +forbiddenPatterns { + // PKCS#12 file are not UTF-8 + exclude '**/*.p12' +} + // Support for testing reindex-from-remote against old Elaticsearch versions configurations { oldesFixture diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollAction.java index 9617d2a3774da..a4b28e6c598d3 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollAction.java @@ -34,6 +34,7 @@ import org.elasticsearch.action.bulk.Retry; import org.elasticsearch.action.delete.DeleteRequest; import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.support.TransportAction; import org.elasticsearch.client.ParentTaskAssigningClient; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.unit.TimeValue; @@ -82,13 +83,15 @@ * Abstract base for scrolling across a search and executing bulk actions on all results. All package private methods are package private so * their tests can use them. Most methods run in the listener thread pool because the are meant to be fast and don't expect to block. */ -public abstract class AbstractAsyncBulkByScrollAction> { +public abstract class AbstractAsyncBulkByScrollAction, + Action extends TransportAction> { + protected final Logger logger; protected final BulkByScrollTask task; protected final WorkerBulkByScrollTaskState worker; protected final ThreadPool threadPool; - protected final ScriptService scriptService; + protected final Action mainAction; /** * The request for this action. Named mainRequest because we create lots of request variables all representing child * requests of this mainRequest. @@ -112,7 +115,7 @@ public abstract class AbstractAsyncBulkByScrollAction listener) { this.task = task; @@ -124,7 +127,7 @@ public AbstractAsyncBulkByScrollAction(BulkByScrollTask task, boolean needsSourc this.logger = logger; this.client = client; this.threadPool = threadPool; - this.scriptService = scriptService; + this.mainAction = mainAction; this.mainRequest = mainRequest; this.listener = listener; BackoffPolicy backoffPolicy = buildBackoffPolicy(); diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AsyncDeleteByQueryAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AsyncDeleteByQueryAction.java index 5026ec0f79c57..b317ea06d9f35 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AsyncDeleteByQueryAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/AsyncDeleteByQueryAction.java @@ -31,22 +31,22 @@ /** * Implementation of delete-by-query using scrolling and bulk. */ -public class AsyncDeleteByQueryAction extends AbstractAsyncBulkByScrollAction { +public class AsyncDeleteByQueryAction extends AbstractAsyncBulkByScrollAction { + private final boolean useSeqNoForCAS; public AsyncDeleteByQueryAction(BulkByScrollTask task, Logger logger, ParentTaskAssigningClient client, - ThreadPool threadPool, DeleteByQueryRequest request, ScriptService scriptService, - ClusterState clusterState, ActionListener listener) { + ThreadPool threadPool, TransportDeleteByQueryAction action, DeleteByQueryRequest request, + ScriptService scriptService, ClusterState clusterState, ActionListener listener) { super(task, // not all nodes support sequence number powered optimistic concurrency control, we fall back to version clusterState.nodes().getMinNodeVersion().onOrAfter(Version.V_6_7_0) == false, // all nodes support sequence number powered optimistic concurrency control and we can use it clusterState.nodes().getMinNodeVersion().onOrAfter(Version.V_6_7_0), - logger, client, threadPool, request, scriptService, listener); + logger, client, threadPool, action, request, listener); useSeqNoForCAS = clusterState.nodes().getMinNodeVersion().onOrAfter(Version.V_6_7_0); } - @Override protected boolean accept(ScrollableHitSource.Hit doc) { // Delete-by-query does not require the source to delete a document diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexPlugin.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexPlugin.java index d601f5c06e728..52fc321286a9e 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexPlugin.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexPlugin.java @@ -21,21 +21,32 @@ import org.elasticsearch.action.ActionRequest; import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.client.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.node.DiscoveryNodes; +import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.SettingsFilter; +import org.elasticsearch.common.xcontent.NamedXContentRegistry; +import org.elasticsearch.env.Environment; +import org.elasticsearch.env.NodeEnvironment; import org.elasticsearch.plugins.ActionPlugin; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestHandler; +import org.elasticsearch.script.ScriptService; import org.elasticsearch.tasks.Task; +import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.watcher.ResourceWatcherService; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.function.Supplier; @@ -69,8 +80,19 @@ public List getRestHandlers(Settings settings, RestController restC new RestRethrottleAction(settings, restController, nodesInCluster)); } + @Override + public Collection createComponents(Client client, ClusterService clusterService, ThreadPool threadPool, + ResourceWatcherService resourceWatcherService, ScriptService scriptService, + NamedXContentRegistry xContentRegistry, Environment environment, + NodeEnvironment nodeEnvironment, NamedWriteableRegistry namedWriteableRegistry) { + return Collections.singletonList(new ReindexSslConfig(environment.settings(), environment, resourceWatcherService)); + } + @Override public List> getSettings() { - return singletonList(TransportReindexAction.REMOTE_CLUSTER_WHITELIST); + final List> settings = new ArrayList<>(); + settings.add(TransportReindexAction.REMOTE_CLUSTER_WHITELIST); + settings.addAll(ReindexSslConfig.getSettings()); + return settings; } } diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexSslConfig.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexSslConfig.java new file mode 100644 index 0000000000000..10be40acbc792 --- /dev/null +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/ReindexSslConfig.java @@ -0,0 +1,161 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.index.reindex; + +import org.apache.http.conn.ssl.DefaultHostnameVerifier; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.settings.SecureSetting; +import org.elasticsearch.common.settings.SecureString; +import org.elasticsearch.common.settings.Setting; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.ssl.SslConfiguration; +import org.elasticsearch.common.ssl.SslConfigurationKeys; +import org.elasticsearch.common.ssl.SslConfigurationLoader; +import org.elasticsearch.env.Environment; +import org.elasticsearch.watcher.FileChangesListener; +import org.elasticsearch.watcher.FileWatcher; +import org.elasticsearch.watcher.ResourceWatcherService; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import static org.elasticsearch.common.settings.Setting.listSetting; +import static org.elasticsearch.common.settings.Setting.simpleString; + +/** + * Loads "reindex.ssl.*" configuration from Settings, and makes the applicable configuration (trust manager / key manager / hostname + * verification / cipher-suites) available for reindex-from-remote. + */ +class ReindexSslConfig { + + private static final Map> SETTINGS = new HashMap<>(); + private static final Map> SECURE_SETTINGS = new HashMap<>(); + + static { + Setting.Property[] defaultProperties = new Setting.Property[] { Setting.Property.NodeScope, Setting.Property.Filtered }; + Setting.Property[] deprecatedProperties = new Setting.Property[] { Setting.Property.Deprecated, Setting.Property.NodeScope, + Setting.Property.Filtered }; + for (String key : SslConfigurationKeys.getStringKeys()) { + String settingName = "reindex.ssl." + key; + final Setting.Property[] properties = SslConfigurationKeys.isDeprecated(key) ? deprecatedProperties : defaultProperties; + SETTINGS.put(settingName, simpleString(settingName, properties)); + } + for (String key : SslConfigurationKeys.getListKeys()) { + String settingName = "reindex.ssl." + key; + final Setting.Property[] properties = SslConfigurationKeys.isDeprecated(key) ? deprecatedProperties : defaultProperties; + SETTINGS.put(settingName, listSetting(settingName, Collections.emptyList(), Function.identity(), properties)); + } + for (String key : SslConfigurationKeys.getSecureStringKeys()) { + String settingName = "reindex.ssl." + key; + SECURE_SETTINGS.put(settingName, SecureSetting.secureString(settingName, null)); + } + } + + private final SslConfiguration configuration; + private volatile SSLContext context; + + public static List> getSettings() { + List> settings = new ArrayList<>(); + settings.addAll(SETTINGS.values()); + settings.addAll(SECURE_SETTINGS.values()); + return settings; + } + + ReindexSslConfig(Settings settings, Environment environment, ResourceWatcherService resourceWatcher) { + final SslConfigurationLoader loader = new SslConfigurationLoader("reindex.ssl.") { + + @Override + protected String getSettingAsString(String key) { + return settings.get(key); + } + + @Override + protected char[] getSecureSetting(String key) { + final Setting setting = SECURE_SETTINGS.get(key); + if (setting == null) { + throw new IllegalArgumentException("The secure setting [" + key + "] is not registered"); + } + return setting.get(settings).getChars(); + } + + @Override + protected List getSettingAsList(String key) throws Exception { + return settings.getAsList(key); + } + }; + configuration = loader.load(environment.configFile()); + reload(); + + final FileChangesListener listener = new FileChangesListener() { + @Override + public void onFileCreated(Path file) { + onFileChanged(file); + } + + @Override + public void onFileDeleted(Path file) { + onFileChanged(file); + } + + @Override + public void onFileChanged(Path file) { + ReindexSslConfig.this.reload(); + } + }; + for (Path file : configuration.getDependentFiles()) { + try { + final FileWatcher watcher = new FileWatcher(file); + watcher.addListener(listener); + resourceWatcher.add(watcher, ResourceWatcherService.Frequency.HIGH); + } catch (IOException e) { + throw new UncheckedIOException("cannot watch file [" + file + "]", e); + } + } + } + + private void reload() { + this.context = configuration.createSslContext(); + } + + /** + * Encapsulate the loaded SSL configuration as a HTTP-client {@link SSLIOSessionStrategy}. + * The returned strategy is immutable, but successive calls will return different objects that may have different + * configurations if the underlying key/certificate files are modified. + */ + SSLIOSessionStrategy getStrategy() { + final HostnameVerifier hostnameVerifier = configuration.getVerificationMode().isHostnameVerificationEnabled() + ? new DefaultHostnameVerifier() + : new NoopHostnameVerifier(); + final String[] protocols = configuration.getSupportedProtocols().toArray(Strings.EMPTY_ARRAY); + final String[] cipherSuites = configuration.getCipherSuites().toArray(Strings.EMPTY_ARRAY); + return new SSLIOSessionStrategy(context, protocols, cipherSuites, hostnameVerifier); + } +} diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportDeleteByQueryAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportDeleteByQueryAction.java index 9bda99b6e3943..08538b335535d 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportDeleteByQueryAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportDeleteByQueryAction.java @@ -61,7 +61,7 @@ public void doExecute(Task task, DeleteByQueryRequest request, ActionListener)ReindexRequest::new); this.threadPool = threadPool; this.clusterService = clusterService; @@ -113,6 +115,7 @@ public TransportReindexAction(Settings settings, ThreadPool threadPool, ActionFi this.client = client; remoteWhitelist = buildRemoteWhitelist(REMOTE_CLUSTER_WHITELIST.get(settings)); this.indexNameExpressionResolver = indexNameExpressionResolver; + this.sslConfig = sslConfig; } @Override @@ -129,7 +132,7 @@ protected void doExecute(Task task, ReindexRequest request, ActionListener { ParentTaskAssigningClient assigningClient = new ParentTaskAssigningClient(client, clusterService.localNode(), bulkByScrollTask); - new AsyncIndexBySearchAction(bulkByScrollTask, logger, assigningClient, threadPool, request, scriptService, state, + new AsyncIndexBySearchAction(bulkByScrollTask, logger, assigningClient, threadPool, this, request, state, listener).start(); } ); @@ -197,10 +200,11 @@ static void validateAgainstAliases(SearchRequest source, IndexRequest destinatio /** * Build the {@link RestClient} used for reindexing from remote clusters. * @param remoteInfo connection information for the remote cluster + * @param sslConfig configuration for potential outgoing HTTPS connections * @param taskId the id of the current task. This is added to the thread name for easier tracking * @param threadCollector a list in which we collect all the threads created by the client */ - static RestClient buildRestClient(RemoteInfo remoteInfo, long taskId, List threadCollector) { + static RestClient buildRestClient(RemoteInfo remoteInfo, ReindexSslConfig sslConfig, long taskId, List threadCollector) { Header[] clientHeaders = new Header[remoteInfo.getHeaders().size()]; int i = 0; for (Map.Entry header : remoteInfo.getHeaders().entrySet()) { @@ -233,6 +237,7 @@ static RestClient buildRestClient(RemoteInfo remoteInfo, long taskId, List { + static class AsyncIndexBySearchAction extends AbstractAsyncBulkByScrollAction { /** * List of threads created by this process. Usually actions don't create threads in Elasticsearch. Instead they use the builtin * {@link ThreadPool}s. But reindex-from-remote uses Elasticsearch's {@link RestClient} which doesn't use the @@ -257,7 +262,7 @@ static class AsyncIndexBySearchAction extends AbstractAsyncBulkByScrollAction createdThreads = emptyList(); AsyncIndexBySearchAction(BulkByScrollTask task, Logger logger, ParentTaskAssigningClient client, - ThreadPool threadPool, ReindexRequest request, ScriptService scriptService, ClusterState clusterState, + ThreadPool threadPool, TransportReindexAction action, ReindexRequest request, ClusterState clusterState, ActionListener listener) { super(task, /* @@ -265,7 +270,7 @@ static class AsyncIndexBySearchAction extends AbstractAsyncBulkByScrollAction()); - RestClient restClient = buildRestClient(remoteInfo, task.getId(), createdThreads); + RestClient restClient = buildRestClient(remoteInfo, mainAction.sslConfig, task.getId(), createdThreads); return new RemoteScrollableHitSource(logger, backoffPolicy, threadPool, worker::countSearchRetry, this::finishHim, restClient, remoteInfo.getQuery(), mainRequest.getSearchRequest()); } @@ -296,7 +301,7 @@ protected void finishHim(Exception failure, List indexingFailures, List public BiFunction, ScrollableHitSource.Hit, RequestWrapper> buildScriptApplier() { Script script = mainRequest.getScript(); if (script != null) { - return new ReindexScriptApplier(worker, scriptService, script, script.getParams()); + return new ReindexScriptApplier(worker, mainAction.scriptService, script, script.getParams()); } return super.buildScriptApplier(); } diff --git a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportUpdateByQueryAction.java b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportUpdateByQueryAction.java index 9ed7744f8a27a..b88696d65526c 100644 --- a/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportUpdateByQueryAction.java +++ b/modules/reindex/src/main/java/org/elasticsearch/index/reindex/TransportUpdateByQueryAction.java @@ -72,7 +72,7 @@ protected void doExecute(Task task, UpdateByQueryRequest request, ActionListener ClusterState state = clusterService.state(); ParentTaskAssigningClient assigningClient = new ParentTaskAssigningClient(client, clusterService.localNode(), bulkByScrollTask); - new AsyncIndexBySearchAction(bulkByScrollTask, logger, assigningClient, threadPool, request, scriptService, state, + new AsyncIndexBySearchAction(bulkByScrollTask, logger, assigningClient, threadPool, this, request, state, listener).start(); } ); @@ -81,19 +81,19 @@ protected void doExecute(Task task, UpdateByQueryRequest request, ActionListener /** * Simple implementation of update-by-query using scrolling and bulk. */ - static class AsyncIndexBySearchAction extends AbstractAsyncBulkByScrollAction { + static class AsyncIndexBySearchAction extends AbstractAsyncBulkByScrollAction { private final boolean useSeqNoForCAS; AsyncIndexBySearchAction(BulkByScrollTask task, Logger logger, ParentTaskAssigningClient client, - ThreadPool threadPool, UpdateByQueryRequest request, ScriptService scriptService, ClusterState clusterState, + ThreadPool threadPool, TransportUpdateByQueryAction action, UpdateByQueryRequest request, ClusterState clusterState, ActionListener listener) { super(task, // not all nodes support sequence number powered optimistic concurrency control, we fall back to version clusterState.nodes().getMinNodeVersion().onOrAfter(Version.V_6_7_0) == false, // all nodes support sequence number powered optimistic concurrency control and we can use it clusterState.nodes().getMinNodeVersion().onOrAfter(Version.V_6_7_0), - logger, client, threadPool, request, scriptService, listener); + logger, client, threadPool, action, request, listener); useSeqNoForCAS = clusterState.nodes().getMinNodeVersion().onOrAfter(Version.V_6_7_0); } @@ -101,7 +101,7 @@ static class AsyncIndexBySearchAction extends AbstractAsyncBulkByScrollAction, ScrollableHitSource.Hit, RequestWrapper> buildScriptApplier() { Script script = mainRequest.getScript(); if (script != null) { - return new UpdateByQueryScriptApplier(worker, scriptService, script, script.getParams()); + return new UpdateByQueryScriptApplier(worker, mainAction.scriptService, script, script.getParams()); } return super.buildScriptApplier(); } diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionMetadataTestCase.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionMetadataTestCase.java index 34da9f56b482c..8d67a3bd6760d 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionMetadataTestCase.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionMetadataTestCase.java @@ -28,5 +28,5 @@ protected ScrollableHitSource.BasicHit doc() { return new ScrollableHitSource.BasicHit("index", "type", "id", 0); } - protected abstract AbstractAsyncBulkByScrollAction action(); + protected abstract AbstractAsyncBulkByScrollAction action(); } diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java index c9eba1927ef15..76bf2a9160f51 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AbstractAsyncBulkByScrollActionScriptTestCase.java @@ -62,7 +62,7 @@ public void execute() { } }; when(scriptService.compile(any(), eq(UpdateScript.CONTEXT))).thenReturn(factory); - AbstractAsyncBulkByScrollAction action = action(scriptService, request().setScript(mockScript(""))); + AbstractAsyncBulkByScrollAction action = action(scriptService, request().setScript(mockScript(""))); RequestWrapper result = action.buildScriptApplier().apply(AbstractAsyncBulkByScrollAction.wrap(index), doc); return (result != null) ? (T) result.self() : null; } @@ -109,5 +109,5 @@ public void testSetOpTypeUnknown() throws Exception { assertThat(e.getMessage(), equalTo("Operation type [unknown] not allowed, only [noop, index, delete] are allowed")); } - protected abstract AbstractAsyncBulkByScrollAction action(ScriptService scriptService, Request request); + protected abstract AbstractAsyncBulkByScrollAction action(ScriptService scriptService, Request request); } diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java index cd23bf03add59..3ee651cf96184 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/AsyncBulkByScrollActionTests.java @@ -48,7 +48,9 @@ import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchScrollRequest; import org.elasticsearch.action.search.ShardSearchFailure; +import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.action.support.PlainActionFuture; +import org.elasticsearch.action.support.TransportAction; import org.elasticsearch.action.update.UpdateRequest; import org.elasticsearch.action.update.UpdateResponse; import org.elasticsearch.client.Client; @@ -71,6 +73,7 @@ import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.internal.InternalSearchResponse; +import org.elasticsearch.tasks.Task; import org.elasticsearch.tasks.TaskId; import org.elasticsearch.tasks.TaskManager; import org.elasticsearch.test.ESTestCase; @@ -675,10 +678,11 @@ private void simulateScrollResponse(DummyAsyncBulkByScrollAction action, TimeVal action.onScrollResponse(lastBatchTime, lastBatchSize, response); } - private class DummyAsyncBulkByScrollAction extends AbstractAsyncBulkByScrollAction { + private class DummyAsyncBulkByScrollAction + extends AbstractAsyncBulkByScrollAction { DummyAsyncBulkByScrollAction() { super(testTask, randomBoolean(), randomBoolean(), AsyncBulkByScrollActionTests.this.logger, - new ParentTaskAssigningClient(client, localNode, testTask), client.threadPool(), testRequest, null, listener); + new ParentTaskAssigningClient(client, localNode, testTask), client.threadPool(), null, testRequest, listener); } @Override @@ -698,6 +702,20 @@ BackoffPolicy buildBackoffPolicy() { } } + private static class DummyTransportAsyncBulkByScrollAction + extends TransportAction { + + + protected DummyTransportAsyncBulkByScrollAction(String actionName, ActionFilters actionFilters, TaskManager taskManager) { + super(actionName, actionFilters, taskManager); + } + + @Override + protected void doExecute(Task task, DummyAbstractBulkByScrollRequest request, ActionListener listener) { + // no-op + } + } + private static class DummyAbstractBulkByScrollRequest extends AbstractBulkByScrollRequest { DummyAbstractBulkByScrollRequest(SearchRequest searchRequest) { super(searchRequest, true); diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java index db32e4813b316..b0b35fbaac74e 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexFromRemoteBuildRestClientTests.java @@ -22,6 +22,10 @@ import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilderTestCase; import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +import org.elasticsearch.env.TestEnvironment; +import org.elasticsearch.watcher.ResourceWatcherService; import java.util.ArrayList; import java.util.HashMap; @@ -31,6 +35,7 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.synchronizedList; import static org.hamcrest.Matchers.hasSize; +import static org.mockito.Mockito.mock; public class ReindexFromRemoteBuildRestClientTests extends RestClientBuilderTestCase { public void testBuildRestClient() throws Exception { @@ -39,7 +44,7 @@ public void testBuildRestClient() throws Exception { RemoteInfo.DEFAULT_SOCKET_TIMEOUT, RemoteInfo.DEFAULT_CONNECT_TIMEOUT); long taskId = randomLong(); List threads = synchronizedList(new ArrayList<>()); - RestClient client = TransportReindexAction.buildRestClient(remoteInfo, taskId, threads); + RestClient client = TransportReindexAction.buildRestClient(remoteInfo, sslConfig(), taskId, threads); try { assertBusy(() -> assertThat(threads, hasSize(2))); int i = 0; @@ -63,11 +68,18 @@ public void testHeaders() throws Exception { headers, RemoteInfo.DEFAULT_SOCKET_TIMEOUT, RemoteInfo.DEFAULT_CONNECT_TIMEOUT); long taskId = randomLong(); List threads = synchronizedList(new ArrayList<>()); - RestClient client = TransportReindexAction.buildRestClient(remoteInfo, taskId, threads); + RestClient client = TransportReindexAction.buildRestClient(remoteInfo, sslConfig(), taskId, threads); try { assertHeaders(client, headers); } finally { client.close(); } } + + private ReindexSslConfig sslConfig() { + final Environment environment = TestEnvironment.newEnvironment(Settings.builder().put("path.home", createTempDir()).build()); + final ResourceWatcherService resourceWatcher = mock(ResourceWatcherService.class); + return new ReindexSslConfig(environment.settings(), environment, resourceWatcher); + } + } diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexMetadataTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexMetadataTests.java index 2fe6dd91cd19c..991300e55271f 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexMetadataTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexMetadataTests.java @@ -76,8 +76,8 @@ protected ReindexRequest request() { private class TestAction extends TransportReindexAction.AsyncIndexBySearchAction { TestAction() { - super(ReindexMetadataTests.this.task, ReindexMetadataTests.this.logger, null, ReindexMetadataTests.this.threadPool, request(), - null, null, listener()); + super(ReindexMetadataTests.this.task, ReindexMetadataTests.this.logger, null, ReindexMetadataTests.this.threadPool, + null, request(), null, listener()); } public ReindexRequest mainRequest() { diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRestClientSslTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRestClientSslTests.java new file mode 100644 index 0000000000000..f71d124986699 --- /dev/null +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexRestClientSslTests.java @@ -0,0 +1,213 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.index.reindex; + +import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsExchange; +import com.sun.net.httpserver.HttpsParameters; +import com.sun.net.httpserver.HttpsServer; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.common.SuppressForbidden; +import org.elasticsearch.common.bytes.BytesArray; +import org.elasticsearch.common.io.PathUtils; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.ssl.PemKeyConfig; +import org.elasticsearch.common.ssl.PemTrustConfig; +import org.elasticsearch.env.Environment; +import org.elasticsearch.env.TestEnvironment; +import org.elasticsearch.mocksocket.MockHttpServer; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.watcher.ResourceWatcherService; +import org.hamcrest.Matchers; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509ExtendedKeyManager; +import javax.net.ssl.X509ExtendedTrustManager; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.file.Path; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import static org.mockito.Mockito.mock; + +/** + * Because core ES doesn't have SSL available, this test uses a mock webserver + * as the remote endpoint. + * This makes it hard to test actual reindex functionality, but does allow us to test that the correct connections are made with the + * right SSL keys + trust settings. + */ +@SuppressForbidden(reason = "use http server") +public class ReindexRestClientSslTests extends ESTestCase { + + private static HttpsServer server; + private static Consumer handler = ignore -> { + }; + + @BeforeClass + public static void setupHttpServer() throws Exception { + InetSocketAddress address = new InetSocketAddress(InetAddress.getLoopbackAddress().getHostAddress(), 0); + SSLContext sslContext = buildServerSslContext(); + server = MockHttpServer.createHttps(address, 0); + server.setHttpsConfigurator(new ClientAuthHttpsConfigurator(sslContext)); + server.start(); + server.createContext("/", http -> { + assert http instanceof HttpsExchange; + HttpsExchange https = (HttpsExchange) http; + handler.accept(https); + // Always respond with 200 + // * If the reindex sees the 200, it means the SSL connection was established correctly. + // * We can check client certs in the handler. + https.sendResponseHeaders(200, 0); + https.close(); + }); + } + + @AfterClass + public static void shutdownHttpServer() { + server.stop(0); + server = null; + handler = null; + } + + private static SSLContext buildServerSslContext() throws Exception { + final SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); + final char[] password = "http-password".toCharArray(); + + final Path cert = PathUtils.get(ReindexRestClientSslTests.class.getResource("http/http.crt").toURI()); + final Path key = PathUtils.get(ReindexRestClientSslTests.class.getResource("http/http.key").toURI()); + final X509ExtendedKeyManager keyManager = new PemKeyConfig(cert, key, password).createKeyManager(); + + final Path ca = PathUtils.get(ReindexRestClientSslTests.class.getResource("ca.pem").toURI()); + final X509ExtendedTrustManager trustManager = new PemTrustConfig(Collections.singletonList(ca)).createTrustManager(); + + sslContext.init(new KeyManager[] { keyManager }, new TrustManager[] { trustManager }, null); + return sslContext; + } + + public void testClientFailsWithUntrustedCertificate() throws IOException { + final List threads = new ArrayList<>(); + final Settings settings = Settings.builder() + .put("path.home", createTempDir()) + .build(); + final Environment environment = TestEnvironment.newEnvironment(settings); + final ReindexSslConfig ssl = new ReindexSslConfig(settings, environment, mock(ResourceWatcherService.class)); + try (RestClient client = TransportReindexAction.buildRestClient(getRemoteInfo(), ssl, 1L, threads)) { + expectThrows(SSLHandshakeException.class, () -> client.performRequest(new Request("GET", "/"))); + } + } + + public void testClientSucceedsWithCertificateAuthorities() throws IOException { + final List threads = new ArrayList<>(); + final Path ca = getDataPath("ca.pem"); + final Settings settings = Settings.builder() + .put("path.home", createTempDir()) + .putList("reindex.ssl.certificate_authorities", ca.toString()) + .build(); + final Environment environment = TestEnvironment.newEnvironment(settings); + final ReindexSslConfig ssl = new ReindexSslConfig(settings, environment, mock(ResourceWatcherService.class)); + try (RestClient client = TransportReindexAction.buildRestClient(getRemoteInfo(), ssl, 1L, threads)) { + final Response response = client.performRequest(new Request("GET", "/")); + assertThat(response.getStatusLine().getStatusCode(), Matchers.is(200)); + } + } + + public void testClientSucceedsWithVerificationDisabled() throws IOException { + assertFalse("Cannot disable verification in FIPS JVM", inFipsJvm()); + final List threads = new ArrayList<>(); + final Settings settings = Settings.builder() + .put("path.home", createTempDir()) + .put("reindex.ssl.verification_mode", "NONE") + .build(); + final Environment environment = TestEnvironment.newEnvironment(settings); + final ReindexSslConfig ssl = new ReindexSslConfig(settings, environment, mock(ResourceWatcherService.class)); + try (RestClient client = TransportReindexAction.buildRestClient(getRemoteInfo(), ssl, 1L, threads)) { + final Response response = client.performRequest(new Request("GET", "/")); + assertThat(response.getStatusLine().getStatusCode(), Matchers.is(200)); + } + } + + public void testClientPassesClientCertificate() throws IOException { + final List threads = new ArrayList<>(); + final Path ca = getDataPath("ca.pem"); + final Path cert = getDataPath("client/client.crt"); + final Path key = getDataPath("client/client.key"); + final Settings settings = Settings.builder() + .put("path.home", createTempDir()) + .putList("reindex.ssl.certificate_authorities", ca.toString()) + .put("reindex.ssl.certificate", cert) + .put("reindex.ssl.key", key) + .put("reindex.ssl.key_passphrase", "client-password") + .build(); + AtomicReference clientCertificates = new AtomicReference<>(); + handler = https -> { + try { + clientCertificates.set(https.getSSLSession().getPeerCertificates()); + } catch (SSLPeerUnverifiedException e) { + logger.warn("Client did not provide certificates", e); + clientCertificates.set(null); + } + }; + final Environment environment = TestEnvironment.newEnvironment(settings); + final ReindexSslConfig ssl = new ReindexSslConfig(settings, environment, mock(ResourceWatcherService.class)); + try (RestClient client = TransportReindexAction.buildRestClient(getRemoteInfo(), ssl, 1L, threads)) { + final Response response = client.performRequest(new Request("GET", "/")); + assertThat(response.getStatusLine().getStatusCode(), Matchers.is(200)); + final Certificate[] certs = clientCertificates.get(); + assertThat(certs, Matchers.notNullValue()); + assertThat(certs, Matchers.arrayWithSize(1)); + assertThat(certs[0], Matchers.instanceOf(X509Certificate.class)); + final X509Certificate clientCert = (X509Certificate) certs[0]; + assertThat(clientCert.getSubjectDN().getName(), Matchers.is("CN=client")); + assertThat(clientCert.getIssuerDN().getName(), Matchers.is("CN=Elastic Certificate Tool Autogenerated CA")); + } + } + + private RemoteInfo getRemoteInfo() { + return new RemoteInfo("https", server.getAddress().getHostName(), server.getAddress().getPort(), "/", new BytesArray("test"), + "user", "password", Collections.emptyMap(), RemoteInfo.DEFAULT_SOCKET_TIMEOUT, RemoteInfo.DEFAULT_CONNECT_TIMEOUT); + } + + @SuppressForbidden(reason = "use http server") + private static class ClientAuthHttpsConfigurator extends HttpsConfigurator { + ClientAuthHttpsConfigurator(SSLContext sslContext) { + super(sslContext); + } + + @Override + public void configure(HttpsParameters params) { + params.setWantClientAuth(true); + } + } +} diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexScriptTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexScriptTests.java index 732bc9acdb662..b7fa35f7877f3 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexScriptTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/ReindexScriptTests.java @@ -20,9 +20,14 @@ package org.elasticsearch.index.reindex; import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.common.lucene.uid.Versions; +import org.elasticsearch.common.settings.Settings; import org.elasticsearch.script.ScriptService; +import org.elasticsearch.transport.TransportService; +import org.mockito.Mockito; +import java.util.Collections; import java.util.Map; import static org.hamcrest.Matchers.containsString; @@ -103,7 +108,11 @@ protected ReindexRequest request() { @Override protected TransportReindexAction.AsyncIndexBySearchAction action(ScriptService scriptService, ReindexRequest request) { - return new TransportReindexAction.AsyncIndexBySearchAction(task, logger, null, threadPool, request, scriptService, - null, listener()); + TransportService transportService = Mockito.mock(TransportService.class); + ReindexSslConfig sslConfig = Mockito.mock(ReindexSslConfig.class); + TransportReindexAction transportAction = new TransportReindexAction(Settings.EMPTY, threadPool, + new ActionFilters(Collections.emptySet()), null, null, scriptService, null, null, transportService, sslConfig); + return new TransportReindexAction.AsyncIndexBySearchAction(task, logger, null, threadPool, transportAction, request, + null, listener()); } } diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryMetadataTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryMetadataTests.java index 95ee787f13f63..6d6f034827ab8 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryMetadataTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryMetadataTests.java @@ -45,7 +45,7 @@ protected UpdateByQueryRequest request() { private class TestAction extends TransportUpdateByQueryAction.AsyncIndexBySearchAction { TestAction() { super(UpdateByQueryMetadataTests.this.task, UpdateByQueryMetadataTests.this.logger, null, - UpdateByQueryMetadataTests.this.threadPool, request(), null, ClusterState.EMPTY_STATE, listener()); + UpdateByQueryMetadataTests.this.threadPool, null, request(), ClusterState.EMPTY_STATE, listener()); } @Override diff --git a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWithScriptTests.java b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWithScriptTests.java index 0eb2a1cfb7d0a..22d4ac0eab799 100644 --- a/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWithScriptTests.java +++ b/modules/reindex/src/test/java/org/elasticsearch/index/reindex/UpdateByQueryWithScriptTests.java @@ -19,13 +19,17 @@ package org.elasticsearch.index.reindex; +import org.elasticsearch.action.support.ActionFilters; import org.elasticsearch.cluster.ClusterState; import org.elasticsearch.script.ScriptService; +import org.elasticsearch.transport.TransportService; +import java.util.Collections; import java.util.Date; import java.util.Map; import static org.hamcrest.Matchers.containsString; +import static org.mockito.Mockito.mock; public class UpdateByQueryWithScriptTests extends AbstractAsyncBulkByScrollActionScriptTestCase { @@ -54,7 +58,10 @@ protected UpdateByQueryRequest request() { @Override protected TransportUpdateByQueryAction.AsyncIndexBySearchAction action(ScriptService scriptService, UpdateByQueryRequest request) { - return new TransportUpdateByQueryAction.AsyncIndexBySearchAction(task, logger, null, threadPool, request, scriptService, + TransportService transportService = mock(TransportService.class); + TransportUpdateByQueryAction transportAction = new TransportUpdateByQueryAction(threadPool, + new ActionFilters(Collections.emptySet()), null, transportService, scriptService, null); + return new TransportUpdateByQueryAction.AsyncIndexBySearchAction(task, logger, null, threadPool, transportAction, request, ClusterState.EMPTY_STATE, listener()); } } diff --git a/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/README.txt b/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/README.txt new file mode 100644 index 0000000000000..4221d479442d9 --- /dev/null +++ b/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/README.txt @@ -0,0 +1,25 @@ +# ca.p12 + +$ES_HOME/bin/elasticsearch-certutil ca --out ca.p12 --pass "ca-password" --days 9999 + +# ca.pem + +openssl pkcs12 -info -in ./ca.p12 -nokeys -out ca.pem -passin "pass:ca-password" + +# http.p12 + +$ES_HOME/bin/elasticsearch-certutil cert --out http.zip --pass "http-password" \ + --days 9999 --pem --name "http" \ + --ca ca.p12 --ca-pass "ca-password" \ + --dns=localhost --dns=localhost.localdomain --dns=localhost4 --dns=localhost4.localdomain4 --dns=localhost6 --dns=localhost6.localdomain6 \ + --ip=127.0.0.1 --ip=0:0:0:0:0:0:0:1 +unzip http.zip +rm http.zip + +# client.p12 + +$ES_HOME/bin/elasticsearch-certutil cert --out client.zip --pass "client-password" \ + --name "client" --days 9999 --pem \ + --ca ca.p12 --ca-pass "ca-password" +unzip client.zip +rm client.zip diff --git a/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/ca.p12 b/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/ca.p12 new file mode 100644 index 0000000000000000000000000000000000000000..332a75aec64c19fcb8a944b1b1300d353748b32f GIT binary patch literal 2527 zcmY+EcRUmhAIFb7936hZNI^qE#Dl{?cyP!C z4nW|kEdNna8G`UsdKXyxA{x~6|NCN~qo5?>LB|L@=l}twru#p>80P`8_O{SBju(+4 zWR0-gU3sZ#?GwmNN+6*99taQW4o}c&J974UKW}p9c-loNE#IE0juZ5|rb=nW zKu6{GAeJc1Fp2 z3?V2ObMj+e-Ei)v%gS!$7KV1+9~!)b*8#5=(dTW+sWBoFLEyla)2x~8+5wFDQ(SMr zH{yw=cMbjW(f8TA%7@*ux`PVbY>TWcf&1ff=6UW44eQobVRR%1&eGi@Vl}1ul7Cc( zss1k$Rgza^Nw?Vmf6IAv?1$JgiZ%k@t5(-ew3|c6awSGYX?K%O26Pg;!D`yCOWVmY zFXU!L;y-VqCzrH5TiMCfr5SC+9wgmratnzx5IV`!@_iUar#g=SOCn>bM6Tt>^49nS z@l8SJ3z;P1J$L@#Mo}Ec+<(MRsp`wo>z&IS@^{P+iRj|+v$Z;5a?7GE~yffxLT3$Onr;wc!hUg^%>Azs`HD84PmU(4}B+pM0)@F zogjg^o~AmrYF%!NYfd|X9WEpU=k^*@dgNxs*qKHFs!+%z?vkTua8j$8)W;&H5j&ID zHlbC|l^JlR8t|_BozyJtzje4O+9^kc89%Ox1b@bJD6gN8d9~FwwTDk<(kmkdM1m$P zP%WcR+7@A-kiFB2zmjhqNF~jY<1&TG_J2yh(z#S0=g=JB7t*?kwyGs^5d}zKpB3a? zME~l0Y0x71D6R6FHDts?|16V0@y+O7Hr(Ei-ZRqIW|C_#Lc9^J)Nl6EXv;TQ^XRhR zQr!5D;`3bBLyy-^ardL(;;A*lEW!lhxa1JhPAH_=OzSL5I{CcnoROlq{K<)6sS0jeDrisbpJ3gO6bt2oCN4_z`T zT0<`XB$R{(wlW50*|56x3jYg8J18klT8tn+q1c0kR+3_NKjf`e)yb48E1q9P))@mwoiW2vMm z&tTj?pTKrH6l*9RNR?0mc&=8H?0tl3TM27G`R3OoMD+mpuex@s#rD}>Mam+s{H01T zE0UTQ1h58JBKZG?Y)DoR%Y8>@TL@C>mJC8xT0%k&iIBOFChK2EG}IR!rMf^AloS92 z9(eS3L-7x@Qvb)SX`Ry;FmIBe?jCA3;@#-#9`dc(f6W?<2e#Dv+7wFH7*5py{gX_N zymE&7(LVya({r_A1J@Qvb+W2g>#nEGAI0#r$3io?e>8db^4~D_kA2d|QZj^XoC@f5 z@EN}SguN->2A#ct8x%hBmhXq8t50X7{5gbqORt)mE8H&?)qytQ!bm?oW%A8`AJRf` zFLL7yQc47=G&zjJU1y@4&C4*U@{pT|3hj8&FA`lQ9vFJ>SwYM8T9kXzl%j}7@Jsg4 zGuxT$MhtY|id-s?Xn=S)`9b9cixn1Cmyx$X!{xNHO#P)Re5>RJY{}|A9}VbxTNx8q^&YZhf=)AEhuv^7U73YU>S@hei5)y+ga330=D&V;$U$ z8W9DfF3v&tgojw73(U5rrKtK(@ON~pYRcfWK{S!I_F%@j9m@7p8I-l@Up}NFztZm8 z%6xuwbJ1&>Z2bt6)oBD#2yH&)do(;M1K;!v36Q?LSo5-^6wGA)$x3jh(kw*$ z_U~{$mFeE~Jiml`I#MG8w*~(;<#=ihwwBb;F*K-N&G}NzB}&hL{<%CTETxzae=`)HZqaJ6SS##!`@ZqC)GfcKTkrJ;7L_q$cut z;WrrZlj2O4cuSng`{ON{h~6pF$|hChIju&y(d^Zy%Zy%&TjZ(`2t1CLVp#NT1)q@D z2E?HRM4$0;-90O;q(59;NS3N%Lco#M=))k)CJ z{LmPzQ>4&O`keR_&v}*7+GD51;7f${^=#XCeP(~#H{4nf;Ix`&*;)z1wzGua0M=pT z^sVA&wcl|s(aegGC_)RkH1!dz>fE}-G8aB85^r2x*P1x_#z^SO&rGQh?@+iVF^y*n z4QCYE5A{xMl_agR+jBgYsZMFTMwkxtxe-0DC-_{&ncn27Ue44m3A(jQ`$%?qre)_< zvu8_=2$5td{et2vGV@@7w86G2>$d{IrRWrP>oHv8{!%lz`Q}B6z{bgBuHmM7OtD7m zSmfQLN;^)oCRcJ)`Bp Z*!*5kK%{lsCg6zGH{GI*5=aqF{5P0cyMq7# literal 0 HcmV?d00001 diff --git a/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/ca.pem b/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/ca.pem new file mode 100644 index 0000000000000..ee758ca3e6370 --- /dev/null +++ b/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/ca.pem @@ -0,0 +1,25 @@ +Bag Attributes + friendlyName: ca + localKeyID: 54 69 6D 65 20 31 35 34 37 30 38 36 32 32 39 31 30 37 +subject=/CN=Elastic Certificate Tool Autogenerated CA +issuer=/CN=Elastic Certificate Tool Autogenerated CA +-----BEGIN CERTIFICATE----- +MIIDSTCCAjGgAwIBAgIUacmv5ElKJ1cs9n61tEpy5KM3Dv0wDQYJKoZIhvcNAQEL +BQAwNDEyMDAGA1UEAxMpRWxhc3RpYyBDZXJ0aWZpY2F0ZSBUb29sIEF1dG9nZW5l +cmF0ZWQgQ0EwHhcNMTkwMTEwMDIxMDI5WhcNNDYwNTI3MDIxMDI5WjA0MTIwMAYD +VQQDEylFbGFzdGljIENlcnRpZmljYXRlIFRvb2wgQXV0b2dlbmVyYXRlZCBDQTCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ0rA35tPl0FN+BPk2YfmET9 +MvDWFLvfL2Z1aw1q1vnd12K9zumjN6veilHA2Iw/P4LG/mkQZvY4bDPgibRD7hbE +vwPoju4vr614tw60+FlkpO6HezYo2I3cni1//Gehhs5EW2P3g7Lw7UNCOAfcR2QQ +p/dtwXYWzXHY9jTevQSv2q/x5jWKZT4ltaQExzvXAcxRGqyWV6d5vol3KH/GpCSI +SQvRmRVNQGXhxi66MjCglGAM2oicd1qCUDCrljdFD/RQ1UzqIJRTXZQKOno1/Em9 +xR0Cd5KQapqttPusAO6uZblMO2Ru+XjCD6Y0o41eCDbkd0xA3/wgP3MD5n41yncC +AwEAAaNTMFEwHQYDVR0OBBYEFJTry9da5RZbbELYCaWVVFllSm8DMB8GA1UdIwQY +MBaAFJTry9da5RZbbELYCaWVVFllSm8DMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADggEBADA6qhC35PwuL7LRddbhjjW8U/cCmG9m7AIvH6N+Mw/k76gt +tJkEDxztMHUG+A2IPyEcYm7MLr1D8xEQYsq0x4pzFcQnMSQDv4WTK35vRxMtaqwA +WZTyA+DibBknbaP1z3gNhR9A0TKx4cPagN3OYFvAi/24abf8qS6D/bcOiPDQ4oPb +DVhmhqt5zduDM+Xsf6d4nsA6sf9+4AzneaZKGAMgCXgo4mYeP7M4nMQk0L3ao9Ts ++Usr8WRxc4xHGyb09fsXWSz7ZmiJ6iXK2NvRUq46WCINLONLzNkx29WEKQpI3wh4 +kyx6wF9lwBF06P1raFIBMeMOCkqDc+nj7A91PEA= +-----END CERTIFICATE----- diff --git a/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/client/client.crt b/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/client/client.crt new file mode 100644 index 0000000000000..337d24e2493ac --- /dev/null +++ b/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/client/client.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDIDCCAgigAwIBAgIUNOREYZadZ2EVkJ1m8Y9jnVmWmtAwDQYJKoZIhvcNAQEL +BQAwNDEyMDAGA1UEAxMpRWxhc3RpYyBDZXJ0aWZpY2F0ZSBUb29sIEF1dG9nZW5l +cmF0ZWQgQ0EwHhcNMTkwMTEwMDIxMDMyWhcNNDYwNTI3MDIxMDMyWjARMQ8wDQYD +VQQDEwZjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCCP2LE +nws2+ZIwSQ3IvIhVfrueUmNt7Y5TdhhwO32p2wC4ZA62J9L8klAzt7R+izcL/qbF +65inbXM0A7ge/2wZ09kbqBk5uS8jDetJS8lQmWVZDHfVi8g/yDMWklz2mQYleYmU +HPyIplai3P3KBoT8HurzHw2C953EZ2HiANFnGoEPZZ5ytcT2WenxuU5kSXSxuDyn +8/dCVHEQL1Yipr2LQKYQAHotjo56OhyL9KS5YPjzSFREeyRfQinssTmpGFsua/PK +Vqj+hRdkaqRfiqPq3wxn8oOSpZLQe58O1e7OlqgjkPuZdjZ0pQ7KJj7N3fUQNSeg +2VC2tk8zv/C/Qr2bAgMBAAGjTTBLMB0GA1UdDgQWBBQziDNuD83ZLwEt1e1txYJu +oSseEDAfBgNVHSMEGDAWgBSU68vXWuUWW2xC2AmllVRZZUpvAzAJBgNVHRMEAjAA +MA0GCSqGSIb3DQEBCwUAA4IBAQAPpyWyR4w6GvfvPmA1nk1qd7fsQ1AucrYweIJx +dTeXg3Ps1bcgNq9Us9xtsKmsoKD8UhtPN6e8W8MkMmri+MSzlEemE+pJZrjHEudi +Sj0AFVOK6jaE0lerbCnTQZvYH+J9Eb1i9RP7XHRShkR4MWgy2BzlENk9/LRbr84W +Yf5TuM9+ApiiiOoX9UfSGBzNnqwhJNpG9yJ+HnQSqTnJJc/wL0211zLme9I/nhf0 +kQx6mPedJ3gGoJ8gqz38djIrhJDxq+0Bd9SsdlR6yT+1+bY7hinYx2eLV91AybZ4 +x07Kyl174DD41PYaE1AtoLlrMrQ5BG7Md50Am+XXOR1X1dkZ +-----END CERTIFICATE----- diff --git a/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/client/client.key b/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/client/client.key new file mode 100644 index 0000000000000..95e11f79cea24 --- /dev/null +++ b/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/client/client.key @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,81AB10154C04B38F + +0L6Buvpeg6QHh/mbYp/3bXDCsu0k0j5xPdIGWd6NCOdb24OQFsOjeA2WuPqs0WWF +gzVrjh984biS3IqeglEr6X6PfVJ0QOgBkq0XgSBXhuoRJL/302N9oPGsf8T8oW9t +pqR/JIB2L7lMbJlJYSjMl0YQT3hWpo2BlrtSIc/GWOKfjDNWc9BL+oHvKJwql1lb +n4yMvYFYJDqgzgxa3r4IIQNsCn3SP+gqbTx9vF6StOIroV51BdSL4IGWRvqnMJrh +ybk1EHSLR1oGcONLU4Ksi33UxdImG70SsnoH/NnInDvV2bxmxmgf5SfYKtxFhoxz +0hISKTMTerPGtRQ5p8wtEi/ULKyInK+qF3tLgZa+S5VbByjDnUo2dCcbDDSkH5pO +uczJ2bs1kJegpCrUueJdbi9OX2upmF+tJb9+5hzFTvey8dUWTEpdiN0xbp4BLfNd +Yp4sMHZovsDJKIjDb0NbXRgLeFh1ijlLPhKwIXWTF3BaCKcSw34Qv22YPwn3qNuw +0KuUPAo0B65R/hoJguvtks8QAXe0S1jZS/fAlQCoIB0TIduy1qkyje+AnSW+1RL0 +ysBxLqbvRUqWlgnu7/28V4FD8JNu3O+UGBEelXlfokLgCBZ6lSys2d3Zy/XVBnG0 +cPl59if+fxKaMWlhFvMLFBup1Y4a/1zA7Sx6kkhvawekHr40NcG4kLHJ+O6UoM4d +/ibnbfIksLNkuo/nwoEcKp7W6SxafV0hROdxClkGKild66rnHtk4IGATjaBqt9nr +FuO3vRtLuUMS+/4kpvhMwl0RhX2/i6xgV+klWNYNu1JTGDFvdG3qfiY2w88EIbGe +rn8JEvRtaH/XNeGdhBwbuObvTifiHyYzA1i5Zh8zvE2+Dthlk19jbBoOUx//LOi2 +JrNkAsqQCF4HXh7n9HWA/ZrKTP7Xvkig6Vf7M2Y/tO361LSJfzKcRFLpl0P2ntEv +XwFOqTvOURERTVr4sBLOVPRAhIs3yvkI5xfurXzbRWtSeLgrMoDgJlXIQbuXd8sq +zIBLqvYf2bcroB66XJqX1IFWEstym/NHGcbrwjR5Fn2p3YAtXnIbw8VhHwV+LIOl +ky/wH9vbnML/DE81qFqRe8vNZw2sGn9skOyU/QvKeV1NRHYZSV3hMx82bPnjgFeB +ilzkb8FEPOAOJ0m44Q3C9eUoazJT8aCuRIAgSL43se1E2pFlIXQTfYRARaWEkSf9 +0hXqQJc17b+Hj0ire3PUqbG3+/l1qMhhIHwq7Kuyy2neTuW/DXbXp2AMv/bLcnHH +apVeRZaYXVSnGXJNk2CeRnCs8OGir8g5zkH+fmVb9knt6TL2oFIsQqULyrLolhfe +6Q8mLzq/sd+w+VuN1n/5+RQqOJZWEkLFzQPx8wTqeTB19OE0gjncrqzCHq7INqRe +tGClWOj/yL0Sciu3ctVGz1VAbgeBKnLdKm2TX4oFB4OG4E7GMXIL7hGxjtjLAVMW +XNc3ZYNQra+iPqJtFxnmbrF2Sn0Wr0hcAT1V0A0TRKe/n0lpUrfhTy/q4DUlOVKG +qdCsTGoYXObpUWU5G9GyCVWWRJyrTxJcBZ9KWJu9Y/aMFzoa2n0HQw== +-----END RSA PRIVATE KEY----- diff --git a/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/http/http.crt b/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/http/http.crt new file mode 100644 index 0000000000000..309ade87fbd78 --- /dev/null +++ b/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/http/http.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDsjCCApqgAwIBAgIUXxlg/0/g3UYekXWBRpkHM84EYfIwDQYJKoZIhvcNAQEL +BQAwNDEyMDAGA1UEAxMpRWxhc3RpYyBDZXJ0aWZpY2F0ZSBUb29sIEF1dG9nZW5l +cmF0ZWQgQ0EwHhcNMTkwMTEwMDIxMDMwWhcNNDYwNTI3MDIxMDMwWjAPMQ0wCwYD +VQQDEwRodHRwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi8VQaSR6 +uqgT1Rkw+a39OSXcXuhJBVdoO+AyYPK7hdUTxj1aqnXkKeAiNGpe/J+uXZ837Spy +rmBZS3k6S5hLEceF2xug8yrR7RYEZ+JvGlRgg/jj+61gGbHAD314+vvu0YUo06YG +wbz9AnjJA/sMbsCp3iSzWIkwZBZcCoZ/YsG4I89LSjYL3YmRi2193WMX6/OfQYMN +Fkv61r/iwBEkgJ14cUSYe3norGuQfZuXSh5kI5D5R7q7Bmb0um+jzY/l62kj3oR1 +YWo3g6DdU/Bc/3/KmEEVXIfdTonMBMyL8PvYORoMKrYdph3E8e39ZQhPeBJNJKw0 +XzsZFzIUlTw0kQIDAQABo4HgMIHdMB0GA1UdDgQWBBTiqknjZLa5E1BneHRvTkNa +Bm4nNTAfBgNVHSMEGDAWgBSU68vXWuUWW2xC2AmllVRZZUpvAzCBjwYDVR0RBIGH +MIGEgglsb2NhbGhvc3SCF2xvY2FsaG9zdDYubG9jYWxkb21haW42hwR/AAABhxAA +AAAAAAAAAAAAAAAAAAABggpsb2NhbGhvc3Q0ggpsb2NhbGhvc3Q2ghVsb2NhbGhv +c3QubG9jYWxkb21haW6CF2xvY2FsaG9zdDQubG9jYWxkb21haW40MAkGA1UdEwQC +MAAwDQYJKoZIhvcNAQELBQADggEBAIZr8EhhCbNyc6iHzUJ/NrUGht5RDHUKN9WU +2fd+SJlWijQYGoFW6LfabmYxIVPAFtYzUiA378NFoOZZ4kdC3gQng8izvS2UDcO6 +cAG5q/dxop3VXqcLeK3NpH2jd83M8VZaOThPj/F07eTkVX+sGu+7VL5Lc/XPe8JS +HhH2QtcTPGPpzPnWOUMLpRy4mh5sDyeftWr2PTFgMXFD6dtzDvaklGJvr1TmcOVb +BFYyVyXRq6v8YsrRPp0GIl+X3zd3KgwUMuEzRKkJgeI1lZRjmHMIyFcqxlwMaHpv +r1XUmz02ycy6t3n+2kCgfU6HnjbeFh55KzNCEv8TXQFg8Z8YpDA= +-----END CERTIFICATE----- diff --git a/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/http/http.key b/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/http/http.key new file mode 100644 index 0000000000000..8b8d3b4083c67 --- /dev/null +++ b/modules/reindex/src/test/resources/org/elasticsearch/index/reindex/http/http.key @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,127A4142FA81C5A1 + +dP6oSAUl47KCnP0YZSX108qcX5s2nVGpD0qtnVQg89mLVFd7IxpKQaIuODSadRTo +AD0KINITy3ZwUr/TTJgERu88baBsTHv3PLEe7TpQI2DGGDz3aZfO9e6Jvglbdi5b +CBLaxRXGGhhH9YH0E87Lp3JEwg4udWmlNahGIhbqNheZNTtDKt+Lx80TyyIml2r/ +GAhjT4UPvIRrATFAcL/3EKOjRqvb6SeGnZu21n2TSmsBEr02gC0Ox3qmsnRM3kvU +jCuUzWTzJSQLXZwZuMtv5srOSFAbU8EklFXNhWJU/7GBy215aAAW48hCzkPMVEbg +oeH4nuze/Uulih9UxJGCBIpvfTnksyMRGP/zdy1mnKuqQk+yI0n7JWMJL8QoDQc8 +XvzqOmKLdBVezmzOVP/PyMAhYWetILh/1UesjyJot2hwSXPAxqBHPVA9bnmel6CQ +VccNSwaK120yT5YhkUMFc0AmUpztzNMQzJ10g1dW+Qsr+n4vtFmAuTvBgogNNVXn +eX1hbbiXGO1Fw4OMu6qTJ4T/P+VFb0CxoxETWeqdjcs4LGbeqF68nayEsW0ZzhbI +W5c+JAbW18Kb+k/KzKZTtJEXBw6B/2FMe9x9z3BIpVhplM2KsNk7joWnumD8LfUT +ORRHUPV7bkdiDsn2CRaevubDQiChcjsdLWhG7JLm54ttyif7/X7htGOXPZLDLK8B +Vxe09B006f7lM0tXEx8BLFDNroMLlrxB4K5MlwWpS3LLqy4zDbHka2I3s/ST/BD4 +0EURHefiXJkR6bRsfGCl3JDk0EakcUXM+Ob5/2rC/rPXO2pC0ksiQ2DSBm7ak9om +vlC7dIzVipL0LZTd4SUDJyvmK4Ws6V98O5b+79To6oZnVs5CjvcmpSFVePZa5gm/ +DB8LOpW4jklz+ybJtHJRbEIzmpfwpizThto/zLbhPRyvJkagJfWgXI0j+jjKZj+w +sy1V8S44aXJ3GX9p4d/Grnx6WGvEJSV0na7m3YQCPEi5sUgr+EMizGUYstSSUPtU +XhxQRZ95K2cKORul9vzG3zZqqvi73Ju5vu9DLmmlI00sLzyVGFtvkuhrF2p7XclM +GU/rMOeMClMb6qyCzldSs84Anhlh/6mYri6uYPhIGvxqtH44FTbu1APvZp0s2rVm +ueClHG78lat+oqWFpbA8+peT0dMPdSKDAFDiHsGoeWCIoCF44a84bJX35OZk+Y4a ++fDFuSiKYBMfAgqf/ZNzV4+ySka7dWdRQ2TDgIuxnvFV1NgC/ir3/mPgkf0xZU5d +w8T+TW6T8PmJfHnW4nxgHaqgxMoEoPm8zn0HNpRFKwsDYRFfobpCXnoyx50JXxa4 +jg095zlp8X0JwconlGJB1gfeqvS2I50WEDR+2ZtDf7fUEnQ3LYJzP4lSwiSKiQsQ +MPjy0SMQnqmWijylLYKunTl3Uh2DdYg4MOON662H3TxQW8TCYwK2maKujwS9VFLN +GtRGlLrOtrOfHBSwDCujFjqEmQBsF/y2C6XfMoNq6xi5NzREGmNXYrHbLvl2Njwm +WB1ouB4JzmEmb1QNwxkllBAaUp1SJGhW2+fYOe0zjWOP9R4sUq4rRw== +-----END RSA PRIVATE KEY-----