From abaf81e37ec8c26f3a78539dc00c1a7ecfa6c921 Mon Sep 17 00:00:00 2001 From: Ioannis Kakavas Date: Wed, 23 Dec 2020 17:29:43 +0200 Subject: [PATCH] Allow ESRestTestClient to trust certificates (#66559) ESRestTestCase rest clients could only be configured to trust the certificate authorities that were contained in a truststore. In certain cases (like in fips mode where JKS/PKCS12 keystores) cannot be used, it's beneficial to be able to trust specific certificate authorities (indicated by the CA PEM endoded certificate) --- .../elasticsearch/common/ssl/PemUtils.java | 4 +-- test/framework/build.gradle | 1 + .../test/rest/ESRestTestCase.java | 33 ++++++++++++++++++- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java index 17046b89c376..e67dac393e96 100644 --- a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java +++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java @@ -62,7 +62,7 @@ import java.util.List; import java.util.Map; import java.util.function.Supplier; -final class PemUtils { +public final class PemUtils { private static final String PKCS1_HEADER = "-----BEGIN RSA PRIVATE KEY-----"; private static final String PKCS1_FOOTER = "-----END RSA PRIVATE KEY-----"; @@ -589,7 +589,7 @@ final class PemUtils { "] is not żsupported"); } - static List readCertificates(Collection certPaths) throws CertificateException, IOException { + public static List readCertificates(Collection certPaths) throws CertificateException, IOException { CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); List certificates = new ArrayList<>(certPaths.size()); for (Path path : certPaths) { diff --git a/test/framework/build.gradle b/test/framework/build.gradle index 8dc363c7a3c6..86db34008075 100644 --- a/test/framework/build.gradle +++ b/test/framework/build.gradle @@ -25,6 +25,7 @@ dependencies { api project(":client:rest") api project(":client:sniffer") api project(':libs:elasticsearch-nio') + api project(':libs:elasticsearch-ssl-config') api project(":server") api project(":libs:elasticsearch-cli") api "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}" diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java index d7da35c1f5f0..e36fee235204 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java @@ -46,6 +46,7 @@ import org.elasticsearch.common.CheckedRunnable; import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.PathUtils; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.ssl.PemUtils; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.util.set.Sets; @@ -81,6 +82,7 @@ import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Arrays; @@ -114,6 +116,7 @@ import static org.hamcrest.Matchers.notNullValue; public abstract class ESRestTestCase extends ESTestCase { public static final String TRUSTSTORE_PATH = "truststore.path"; public static final String TRUSTSTORE_PASSWORD = "truststore.password"; + public static final String CERTIFICATE_AUTHORITIES = "certificate_authorities"; public static final String CLIENT_SOCKET_TIMEOUT = "client.socket.timeout"; public static final String CLIENT_PATH_PREFIX = "client.path.prefix"; @@ -1001,8 +1004,19 @@ public abstract class ESRestTestCase extends ESTestCase { } protected static void configureClient(RestClientBuilder builder, Settings settings) throws IOException { + String certificateAuthorities = settings.get(CERTIFICATE_AUTHORITIES); String keystorePath = settings.get(TRUSTSTORE_PATH); + + if (certificateAuthorities != null && keystorePath != null) { + throw new IllegalStateException("Cannot set both " + CERTIFICATE_AUTHORITIES + " and " + TRUSTSTORE_PATH + + ". Please configure one of these."); + + } if (keystorePath != null) { + if (inFipsJvm()) { + throw new IllegalStateException("Keystore " + keystorePath + "cannot be used in FIPS 140 mode. Please configure " + + CERTIFICATE_AUTHORITIES + " with a PEM encoded trusted CA/certificate instead"); + } final String keystorePass = settings.get(TRUSTSTORE_PASSWORD); if (keystorePass == null) { throw new IllegalStateException(TRUSTSTORE_PATH + " is provided but not " + TRUSTSTORE_PASSWORD); @@ -1020,7 +1034,24 @@ public abstract class ESRestTestCase extends ESTestCase { SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(keyStore, null).build(); SSLIOSessionStrategy sessionStrategy = new SSLIOSessionStrategy(sslcontext); builder.setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setSSLStrategy(sessionStrategy)); - } catch (KeyStoreException |NoSuchAlgorithmException |KeyManagementException |CertificateException e) { + } catch (KeyStoreException | NoSuchAlgorithmException | KeyManagementException | CertificateException e) { + throw new RuntimeException("Error setting up ssl", e); + } + } + if (certificateAuthorities != null) { + Path path = PathUtils.get(certificateAuthorities); + if (!Files.exists(path)) { + throw new IllegalStateException(CERTIFICATE_AUTHORITIES + " is set but points to a non-existing file"); + } + try { + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null, null); + Certificate cert = PemUtils.readCertificates(List.of(path)).get(0); + keyStore.setCertificateEntry(cert.toString(), cert); + SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(keyStore, null).build(); + SSLIOSessionStrategy sessionStrategy = new SSLIOSessionStrategy(sslcontext); + builder.setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setSSLStrategy(sessionStrategy)); + } catch (KeyStoreException | NoSuchAlgorithmException | KeyManagementException | CertificateException e) { throw new RuntimeException("Error setting up ssl", e); } }