diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 3c01e73159b2c..ab173f0c43bb7 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -230,6 +230,8 @@ if (project != rootProject) { forbiddenPatterns { exclude '**/*.wav' exclude '**/*.p12' + exclude '**/*.jks' + exclude '**/*.crt' // the file that actually defines nocommit exclude '**/ForbiddenPatternsTask.java' exclude '**/*.bcfks' diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/TestWithSslPlugin.java b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/TestWithSslPlugin.java new file mode 100644 index 0000000000000..6417ef50dbeb9 --- /dev/null +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/test/TestWithSslPlugin.java @@ -0,0 +1,88 @@ +/* + * 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.gradle.test; + +import org.elasticsearch.gradle.ExportElasticsearchBuildResourcesTask; +import org.elasticsearch.gradle.precommit.ForbiddenPatternsTask; +import org.elasticsearch.gradle.testclusters.ElasticsearchCluster; +import org.elasticsearch.gradle.testclusters.RestTestRunnerTask; +import org.elasticsearch.gradle.testclusters.TestClustersAware; +import org.elasticsearch.gradle.testclusters.TestClustersPlugin; +import org.elasticsearch.gradle.util.Util; +import org.gradle.api.NamedDomainObjectContainer; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.TaskProvider; + +import java.io.File; + +public class TestWithSslPlugin implements Plugin { + + @Override + public void apply(Project project) { + File keyStoreDir = new File(project.getBuildDir(), "keystore"); + TaskProvider exportKeyStore = project.getTasks() + .register("copyTestCertificates", ExportElasticsearchBuildResourcesTask.class, (t) -> { + t.copy("test/ssl/test-client.crt"); + t.copy("test/ssl/test-client.jks"); + t.copy("test/ssl/test-node.crt"); + t.copy("test/ssl/test-node.jks"); + t.setOutputDir(keyStoreDir); + }); + + project.getPlugins().withType(StandaloneRestTestPlugin.class).configureEach(restTestPlugin -> { + SourceSet testSourceSet = Util.getJavaTestSourceSet(project).get(); + testSourceSet.getResources().srcDir(new File(keyStoreDir, "test/ssl")); + testSourceSet.compiledBy(exportKeyStore); + + project.getTasks().withType(TestClustersAware.class).configureEach(clusterAware -> clusterAware.dependsOn(exportKeyStore)); + + // Tell the tests we're running with ssl enabled + project.getTasks() + .withType(RestTestRunnerTask.class) + .configureEach(runner -> runner.systemProperty("tests.ssl.enabled", "true")); + }); + + project.getPlugins().withType(TestClustersPlugin.class).configureEach(clustersPlugin -> { + File keystoreDir = new File(project.getBuildDir(), "keystore/test/ssl"); + File nodeKeystore = new File(keystoreDir, "test-node.jks"); + File clientKeyStore = new File(keystoreDir, "test-client.jks"); + NamedDomainObjectContainer clusters = (NamedDomainObjectContainer) project + .getExtensions() + .getByName(TestClustersPlugin.EXTENSION_NAME); + clusters.all(c -> { + // ceremony to set up ssl + c.setting("xpack.security.transport.ssl.keystore.path", "test-node.jks"); + c.setting("xpack.security.http.ssl.keystore.path", "test-node.jks"); + c.keystore("xpack.security.transport.ssl.keystore.secure_password", "keypass"); + c.keystore("xpack.security.http.ssl.keystore.secure_password", "keypass"); + + // copy keystores & certs into config/ + c.extraConfigFile(nodeKeystore.getName(), nodeKeystore); + c.extraConfigFile(clientKeyStore.getName(), clientKeyStore); + }); + }); + + project.getTasks() + .withType(ForbiddenPatternsTask.class) + .configureEach(forbiddenPatternTask -> forbiddenPatternTask.exclude("**/*.crt")); + } +} diff --git a/buildSrc/src/main/java/org/elasticsearch/gradle/network/SanEvaluator.java b/buildSrc/src/main/java/org/elasticsearch/gradle/network/SanEvaluator.java deleted file mode 100644 index 06fb04939068d..0000000000000 --- a/buildSrc/src/main/java/org/elasticsearch/gradle/network/SanEvaluator.java +++ /dev/null @@ -1,200 +0,0 @@ -package org.elasticsearch.gradle.network; - -import java.io.IOException; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -/** - * A lazy evaluator to find the san to use for certificate generation. - */ -public class SanEvaluator { - - private static String san = null; - - public String toString() { - synchronized (SanEvaluator.class) { - if (san == null) { - san = getSubjectAlternativeNameString(); - } - } - return san; - } - - // Code stolen from NetworkUtils/InetAddresses/NetworkAddress to support SAN - - /** - * Return all interfaces (and subinterfaces) on the system - */ - private static List getInterfaces() throws SocketException { - List all = new ArrayList<>(); - addAllInterfaces(all, Collections.list(NetworkInterface.getNetworkInterfaces())); - Collections.sort(all, new Comparator() { - @Override - public int compare(NetworkInterface left, NetworkInterface right) { - return Integer.compare(left.getIndex(), right.getIndex()); - } - }); - return all; - } - - /** - * Helper for getInterfaces, recursively adds subinterfaces to {@code target} - */ - private static void addAllInterfaces(List target, List level) { - if (!level.isEmpty()) { - target.addAll(level); - for (NetworkInterface intf : level) { - addAllInterfaces(target, Collections.list(intf.getSubInterfaces())); - } - } - } - - private static String getSubjectAlternativeNameString() { - List list = new ArrayList<>(); - try { - - for (NetworkInterface intf : getInterfaces()) { - for (final InetAddress address : Collections.list(intf.getInetAddresses())) { - /* - * Some OS (e.g., BSD) assign a link-local address to the loopback interface. - * While technically not a loopback interface, some of these OS treat them as one (e.g., localhost on macOS), - * so we must too. Otherwise, things just won't work out of the box. So we include all addresses from - * loopback interfaces. - * - * By checking if the interface is a loopback interface or the address is a loopback address first, - * we avoid having to check if the interface is up unless necessary. - * This means we can avoid checking if the interface is up for virtual ethernet devices which have - * a tendency to disappear outside of our control (e.g., due to Docker). - */ - if ((intf.isLoopback() || address.isLoopbackAddress()) && isUp(intf, address)) { - list.add(address); - } - } - } - if (list.isEmpty()) { - throw new IllegalArgumentException("no up-and-running loopback addresses found, got " + getInterfaces()); - } - - StringBuilder builder = new StringBuilder("san="); - for (int i = 0; i < list.size(); i++) { - InetAddress address = list.get(i); - String hostAddress; - if (address instanceof Inet6Address) { - hostAddress = compressedIPV6Address((Inet6Address) address); - } else { - hostAddress = address.getHostAddress(); - } - builder.append("ip:").append(hostAddress); - String hostname = address.getHostName(); - if (hostname.equals(address.getHostAddress()) == false) { - builder.append(",dns:").append(hostname); - } - - if (i != (list.size() - 1)) { - builder.append(","); - } - } - - return builder.toString(); - } catch (IOException e) { - throw new IllegalStateException("Cannot resolve alternative name string", e); - } - } - - private static boolean isUp(final NetworkInterface intf, final InetAddress address) throws IOException { - try { - return intf.isUp(); - } catch (final SocketException e) { - /* - * In Elasticsearch production code (NetworkUtils) we suppress this if the device is a virtual ethernet device. - * That should not happen here since the interface must be a loopback device or the address a loopback address - * to get here to begin with. - */ - assert intf.isLoopback() || address.isLoopbackAddress(); - throw new IOException("failed to check if interface [" + intf.getName() + "] is up", e); - } - } - - private static String compressedIPV6Address(Inet6Address inet6Address) { - byte[] bytes = inet6Address.getAddress(); - int[] hextets = new int[8]; - for (int i = 0; i < hextets.length; i++) { - hextets[i] = (bytes[2 * i] & 255) << 8 | bytes[2 * i + 1] & 255; - } - compressLongestRunOfZeroes(hextets); - return hextetsToIPv6String(hextets); - } - - /** - * Identify and mark the longest run of zeroes in an IPv6 address. - * - *

Only runs of two or more hextets are considered. In case of a tie, the - * leftmost run wins. If a qualifying run is found, its hextets are replaced - * by the sentinel value -1. - * - * @param hextets {@code int[]} mutable array of eight 16-bit hextets - */ - private static void compressLongestRunOfZeroes(int[] hextets) { - int bestRunStart = -1; - int bestRunLength = -1; - int runStart = -1; - for (int i = 0; i < hextets.length + 1; i++) { - if (i < hextets.length && hextets[i] == 0) { - if (runStart < 0) { - runStart = i; - } - } else if (runStart >= 0) { - int runLength = i - runStart; - if (runLength > bestRunLength) { - bestRunStart = runStart; - bestRunLength = runLength; - } - runStart = -1; - } - } - if (bestRunLength >= 2) { - Arrays.fill(hextets, bestRunStart, bestRunStart + bestRunLength, -1); - } - } - - /** - * Convert a list of hextets into a human-readable IPv6 address. - * - *

In order for "::" compression to work, the input should contain negative - * sentinel values in place of the elided zeroes. - * - * @param hextets {@code int[]} array of eight 16-bit hextets, or -1s - */ - private static String hextetsToIPv6String(int[] hextets) { - /* - * While scanning the array, handle these state transitions: - * start->num => "num" start->gap => "::" - * num->num => ":num" num->gap => "::" - * gap->num => "num" gap->gap => "" - */ - StringBuilder buf = new StringBuilder(39); - boolean lastWasNumber = false; - for (int i = 0; i < hextets.length; i++) { - boolean thisIsNumber = hextets[i] >= 0; - if (thisIsNumber) { - if (lastWasNumber) { - buf.append(':'); - } - buf.append(Integer.toHexString(hextets[i])); - } else { - if (i == 0 || lastWasNumber) { - buf.append("::"); - } - } - lastWasNumber = thisIsNumber; - } - return buf.toString(); - } -} diff --git a/buildSrc/src/main/java/org/elasticsearch/gradle/testclusters/TestClustersAware.java b/buildSrc/src/main/java/org/elasticsearch/gradle/testclusters/TestClustersAware.java index b55629717fd58..4ce14519e7a07 100644 --- a/buildSrc/src/main/java/org/elasticsearch/gradle/testclusters/TestClustersAware.java +++ b/buildSrc/src/main/java/org/elasticsearch/gradle/testclusters/TestClustersAware.java @@ -7,7 +7,7 @@ import java.util.Collection; import java.util.concurrent.Callable; -interface TestClustersAware extends Task { +public interface TestClustersAware extends Task { @Nested Collection getClusters(); diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/elasticsearch.test-with-ssl.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/elasticsearch.test-with-ssl.properties new file mode 100644 index 0000000000000..ec109acb5eb67 --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/elasticsearch.test-with-ssl.properties @@ -0,0 +1,20 @@ +# +# 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. +# + +implementation-class=org.elasticsearch.gradle.test.TestWithSslPlugin diff --git a/buildSrc/src/main/resources/test/ssl/README.md b/buildSrc/src/main/resources/test/ssl/README.md new file mode 100644 index 0000000000000..efe154a4d777b --- /dev/null +++ b/buildSrc/src/main/resources/test/ssl/README.md @@ -0,0 +1,20 @@ +This directory contains test certificates used for testing ssl handling. + +These keystores and certificates can be used via applying the `elasticsearch.test-with-ssl` plugin. + +The generated certificates are valid till 05. Jun 2030. + +The certificates are generated using catch-all SAN in the following procedure: + +1. Generate the node's keystore: + `keytool -genkey -alias test-node -keystore test-node.jks -keyalg RSA -keysize 2048 -validity 3654 -dname CN="Elasticsearch Build Test Infrastructure" -keypass keypass -storepass keypass -ext san=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` +2. Generate the client's keystore: + `keytool -genkey -alias test-client -keystore test-client.jks -keyalg RSA -keysize 2048 -validity 3654 -dname CN="Elasticsearch Build Test Infrastructure" -keypass keypass -storepass keypass -ext san=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` +3. Export the node's certificate: + `keytool -export -alias test-node -keystore test-node.jks -storepass keypass -file test-node.crt` +4. Import the node certificate in the client's keystore: + `keytool -import -alias test-node -keystore test-client.jks -storepass keypass -file test-node.crt -noprompt` +5. Export the client's certificate: + `keytool -export -alias test-client -keystore test-client.jks -storepass keypass -file test-client.crt` +6. Import the client certificate in the node's keystore: + `keytool -import -alias test-client -keystore test-node.jks -storepass keypass -file test-client.crt -noprompt` diff --git a/buildSrc/src/main/resources/test/ssl/test-client.crt b/buildSrc/src/main/resources/test/ssl/test-client.crt new file mode 100644 index 0000000000000..5459c2a0b0707 Binary files /dev/null and b/buildSrc/src/main/resources/test/ssl/test-client.crt differ diff --git a/buildSrc/src/main/resources/test/ssl/test-client.jks b/buildSrc/src/main/resources/test/ssl/test-client.jks new file mode 100644 index 0000000000000..e2ed37ec7cf03 Binary files /dev/null and b/buildSrc/src/main/resources/test/ssl/test-client.jks differ diff --git a/buildSrc/src/main/resources/test/ssl/test-node.crt b/buildSrc/src/main/resources/test/ssl/test-node.crt new file mode 100644 index 0000000000000..c2a27e3544875 Binary files /dev/null and b/buildSrc/src/main/resources/test/ssl/test-node.crt differ diff --git a/buildSrc/src/main/resources/test/ssl/test-node.jks b/buildSrc/src/main/resources/test/ssl/test-node.jks new file mode 100644 index 0000000000000..59e995321a943 Binary files /dev/null and b/buildSrc/src/main/resources/test/ssl/test-node.jks differ diff --git a/x-pack/plugin/sql/qa/jdbc/security/with-ssl/build.gradle b/x-pack/plugin/sql/qa/jdbc/security/with-ssl/build.gradle index d51bf7b2c48ff..bf584e78c31fe 100644 --- a/x-pack/plugin/sql/qa/jdbc/security/with-ssl/build.gradle +++ b/x-pack/plugin/sql/qa/jdbc/security/with-ssl/build.gradle @@ -1,168 +1,8 @@ -import org.elasticsearch.gradle.LoggedExec -import org.elasticsearch.gradle.network.SanEvaluator -import org.elasticsearch.gradle.info.BuildParams -import org.gradle.internal.jvm.Jvm - -// Tell the tests we're running with ssl enabled -integTest.runner { - systemProperty 'tests.ssl.enabled', 'true' -} - -// needed to be consistent with ssl host checking -Object san = new SanEvaluator() - -// needed to be consistent with ssl host checking -String host = InetAddress.getLoopbackAddress().getHostAddress(); - -// location of generated keystores and certificates -File keystoreDir = new File(project.buildDir, 'keystore') - -// Generate the node's keystore -File nodeKeystore = file("$keystoreDir/test-node.jks") -tasks.register("createNodeKeyStore", LoggedExec) { - doFirst { - if (nodeKeystore.parentFile.exists() == false) { - nodeKeystore.parentFile.mkdirs() - } - if (nodeKeystore.exists()) { - delete nodeKeystore - } - } - executable = "${Jvm.current().javaHome}/bin/keytool" - standardInput = new ByteArrayInputStream('FirstName LastName\nUnit\nOrganization\nCity\nState\nNL\nyes\n\n'.getBytes('UTF-8')) - args '-genkey', - '-alias', 'test-node', - '-keystore', nodeKeystore, - '-keyalg', 'RSA', - '-keysize', '2048', - '-validity', '712', - '-dname', 'CN=' + host, - '-keypass', 'keypass', - '-storepass', 'keypass', - '-ext', san.toString() -} - -// Generate the client's keystore -File clientKeyStore = file("$keystoreDir/test-client.jks") -tasks.register("createClientKeyStore", LoggedExec) { - doFirst { - if (clientKeyStore.parentFile.exists() == false) { - clientKeyStore.parentFile.mkdirs() - } - if (clientKeyStore.exists()) { - delete clientKeyStore - } - } - executable = "${BuildParams.runtimeJavaHome}/bin/keytool" - standardInput = new ByteArrayInputStream('FirstName LastName\nUnit\nOrganization\nCity\nState\nNL\nyes\n\n'.getBytes('UTF-8')) - args '-genkey', - '-alias', 'test-client', - '-keystore', clientKeyStore, - '-keyalg', 'RSA', - '-keysize', '2048', - '-validity', '712', - '-dname', 'CN=' + host, - '-keypass', 'keypass', - '-storepass', 'keypass', - '-ext', san.toString() -} - -// Export the node's certificate -File nodeCertificate = file("$keystoreDir/test-node.cert") -tasks.register("exportNodeCertificate", LoggedExec) { - dependsOn "createNodeKeyStore" - doFirst { - if (nodeCertificate.parentFile.exists() == false) { - nodeCertificate.parentFile.mkdirs() - } - if (nodeCertificate.exists()) { - delete nodeCertificate - } - } - executable = "${BuildParams.runtimeJavaHome}/bin/keytool" - args '-export', - '-alias', 'test-node', - '-keystore', nodeKeystore, - '-storepass', 'keypass', - '-file', nodeCertificate -} - -// Import the node certificate in the client's keystore -tasks.register("importNodeCertificateInClientKeyStore", LoggedExec) { - dependsOn "createClientKeyStore", "exportNodeCertificate" - executable = "${BuildParams.runtimeJavaHome}/bin/keytool" - args '-import', - '-alias', 'test-node', - '-keystore', clientKeyStore, - '-storepass', 'keypass', - '-file', nodeCertificate, - '-noprompt' -} - -// Export the client's certificate -File clientCertificate = file("$keystoreDir/test-client.cert") -tasks.register("exportClientCertificate", LoggedExec) { - dependsOn "createClientKeyStore" - doFirst { - if (clientCertificate.parentFile.exists() == false) { - clientCertificate.parentFile.mkdirs() - } - if (clientCertificate.exists()) { - delete clientCertificate - } - } - executable = "${BuildParams.runtimeJavaHome}/bin/keytool" - args '-export', - '-alias', 'test-client', - '-keystore', clientKeyStore, - '-storepass', 'keypass', - '-file', clientCertificate -} - -// Import the client certificate in the node's keystore -tasks.register("importClientCertificateInNodeKeyStore", LoggedExec) { - dependsOn "createNodeKeyStore", "exportClientCertificate" - executable = "${BuildParams.runtimeJavaHome}/bin/keytool" - args '-import', - '-alias', 'test-client', - '-keystore', nodeKeystore, - '-storepass', 'keypass', - '-file', clientCertificate, - '-noprompt' -} - -forbiddenPatterns { - exclude '**/*.cert' -} - -// Add keystores to test classpath: it expects it there -sourceSets.test.resources.srcDir(keystoreDir) -processTestResources.dependsOn("importNodeCertificateInClientKeyStore", "importClientCertificateInNodeKeyStore") - -integTest.runner { - dependsOn("importClientCertificateInNodeKeyStore") - onlyIf { - // Do not attempt to form a cluster in a FIPS JVM, as doing so with a JKS keystore will fail. - // TODO Revisit this when SQL CLI client can handle key/certificate instead of only Keystores. - // https://github.com/elastic/elasticsearch/issues/32306 - BuildParams.inFipsJvm == false - } -} +apply plugin: 'elasticsearch.test-with-ssl' testClusters.integTest { - // The setup that we actually want - setting 'xpack.license.self_generated.type', 'trial' - setting 'xpack.security.http.ssl.enabled', 'true' - setting 'xpack.security.transport.ssl.enabled', 'true' - - // ceremony to set up ssl - setting 'xpack.security.transport.ssl.keystore.path', 'test-node.jks' - setting 'xpack.security.http.ssl.keystore.path', 'test-node.jks' - keystore 'xpack.security.transport.ssl.keystore.secure_password', 'keypass' - keystore 'xpack.security.http.ssl.keystore.secure_password', 'keypass' - - - // copy keystores into config/ - extraConfigFile nodeKeystore.name, nodeKeystore - extraConfigFile clientKeyStore.name, clientKeyStore + // The setup that we actually want + setting 'xpack.license.self_generated.type', 'trial' + setting 'xpack.security.http.ssl.enabled', 'true' + setting 'xpack.security.transport.ssl.enabled', 'true' } diff --git a/x-pack/plugin/sql/qa/server/security/with-ssl/build.gradle b/x-pack/plugin/sql/qa/server/security/with-ssl/build.gradle index 8cfecfc8c5285..8d5d5f4b1defe 100644 --- a/x-pack/plugin/sql/qa/server/security/with-ssl/build.gradle +++ b/x-pack/plugin/sql/qa/server/security/with-ssl/build.gradle @@ -1,143 +1,8 @@ -import org.elasticsearch.gradle.LoggedExec -import org.elasticsearch.gradle.network.SanEvaluator import org.elasticsearch.gradle.info.BuildParams -import org.gradle.internal.jvm.Jvm -// Tell the tests we're running with ssl enabled -integTest.runner { - systemProperty 'tests.ssl.enabled', 'true' -} - -// needed to be consistent with ssl host checking -Object san = new SanEvaluator() - -// location of generated keystores and certificates -File keystoreDir = new File(project.buildDir, 'keystore') - -// Generate the node's keystore -File nodeKeystore = file("$keystoreDir/test-node.jks") -tasks.register("createNodeKeyStore", LoggedExec) { - doFirst { - if (nodeKeystore.parentFile.exists() == false) { - nodeKeystore.parentFile.mkdirs() - } - if (nodeKeystore.exists()) { - delete nodeKeystore - } - } - executable = "${Jvm.current().javaHome}/bin/keytool" - standardInput = new ByteArrayInputStream('FirstName LastName\nUnit\nOrganization\nCity\nState\nNL\nyes\n\n'.getBytes('UTF-8')) - args '-genkey', - '-alias', 'test-node', - '-keystore', nodeKeystore, - '-keyalg', 'RSA', - '-keysize', '2048', - '-validity', '712', - '-dname', 'CN=smoke-test-plugins-ssl', - '-keypass', 'keypass', - '-storepass', 'keypass', - '-ext', san.toString() -} - -// Generate the client's keystore -File clientKeyStore = file("$keystoreDir/test-client.jks") -tasks.register("createClientKeyStore", LoggedExec) { - doFirst { - if (clientKeyStore.parentFile.exists() == false) { - clientKeyStore.parentFile.mkdirs() - } - if (clientKeyStore.exists()) { - delete clientKeyStore - } - } - executable = "${BuildParams.runtimeJavaHome}/bin/keytool" - standardInput = new ByteArrayInputStream('FirstName LastName\nUnit\nOrganization\nCity\nState\nNL\nyes\n\n'.getBytes('UTF-8')) - args '-genkey', - '-alias', 'test-client', - '-keystore', clientKeyStore, - '-keyalg', 'RSA', - '-keysize', '2048', - '-validity', '712', - '-dname', 'CN=smoke-test-plugins-ssl', - '-keypass', 'keypass', - '-storepass', 'keypass', - '-ext', san.toString() -} - -// Export the node's certificate -File nodeCertificate = file("$keystoreDir/test-node.cert") -tasks.register("exportNodeCertificate", LoggedExec) { - dependsOn "createNodeKeyStore" - doFirst { - if (nodeCertificate.parentFile.exists() == false) { - nodeCertificate.parentFile.mkdirs() - } - if (nodeCertificate.exists()) { - delete nodeCertificate - } - } - executable = "${BuildParams.runtimeJavaHome}/bin/keytool" - args '-export', - '-alias', 'test-node', - '-keystore', nodeKeystore, - '-storepass', 'keypass', - '-file', nodeCertificate -} - -// Import the node certificate in the client's keystore -tasks.register("importNodeCertificateInClientKeyStore", LoggedExec) { - dependsOn "createClientKeyStore", "exportNodeCertificate" - executable = "${BuildParams.runtimeJavaHome}/bin/keytool" - args '-import', - '-alias', 'test-node', - '-keystore', clientKeyStore, - '-storepass', 'keypass', - '-file', nodeCertificate, - '-noprompt' -} - -// Export the client's certificate -File clientCertificate = file("$keystoreDir/test-client.cert") -tasks.register("exportClientCertificate", LoggedExec) { - dependsOn "createClientKeyStore" - doFirst { - if (clientCertificate.parentFile.exists() == false) { - clientCertificate.parentFile.mkdirs() - } - if (clientCertificate.exists()) { - delete clientCertificate - } - } - executable = "${BuildParams.runtimeJavaHome}/bin/keytool" - args '-export', - '-alias', 'test-client', - '-keystore', clientKeyStore, - '-storepass', 'keypass', - '-file', clientCertificate -} - -// Import the client certificate in the node's keystore -tasks.register("importClientCertificateInNodeKeyStore", LoggedExec) { - dependsOn "createNodeKeyStore", "exportClientCertificate" - executable = "${BuildParams.runtimeJavaHome}/bin/keytool" - args '-import', - '-alias', 'test-client', - '-keystore', nodeKeystore, - '-storepass', 'keypass', - '-file', clientCertificate, - '-noprompt' -} - -forbiddenPatterns { - exclude '**/*.cert' -} - -// Add keystores to test classpath: it expects it there -sourceSets.test.resources.srcDir(keystoreDir) -processTestResources.dependsOn("importNodeCertificateInClientKeyStore", "importClientCertificateInNodeKeyStore") +apply plugin: 'elasticsearch.test-with-ssl' integTest.runner { - dependsOn(importClientCertificateInNodeKeyStore) onlyIf { // Do not attempt to form a cluster in a FIPS JVM, as doing so with a JKS keystore will fail. // TODO Revisit this when SQL CLI client can handle key/certificate instead of only Keystores. @@ -151,15 +16,4 @@ testClusters.integTest { setting 'xpack.license.self_generated.type', 'trial' setting 'xpack.security.http.ssl.enabled', 'true' setting 'xpack.security.transport.ssl.enabled', 'true' - - // ceremony to set up ssl - setting 'xpack.security.transport.ssl.keystore.path', 'test-node.jks' - setting 'xpack.security.http.ssl.keystore.path', 'test-node.jks' - keystore 'xpack.security.transport.ssl.keystore.secure_password', 'keypass' - keystore 'xpack.security.http.ssl.keystore.secure_password', 'keypass' - - - // copy keystores into config/ - extraConfigFile nodeKeystore.name, nodeKeystore - extraConfigFile clientKeyStore.name, clientKeyStore }