From a12171a6d471609703e14526a4491e698c19404f Mon Sep 17 00:00:00 2001 From: Peter Nied Date: Wed, 26 Jul 2023 20:19:29 +0000 Subject: [PATCH] Hacking together BWC tests that use SSL, doesn't work Signed-off-by: Peter Nied --- bwc-test/build.gradle | 4 +- bwc-test/src/test/java/ConfigConstants.java | 58 ++++ .../test/java/SecureRestClientBuilder.java | 323 ++++++++++++++++++ .../SecurityBackwardsCompatibilityIT.java | 84 ++++- bwc-test/src/test/java/TrustStore.java | 68 ++++ 5 files changed, 528 insertions(+), 9 deletions(-) create mode 100644 bwc-test/src/test/java/ConfigConstants.java create mode 100644 bwc-test/src/test/java/SecureRestClientBuilder.java create mode 100644 bwc-test/src/test/java/TrustStore.java diff --git a/bwc-test/build.gradle b/bwc-test/build.gradle index 68b9f27e04..13c7f257af 100644 --- a/bwc-test/build.gradle +++ b/bwc-test/build.gradle @@ -125,7 +125,7 @@ def String extractVersion(versionStr) { node.extraConfigFile("esnode.pem", file("src/test/resources/security/esnode.pem")) node.extraConfigFile("esnode-key.pem", file("src/test/resources/security/esnode-key.pem")) node.extraConfigFile("root-ca.pem", file("src/test/resources/security/root-ca.pem")) - node.setting("plugins.security.disabled", "true") + node.setting("plugins.security.disabled", "false") node.setting("plugins.security.ssl.transport.pemcert_filepath", "esnode.pem") node.setting("plugins.security.ssl.transport.pemkey_filepath", "esnode-key.pem") node.setting("plugins.security.ssl.transport.pemtrustedcas_filepath", "root-ca.pem") @@ -137,7 +137,7 @@ def String extractVersion(versionStr) { node.setting("plugins.security.allow_unsafe_democertificates", "true") node.setting("plugins.security.allow_default_init_securityindex", "true") node.setting("plugins.security.authcz.admin_dn", "CN=kirk,OU=client,O=client,L=test,C=de") - node.setting("plugins.security.audit.type", "internal_elasticsearch") + node.setting("plugins.security.audit.type", "internal_opensearch") node.setting("plugins.security.enable_snapshot_restore_privilege", "true") node.setting("plugins.security.check_snapshot_restore_write_privileges", "true") node.setting("plugins.security.restapi.roles_enabled", "[\"all_access\", \"security_rest_api_access\"]") diff --git a/bwc-test/src/test/java/ConfigConstants.java b/bwc-test/src/test/java/ConfigConstants.java new file mode 100644 index 0000000000..5bd7e0e813 --- /dev/null +++ b/bwc-test/src/test/java/ConfigConstants.java @@ -0,0 +1,58 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.commons; + +import org.opensearch.common.settings.SecureSetting; +import org.opensearch.common.settings.Setting; +import org.opensearch.core.common.settings.SecureString; + +public class ConfigConstants { + + public static final String HTTPS = "https"; + public static final String HTTP = "http"; + public static final String HOST_DEFAULT = "localhost"; + public static final String HTTP_PORT = "http.port"; + public static final int HTTP_PORT_DEFAULT = 9200; + public static final String CONTENT_TYPE = "content-type"; + public static final String CONTENT_TYPE_DEFAULT = "application/json"; + public static final String AUTHORIZATION = "Authorization"; + + // These reside in security plugin. + public static final String OPENSEARCH_SECURITY_SSL_HTTP_PEMCERT_FILEPATH = "plugins.security.ssl.http.pemcert_filepath"; + public static final String OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_FILEPATH = "plugins.security.ssl.http.keystore_filepath"; + /** + * @deprecated in favor of the {@link #OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_PASSWORD_SETTING} secure setting + */ + @Deprecated + public static final String OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_PASSWORD = "plugins.security.ssl.http.keystore_password"; + + /** + * @deprecated in favor of the {@link #OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_KEYPASSWORD_SETTING} secure setting + */ + @Deprecated + public static final String OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_KEYPASSWORD = "plugins.security.ssl.http.keystore_keypassword"; + private static final String SECURE_SUFFIX = "_secure"; + + private static Setting createFallbackInsecureSetting(String key) { + return new Setting<>(key, (settings) -> "", (strValue) -> new SecureString(strValue.toCharArray())); + } + + public static final Setting OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_PASSWORD_SETTING = SecureSetting + .secureString( + OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_PASSWORD + SECURE_SUFFIX, + createFallbackInsecureSetting(OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_PASSWORD) + ); + public static final Setting OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_KEYPASSWORD_SETTING = SecureSetting + .secureString( + OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_KEYPASSWORD + SECURE_SUFFIX, + createFallbackInsecureSetting(OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_KEYPASSWORD) + ); + public static final String OPENSEARCH_SECURITY_INJECTED_ROLES = "opendistro_security_injected_roles"; + public static final String INJECTED_USER = "injected_user"; + public static final String OPENSEARCH_SECURITY_USE_INJECTED_USER_FOR_PLUGINS = "plugins.security_use_injected_user_for_plugins"; + public static final String OPENSEARCH_SECURITY_SSL_HTTP_ENABLED = "plugins.security.ssl.http.enabled"; + public static final String OPENSEARCH_SECURITY_USER_INFO_THREAD_CONTEXT = "_opendistro_security_user_info"; +} \ No newline at end of file diff --git a/bwc-test/src/test/java/SecureRestClientBuilder.java b/bwc-test/src/test/java/SecureRestClientBuilder.java new file mode 100644 index 0000000000..d003aa0487 --- /dev/null +++ b/bwc-test/src/test/java/SecureRestClientBuilder.java @@ -0,0 +1,323 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.commons.rest; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.util.ArrayList; +import java.util.Arrays; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; + +import org.apache.hc.client5.http.auth.AuthScope; +import org.apache.hc.client5.http.auth.CredentialsProvider; +import org.apache.hc.client5.http.auth.UsernamePasswordCredentials; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder; +import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider; +import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager; +import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; +import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder; +import org.apache.hc.client5.http.ssl.TrustSelfSignedStrategy; +import org.apache.hc.core5.function.Factory; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.nio.ssl.TlsStrategy; +import org.apache.hc.core5.reactor.ssl.TlsDetails; +import org.apache.hc.core5.ssl.SSLContextBuilder; +import org.apache.hc.core5.util.Timeout; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.OpenSearchException; +import org.opensearch.client.RestClient; +import org.opensearch.client.RestClientBuilder; +import org.opensearch.common.settings.Settings; +import org.opensearch.commons.ConfigConstants; +import org.opensearch.core.common.Strings; + +/** + * Provides builder to create low-level and high-level REST client to make calls to OpenSearch. + * + * Sample usage: + * SecureRestClientBuilder builder = new SecureRestClientBuilder(settings).build() + * RestClient restClient = builder.build(); + * + * Other usage: + * RestClient restClient = new SecureRestClientBuilder("localhost", 9200, false) + * .setUserPassword("admin", "admin") + * .setTrustCerts(trustStorePath) + * .build(); + * + * + * If https is enabled, creates RestClientBuilder using self-signed certificates or passed pem + * as trusted. + * + * If https is not enabled, creates a http based client. + */ +public class SecureRestClientBuilder { + + private final boolean httpSSLEnabled; + private final String user; + private final String passwd; + private final ArrayList hosts = new ArrayList<>(); + + private final Path configPath; + private final Settings settings; + + private int defaultConnectTimeOutMSecs = 5000; + private int defaultSoTimeoutMSecs = 10000; + private int defaultConnRequestTimeoutMSecs = 3 * 60 * 1000; /* 3 mins */ + private int defaultMaxConnPerRoute = RestClientBuilder.DEFAULT_MAX_CONN_PER_ROUTE; + private int defaultMaxConnTotal = RestClientBuilder.DEFAULT_MAX_CONN_TOTAL; + + private static final Logger log = LogManager.getLogger(SecureRestClientBuilder.class); + + /** + * ONLY for integration tests. + * @param host + * @param port + * @param httpSSLEnabled + * @param user + * @param passWord + */ + public SecureRestClientBuilder( + final String host, + final int port, + final boolean httpSSLEnabled, + final String user, + final String passWord + ) { + if (Strings.isNullOrEmpty(user) || Strings.isNullOrEmpty(passWord)) { + throw new IllegalArgumentException("Invalid user or password"); + } + + this.httpSSLEnabled = httpSSLEnabled; + this.user = user; + this.passwd = passWord; + this.settings = Settings.EMPTY; + this.configPath = null; + hosts.add(new HttpHost(httpSSLEnabled ? ConfigConstants.HTTPS : ConfigConstants.HTTP, host, port)); + } + + /** + * ONLY for integration tests. + * @param httpHosts + * @param httpSSLEnabled + * @param user + * @param passWord + */ + public SecureRestClientBuilder(HttpHost[] httpHosts, final boolean httpSSLEnabled, final String user, final String passWord) { + + if (Strings.isNullOrEmpty(user) || Strings.isNullOrEmpty(passWord)) { + throw new IllegalArgumentException("Invalid user or password"); + } + + this.httpSSLEnabled = httpSSLEnabled; + this.user = user; + this.passwd = passWord; + this.settings = Settings.EMPTY; + this.configPath = null; + hosts.addAll(Arrays.asList(httpHosts)); + } + + public SecureRestClientBuilder(Settings settings, Path configPath) { + + this.httpSSLEnabled = settings.getAsBoolean(ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_ENABLED, false); + this.settings = settings; + this.configPath = configPath; + this.user = null; + this.passwd = null; + String host = ConfigConstants.HOST_DEFAULT; + int port = settings.getAsInt(ConfigConstants.HTTP_PORT, ConfigConstants.HTTP_PORT_DEFAULT); + hosts.add(new HttpHost(httpSSLEnabled ? ConfigConstants.HTTPS : ConfigConstants.HTTP, host, port)); + } + + public SecureRestClientBuilder(Settings settings, Path configPath, HttpHost[] httpHosts) { + this.httpSSLEnabled = settings.getAsBoolean(ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_ENABLED, false); + this.settings = settings; + this.configPath = configPath; + this.user = null; + this.passwd = null; + hosts.addAll(Arrays.asList(httpHosts)); + } + + /** + * Creates a low-level Rest client. + * @return + * @throws IOException + */ + public RestClient build() throws IOException { + return createRestClientBuilder().build(); + } + + public SecureRestClientBuilder setConnectTimeout(int timeout) { + this.defaultConnectTimeOutMSecs = timeout; + return this; + } + + public SecureRestClientBuilder setSocketTimeout(int timeout) { + this.defaultSoTimeoutMSecs = timeout; + return this; + } + + public SecureRestClientBuilder setConnectionRequestTimeout(int timeout) { + this.defaultConnRequestTimeoutMSecs = timeout; + return this; + } + + public SecureRestClientBuilder setMaxConnPerRoute(final int maxConnPerRoute) { + this.defaultMaxConnPerRoute = maxConnPerRoute; + return this; + } + + public SecureRestClientBuilder setMaxConnTotal(final int maxConnTotal) { + this.defaultMaxConnTotal = maxConnTotal; + return this; + } + + private RestClientBuilder createRestClientBuilder() throws IOException { + RestClientBuilder builder = RestClient.builder(hosts.toArray(new HttpHost[hosts.size()])); + + builder.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() { + @Override + public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) { + return requestConfigBuilder + .setConnectTimeout(Timeout.ofMilliseconds(defaultConnectTimeOutMSecs)) + .setResponseTimeout(Timeout.ofMilliseconds(defaultSoTimeoutMSecs)) + .setConnectionRequestTimeout(Timeout.ofMilliseconds(defaultConnRequestTimeoutMSecs)); + } + }); + + final SSLContext sslContext; + try { + sslContext = createSSLContext(); + } catch (GeneralSecurityException | IOException ex) { + throw new IOException(ex); + } + final CredentialsProvider credentialsProvider = createCredsProvider(); + builder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() { + @Override + public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) { + if (sslContext != null) { + TlsStrategy tlsStrategy = ClientTlsStrategyBuilder + .create() + .setSslContext(sslContext) + // See please https://issues.apache.org/jira/browse/HTTPCLIENT-2219 + .setTlsDetailsFactory(new Factory() { + @Override + public TlsDetails create(final SSLEngine sslEngine) { + return new TlsDetails(sslEngine.getSession(), sslEngine.getApplicationProtocol()); + } + }) + .build(); + PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder + .create() + .setTlsStrategy(tlsStrategy) + .setMaxConnPerRoute(defaultMaxConnPerRoute) + .setMaxConnTotal(defaultMaxConnTotal) + .build(); + httpClientBuilder.setConnectionManager(connectionManager); + } + if (credentialsProvider != null) { + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + } + return httpClientBuilder; + } + }); + return builder; + } + + private SSLContext createSSLContext() throws IOException, GeneralSecurityException { + SSLContextBuilder builder = new SSLContextBuilder(); + if (httpSSLEnabled) { + // Handle trust store + String pemFile = getTrustPem(); + if (Strings.isNullOrEmpty(pemFile)) { + builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); + } else { + String pem = resolve(pemFile, configPath); + KeyStore trustStore = new TrustStore(pem).create(); + builder.loadTrustMaterial(trustStore, null); + } + + // Handle key store. + KeyStore keyStore = getKeyStore(); + if (keyStore != null) { + builder.loadKeyMaterial(keyStore, getKeystorePasswd().toCharArray()); + } + + } + return builder.build(); + } + + private CredentialsProvider createCredsProvider() { + if (Strings.isNullOrEmpty(user) || Strings.isNullOrEmpty(passwd)) + return null; + + final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(new AuthScope(null, -1), new UsernamePasswordCredentials(user, passwd.toCharArray())); + return credentialsProvider; + } + + private String resolve(final String originalFile, final Path configPath) { + String path = null; + if (originalFile != null && originalFile.length() > 0) { + path = configPath.resolve(originalFile).toAbsolutePath().toString(); + log.debug("Resolved {} to {} against {}", originalFile, path, configPath.toAbsolutePath().toString()); + } + + if (path == null || path.length() == 0) { + throw new OpenSearchException("Empty file path for " + originalFile); + } + + if (Files.isDirectory(Paths.get(path), LinkOption.NOFOLLOW_LINKS)) { + throw new OpenSearchException("Is a directory: " + path + " Expected a file for " + originalFile); + } + + if (!Files.isReadable(Paths.get(path))) { + throw new OpenSearchException( + "Unable to read " + + path + + " (" + + Paths.get(path) + + "). Please make sure this files exists and is readable regarding to permissions. Property: " + + originalFile + ); + } + if ("".equals(path)) { + path = null; + } + return path; + } + + private String getTrustPem() { + return settings.get(ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_PEMCERT_FILEPATH, null); + } + + private String getKeystorePasswd() { + return ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_PASSWORD_SETTING.get(settings).toString(); + } + + private KeyStore getKeyStore() throws IOException, GeneralSecurityException { + KeyStore keyStore = KeyStore.getInstance("jks"); + String keyStoreFile = settings.get(ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_FILEPATH, null); + String passwd = ConfigConstants.OPENSEARCH_SECURITY_SSL_HTTP_KEYSTORE_PASSWORD_SETTING.get(settings).toString(); + if (Strings.isNullOrEmpty(keyStoreFile) || Strings.isNullOrEmpty(passwd)) { + return null; + } + String keyStorePath = resolve(keyStoreFile, configPath); + try (InputStream is = Files.newInputStream(Paths.get(keyStorePath))) { + keyStore.load(is, passwd.toCharArray()); + } + return keyStore; + } +} \ No newline at end of file diff --git a/bwc-test/src/test/java/SecurityBackwardsCompatibilityIT.java b/bwc-test/src/test/java/SecurityBackwardsCompatibilityIT.java index 59c2a26c03..f4e45a0ee2 100644 --- a/bwc-test/src/test/java/SecurityBackwardsCompatibilityIT.java +++ b/bwc-test/src/test/java/SecurityBackwardsCompatibilityIT.java @@ -22,6 +22,22 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItem; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Map; + +import org.apache.hc.core5.http.HttpHost; + +import org.opensearch.client.Request; +import org.opensearch.client.Response; +import org.opensearch.client.RestClient; +import org.opensearch.client.RestClientBuilder; +import org.opensearch.common.io.PathUtils; +import org.opensearch.common.settings.Settings; +import org.opensearch.commons.rest.SecureRestClientBuilder; +import org.opensearch.test.rest.OpenSearchRestTestCase; + public class SecurityBackwardsCompatibilityIT extends OpenSearchRestTestCase { private ClusterType CLUSTER_TYPE; @@ -50,17 +66,61 @@ protected boolean preserveTemplatesUponCompletion() { return true; } + // Many changes from SecurityRestTestCase which replaces the rest client, not sure if this works + // ../src/test/java/org/opensearch/security/sanity/tests/SecurityRestTestCase.java + /** START SecurityRestTestCase */ + private static final String SECURITY_SSL_HTTP_ENABLED = "plugins.security.ssl.http.enabled"; + private static final String SECURITY_SSL_HTTP_CLIENTAUTH_MODE = "plugins.security.ssl.http.clientauth_mode"; + private static final String SECURITY_SSL_HTTP_KEYSTORE_ALIAS = "plugins.security.ssl.http.keystore_alias"; + private static final String SECURITY_SSL_HTTP_KEYSTORE_FILEPATH = "plugins.security.ssl.http.keystore_filepath"; + private static final String SECURITY_SSL_HTTP_PEMKEY_FILEPATH = "plugins.security.ssl.http.pemkey_filepath"; + private static final String SECURITY_SSL_HTTP_PEMCERT_FILEPATH = "plugins.security.ssl.http.pemcert_filepath"; + private static final String SECURITY_SSL_HTTP_PEMTRUSTEDCAS_FILEPATH = "plugins.security.ssl.http.pemtrustedcas_filepath"; + private static final String SECURITY_SSL_HTTP_KEYSTORE_TYPE = "plugins.security.ssl.http.keystore_type"; + private static final String SECURITY_SSL_HTTP_TRUSTSTORE_ALIAS = "plugins.security.ssl.http.truststore_alias"; + private static final String SECURITY_SSL_HTTP_TRUSTSTORE_FILEPATH = "plugins.security.ssl.http.truststore_filepath"; + private static final String SECURITY_SSL_HTTP_TRUSTSTORE_TYPE = "plugins.security.ssl.http.truststore_type"; + @Override - protected final Settings restClientSettings() { + protected Settings restClientSettings() { return Settings.builder() - .put(super.restClientSettings()) - // increase the timeout here to 90 seconds to handle long waits for a green - // cluster health. the waits for green need to be longer than a minute to - // account for delayed shards - .put(OpenSearchRestTestCase.CLIENT_SOCKET_TIMEOUT, "90s") + .put("http.port", 9200) + .put(SECURITY_SSL_HTTP_ENABLED, "true") + .put(SECURITY_SSL_HTTP_PEMCERT_FILEPATH, "opensearch-node.pem") + .put(SECURITY_SSL_HTTP_PEMKEY_FILEPATH, "opensearch-node-key.pem") + .put(SECURITY_SSL_HTTP_PEMTRUSTEDCAS_FILEPATH, "root-ca.pem") + .put(SECURITY_SSL_HTTP_KEYSTORE_FILEPATH, "test-kirk.jks") + .put("plugins.security.ssl.http.keystore_password", "changeit") + .put("plugins.security.ssl.http.keystore_keypassword", "changeit") .build(); } + @Override + protected RestClient buildClient(Settings settings, HttpHost[] hosts) throws IOException { + String keystore = settings.get(SECURITY_SSL_HTTP_KEYSTORE_FILEPATH); + + if (keystore != null) { + // create adminDN (super-admin) client + File file = new File(getClass().getClassLoader().getResource(".").getFile()); + Path configPath = PathUtils.get(file.toURI()).getParent().toAbsolutePath(); + return new SecureRestClientBuilder(settings, configPath).setSocketTimeout(60000) + .setConnectionRequestTimeout(180000) + .build(); + } + + // TODO: These should be part of the test properties + // create client with passed user + String userName = System.getProperty("user"); + String password = System.getProperty("password"); + + return new SecureRestClientBuilder(hosts, true, userName, password).setSocketTimeout(60000) + .setConnectionRequestTimeout(180000) + .build(); + } + + /** END FROM SecurityRestTestCase */ + + public void testBasicBackwardsCompatibility() throws Exception { String round = System.getProperty("tests.rest.bwcsuite_round"); @@ -94,6 +154,17 @@ public static ClusterType parse(String value) { @SuppressWarnings("unchecked") private void assertPluginUpgrade(String uri) throws Exception { + // This was adding in debugging, when there is a failure the node output is saved + // Otherwise, manual inspection of the log files is recommend + // ./security/bwc-test/build/testclusters/securityBwcCluster1-0/logs/opensearch.stdout.log + // ./security/bwc-test/build/testclusters/securityBwcCluster1-1/logs/opensearch.stdout.log + // ./security/bwc-test/build/testclusters/securityBwcCluster1-2/logs/opensearch.stdout.log + // TODO: Make an issue about capturing the output from these cases better, even when they pass. + fail("Testing output from typical run"); + + + // As written this test isn't using a user to make the call to _nodes, maybe as part of setup this is + // handled, but we need a way to switch between different user accounts during the test. Map> responseMap = (Map>) getAsMap(uri).get("nodes"); for (Map response : responseMap.values()) { List> plugins = (List>) response.get("plugins"); @@ -106,7 +177,6 @@ private void assertPluginUpgrade(String uri) throws Exception { } else { assertThat(pluginNames, hasItem("opensearch-security")); } - } } } diff --git a/bwc-test/src/test/java/TrustStore.java b/bwc-test/src/test/java/TrustStore.java new file mode 100644 index 0000000000..6dad07bf6b --- /dev/null +++ b/bwc-test/src/test/java/TrustStore.java @@ -0,0 +1,68 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.commons.rest; + +import java.io.FileInputStream; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Collection; + +/** + * Helper class to read raw pem files to keystore. + */ +public class TrustStore { + + private final String effectiveKeyAlias = "al"; + private final String storeType = "JKS"; + private final String certType = "X.509"; + private final String cert; + + public TrustStore(final String file) { + cert = file; + } + + public KeyStore create() throws IOException, GeneralSecurityException { + X509Certificate[] trustCerts = loadCertificatesFromFile(cert); + return toTrustStore(effectiveKeyAlias, trustCerts); + } + + private X509Certificate[] loadCertificatesFromFile(String file) throws IOException, GeneralSecurityException { + if (file == null) { + return null; + } + CertificateFactory fact = CertificateFactory.getInstance(certType); + try (FileInputStream is = new FileInputStream(file)) { + Collection certs = fact.generateCertificates(is); + X509Certificate[] x509Certs = new X509Certificate[certs.size()]; + int i = 0; + for (Certificate cert : certs) { + x509Certs[i++] = (X509Certificate) cert; + } + return x509Certs; + } + } + + private KeyStore toTrustStore(final String trustCertificatesAliasPrefix, final X509Certificate[] trustCertificates) throws IOException, + GeneralSecurityException { + if (trustCertificates == null) { + return null; + } + KeyStore ks = KeyStore.getInstance(storeType); + ks.load(null); + + if (trustCertificates != null) { + for (int i = 0; i < trustCertificates.length; i++) { + X509Certificate x509Certificate = trustCertificates[i]; + ks.setCertificateEntry(trustCertificatesAliasPrefix + "_" + i, x509Certificate); + } + } + return ks; + } +} \ No newline at end of file