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)
This commit is contained in:
Ioannis Kakavas 2020-12-23 17:29:43 +02:00 committed by GitHub
parent ed43be8c42
commit abaf81e37e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 35 additions and 3 deletions

View file

@ -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<Certificate> readCertificates(Collection<Path> certPaths) throws CertificateException, IOException {
public static List<Certificate> readCertificates(Collection<Path> certPaths) throws CertificateException, IOException {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
List<Certificate> certificates = new ArrayList<>(certPaths.size());
for (Path path : certPaths) {

View file

@ -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}"

View file

@ -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);
@ -1024,6 +1038,23 @@ public abstract class ESRestTestCase extends ESTestCase {
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);
}
}
Map<String, String> headers = ThreadContext.buildDefaultHeaders(settings);
Header[] defaultHeaders = new Header[headers.size()];
int i = 0;