You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ro...@apache.org on 2019/11/14 09:13:27 UTC

[james-project] 07/07: JAMES-2905 One more option about HostNameVerifier

This is an automated email from the ASF dual-hosted git repository.

rouazana pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 2e87e12dbce48737c21ad29138e445ceb1b8a35b
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Wed Nov 13 18:20:06 2019 +0700

    JAMES-2905 One more option about HostNameVerifier
---
 .../apache/james/backends/es/ClientProvider.java   |  29 ++++-
 .../backends/es/ElasticSearchConfiguration.java    | 144 ++++++++++++++++-----
 ...iderImplConnectionAuthESIgnoreSSLCheckTest.java |   7 +-
 ...ImplConnectionAuthESOverrideTrustStoreTest.java |  10 +-
 .../es/ElasticSearchConfigurationTest.java         | 118 +++++++++++++----
 .../destination/conf/elasticsearch.properties      |   6 +
 .../destination/conf/elasticsearch.properties      |   6 +
 .../destination/conf/elasticsearch.properties      |   6 +
 .../destination/conf/elasticsearch.properties      |   6 +
 src/site/xdoc/server/config-elasticsearch.xml      |  15 +++
 10 files changed, 282 insertions(+), 65 deletions(-)

diff --git a/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ClientProvider.java b/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ClientProvider.java
index 50a51e7..1ff14cd 100644
--- a/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ClientProvider.java
+++ b/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ClientProvider.java
@@ -38,13 +38,15 @@ import org.apache.http.HttpHost;
 import org.apache.http.auth.AuthScope;
 import org.apache.http.auth.UsernamePasswordCredentials;
 import org.apache.http.client.CredentialsProvider;
+import org.apache.http.conn.ssl.DefaultHostnameVerifier;
 import org.apache.http.impl.client.BasicCredentialsProvider;
 import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
 import org.apache.http.ssl.SSLContextBuilder;
 import org.apache.http.ssl.TrustStrategy;
 import org.apache.james.backends.es.ElasticSearchConfiguration.HostScheme;
-import org.apache.james.backends.es.ElasticSearchConfiguration.SSLTrustConfiguration.SSLTrustStore;
-import org.apache.james.backends.es.ElasticSearchConfiguration.SSLTrustConfiguration.SSLValidationStrategy;
+import org.apache.james.backends.es.ElasticSearchConfiguration.SSLConfiguration.HostNameVerifier;
+import org.apache.james.backends.es.ElasticSearchConfiguration.SSLConfiguration.SSLTrustStore;
+import org.apache.james.backends.es.ElasticSearchConfiguration.SSLConfiguration.SSLValidationStrategy;
 import org.elasticsearch.client.RestClient;
 import org.elasticsearch.client.RestHighLevelClient;
 import org.slf4j.Logger;
@@ -60,7 +62,7 @@ public class ClientProvider implements Provider<RestHighLevelClient> {
     private static class HttpAsyncClientConfigurer {
 
         private static final TrustStrategy TRUST_ALL = (x509Certificates, authType) -> true;
-        private static final HostnameVerifier ACCEPT_ANY_HOST = (hostname, sslSession) -> true;
+        private static final HostnameVerifier ACCEPT_ANY_HOSTNAME = (hostname, sslSession) -> true;
 
         private final ElasticSearchConfiguration configuration;
 
@@ -94,7 +96,7 @@ public class ClientProvider implements Provider<RestHighLevelClient> {
             try {
                 builder
                     .setSSLContext(sslContext())
-                    .setSSLHostnameVerifier(ACCEPT_ANY_HOST);
+                    .setSSLHostnameVerifier(hostnameVerifier());
             } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | CertificateException | IOException e) {
                 throw new RuntimeException("Cannot set SSL options to the builder", e);
             }
@@ -105,7 +107,7 @@ public class ClientProvider implements Provider<RestHighLevelClient> {
 
             SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
 
-            SSLValidationStrategy strategy = configuration.getSslTrustConfiguration()
+            SSLValidationStrategy strategy = configuration.getSslConfiguration()
                 .getStrategy();
 
             switch (strategy) {
@@ -123,10 +125,25 @@ public class ClientProvider implements Provider<RestHighLevelClient> {
             }
         }
 
+        private HostnameVerifier hostnameVerifier() {
+            HostNameVerifier hostnameVerifier = configuration.getSslConfiguration()
+                .getHostNameVerifier();
+
+            switch (hostnameVerifier) {
+                case DEFAULT:
+                    return new DefaultHostnameVerifier();
+                case ACCEPT_ANY_HOSTNAME:
+                    return ACCEPT_ANY_HOSTNAME;
+                default:
+                    throw new NotImplementedException(
+                        String.format("unrecognized HostNameVerifier '%s'", hostnameVerifier.name()));
+            }
+        }
+
         private SSLContextBuilder applyTrustStore(SSLContextBuilder sslContextBuilder) throws CertificateException, NoSuchAlgorithmException,
             KeyStoreException, IOException {
 
-            SSLTrustStore trustStore = configuration.getSslTrustConfiguration()
+            SSLTrustStore trustStore = configuration.getSslConfiguration()
                 .getTrustStore()
                 .orElseThrow(() -> new IllegalStateException("SSLTrustStore cannot to be empty"));
 
diff --git a/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ElasticSearchConfiguration.java b/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ElasticSearchConfiguration.java
index 47681a5..16727ea 100644
--- a/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ElasticSearchConfiguration.java
+++ b/backends-common/elasticsearch/src/main/java/org/apache/james/backends/es/ElasticSearchConfiguration.java
@@ -19,7 +19,7 @@
 
 package org.apache.james.backends.es;
 
-import static org.apache.james.backends.es.ElasticSearchConfiguration.SSLTrustConfiguration.SSLValidationStrategy.OVERRIDE;
+import static org.apache.james.backends.es.ElasticSearchConfiguration.SSLConfiguration.SSLValidationStrategy.OVERRIDE;
 
 import java.io.File;
 import java.nio.file.Files;
@@ -35,8 +35,9 @@ import java.util.stream.Stream;
 
 import org.apache.commons.configuration2.Configuration;
 import org.apache.commons.configuration2.ex.ConfigurationException;
-import org.apache.james.backends.es.ElasticSearchConfiguration.SSLTrustConfiguration.SSLTrustStore;
-import org.apache.james.backends.es.ElasticSearchConfiguration.SSLTrustConfiguration.SSLValidationStrategy;
+import org.apache.james.backends.es.ElasticSearchConfiguration.SSLConfiguration.HostNameVerifier;
+import org.apache.james.backends.es.ElasticSearchConfiguration.SSLConfiguration.SSLTrustStore;
+import org.apache.james.backends.es.ElasticSearchConfiguration.SSLConfiguration.SSLValidationStrategy;
 import org.apache.james.util.Host;
 
 import com.github.steveash.guavate.Guavate;
@@ -110,7 +111,7 @@ public class ElasticSearchConfiguration {
         }
     }
 
-    public static class SSLTrustConfiguration {
+    public static class SSLConfiguration {
 
         public enum SSLValidationStrategy {
             DEFAULT,
@@ -128,6 +129,21 @@ public class ElasticSearchConfiguration {
             }
         }
 
+        public enum HostNameVerifier {
+            DEFAULT,
+            ACCEPT_ANY_HOSTNAME;
+
+            static HostNameVerifier from(String rawValue) {
+                Preconditions.checkNotNull(rawValue);
+
+                return Stream.of(values())
+                    .filter(verifier -> verifier.name().equalsIgnoreCase(rawValue))
+                    .findAny()
+                    .orElseThrow(() -> new IllegalArgumentException(String.format("invalid HostNameVerifier '%s'", rawValue)));
+
+            }
+        }
+
         public static class SSLTrustStore {
 
             public static SSLTrustStore of(String filePath, String password) {
@@ -174,28 +190,79 @@ public class ElasticSearchConfiguration {
             }
         }
 
-        static SSLTrustConfiguration defaultBehavior() {
-            return new SSLTrustConfiguration(SSLValidationStrategy.DEFAULT, Optional.empty());
+        static class Builder {
+
+            interface RequireSSLStrategyTrustStore {
+                RequireHostNameVerifier sslStrategy(SSLValidationStrategy strategy, Optional<SSLTrustStore> trustStore);
+
+                default RequireHostNameVerifier strategyIgnore() {
+                    return sslStrategy(SSLValidationStrategy.IGNORE, Optional.empty());
+                }
+
+                default RequireHostNameVerifier strategyOverride(SSLTrustStore trustStore) {
+                    return sslStrategy(SSLValidationStrategy.OVERRIDE, Optional.of(trustStore));
+                }
+
+                default RequireHostNameVerifier strategyDefault() {
+                    return sslStrategy(SSLValidationStrategy.DEFAULT, Optional.empty());
+                }
+            }
+
+            interface RequireHostNameVerifier {
+                ReadyToBuild hostNameVerifier(HostNameVerifier hostNameVerifier);
+
+                default ReadyToBuild acceptAnyHostNameVerifier() {
+                    return hostNameVerifier(HostNameVerifier.ACCEPT_ANY_HOSTNAME);
+                }
+
+                default ReadyToBuild defaultHostNameVerifier() {
+                    return hostNameVerifier(HostNameVerifier.DEFAULT);
+                }
+            }
+
+            static class ReadyToBuild {
+                private final SSLValidationStrategy sslValidationStrategy;
+                private final HostNameVerifier hostNameVerifier;
+                private Optional<SSLTrustStore> sslTrustStore;
+
+                private ReadyToBuild(SSLValidationStrategy sslValidationStrategy, HostNameVerifier hostNameVerifier, Optional<SSLTrustStore> sslTrustStore) {
+                    this.sslValidationStrategy = sslValidationStrategy;
+                    this.hostNameVerifier = hostNameVerifier;
+                    this.sslTrustStore = sslTrustStore;
+                }
+
+                public ReadyToBuild sslTrustStore(SSLTrustStore sslTrustStore) {
+                    this.sslTrustStore = Optional.of(sslTrustStore);
+                    return this;
+                }
+
+                public SSLConfiguration build() {
+                    return new SSLConfiguration(sslValidationStrategy, hostNameVerifier, sslTrustStore);
+                }
+            }
         }
 
-        static SSLTrustConfiguration ignore() {
-            return new SSLTrustConfiguration(SSLValidationStrategy.IGNORE, Optional.empty());
+        static SSLConfiguration defaultBehavior() {
+            return new SSLConfiguration(SSLValidationStrategy.DEFAULT, HostNameVerifier.DEFAULT, Optional.empty());
         }
 
-        static SSLTrustConfiguration override(SSLTrustStore sslTrustStore) {
-            return new SSLTrustConfiguration(OVERRIDE, Optional.of(sslTrustStore));
+        static Builder.RequireSSLStrategyTrustStore builder() {
+            return (strategy, trustStore) -> hostNameVerifier -> new Builder.ReadyToBuild(strategy, hostNameVerifier, trustStore);
         }
 
         private final SSLValidationStrategy strategy;
+        private final HostNameVerifier hostNameVerifier;
         private final Optional<SSLTrustStore> trustStore;
 
-        private SSLTrustConfiguration(SSLValidationStrategy strategy, Optional<SSLTrustStore> trustStore) {
+        private SSLConfiguration(SSLValidationStrategy strategy, HostNameVerifier hostNameVerifier, Optional<SSLTrustStore> trustStore) {
             Preconditions.checkNotNull(strategy);
             Preconditions.checkNotNull(trustStore);
+            Preconditions.checkNotNull(hostNameVerifier);
             Preconditions.checkArgument(strategy != OVERRIDE || trustStore.isPresent(), OVERRIDE.name() + " strategy requires trustStore to be present");
 
             this.strategy = strategy;
             this.trustStore = trustStore;
+            this.hostNameVerifier = hostNameVerifier;
         }
 
         public SSLValidationStrategy getStrategy() {
@@ -206,20 +273,25 @@ public class ElasticSearchConfiguration {
             return trustStore;
         }
 
+        public HostNameVerifier getHostNameVerifier() {
+            return hostNameVerifier;
+        }
+
         @Override
         public final boolean equals(Object o) {
-            if (o instanceof SSLTrustConfiguration) {
-                SSLTrustConfiguration that = (SSLTrustConfiguration) o;
+            if (o instanceof SSLConfiguration) {
+                SSLConfiguration that = (SSLConfiguration) o;
 
                 return Objects.equals(this.strategy, that.strategy)
-                    && Objects.equals(this.trustStore, that.trustStore);
+                    && Objects.equals(this.trustStore, that.trustStore)
+                    && Objects.equals(this.hostNameVerifier, that.hostNameVerifier);
             }
             return false;
         }
 
         @Override
         public final int hashCode() {
-            return Objects.hash(strategy, trustStore);
+            return Objects.hash(strategy, trustStore, hostNameVerifier);
         }
     }
 
@@ -234,7 +306,7 @@ public class ElasticSearchConfiguration {
         private Optional<Duration> requestTimeout;
         private Optional<HostScheme> hostScheme;
         private Optional<Credential> credential;
-        private Optional<SSLTrustConfiguration> sslTrustConfiguration;
+        private Optional<SSLConfiguration> sslTrustConfiguration;
 
         public Builder() {
             hosts = ImmutableList.builder();
@@ -302,12 +374,12 @@ public class ElasticSearchConfiguration {
             return this;
         }
 
-        public Builder sslTrustConfiguration(SSLTrustConfiguration sslTrustConfiguration) {
-            this.sslTrustConfiguration = Optional.of(sslTrustConfiguration);
+        public Builder sslTrustConfiguration(SSLConfiguration sslConfiguration) {
+            this.sslTrustConfiguration = Optional.of(sslConfiguration);
             return this;
         }
 
-        public Builder sslTrustConfiguration(Optional<SSLTrustConfiguration> sslTrustStore) {
+        public Builder sslTrustConfiguration(Optional<SSLConfiguration> sslTrustStore) {
             this.sslTrustConfiguration = sslTrustStore;
             return this;
         }
@@ -338,6 +410,7 @@ public class ElasticSearchConfiguration {
     public static final String ELASTICSEARCH_PORT = "elasticsearch.port";
     public static final String ELASTICSEARCH_HOST_SCHEME = "elasticsearch.hostScheme";
     public static final String ELASTICSEARCH_HTTPS_SSL_VALIDATION_STRATEGY = "elasticsearch.hostScheme.https.sslValidationStrategy";
+    public static final String ELASTICSEARCH_HTTPS_HOSTNAME_VERIFIER = "elasticsearch.hostScheme.https.hostNameVerifier";
     public static final String ELASTICSEARCH_HTTPS_TRUST_STORE_PATH = "elasticsearch.hostScheme.https.trustStorePath";
     public static final String ELASTICSEARCH_HTTPS_TRUST_STORE_PASSWORD = "elasticsearch.hostScheme.https.trustStorePassword";
     public static final String ELASTICSEARCH_USER = "elasticsearch.user";
@@ -358,7 +431,7 @@ public class ElasticSearchConfiguration {
     public static final String LOCALHOST = "127.0.0.1";
     public static final Optional<Integer> DEFAULT_PORT_AS_OPTIONAL = Optional.of(DEFAULT_PORT);
     public static final HostScheme DEFAULT_SCHEME = HostScheme.HTTP;
-    public static final SSLTrustConfiguration DEFAULT_SSL_TRUST_CONFIGURATION = SSLTrustConfiguration.defaultBehavior();
+    public static final SSLConfiguration DEFAULT_SSL_TRUST_CONFIGURATION = SSLConfiguration.defaultBehavior();
 
     public static final ElasticSearchConfiguration DEFAULT_CONFIGURATION = builder()
         .addHost(Host.from(LOCALHOST, DEFAULT_PORT))
@@ -378,10 +451,21 @@ public class ElasticSearchConfiguration {
             .build();
     }
 
-    private static Optional<SSLTrustConfiguration> sslTrustConfiguration(Configuration configuration) {
-        return Optional.ofNullable(configuration.getString(ELASTICSEARCH_HTTPS_SSL_VALIDATION_STRATEGY))
+    private static SSLConfiguration sslTrustConfiguration(Configuration configuration) {
+        SSLValidationStrategy sslStrategy = Optional
+            .ofNullable(configuration.getString(ELASTICSEARCH_HTTPS_SSL_VALIDATION_STRATEGY))
             .map(SSLValidationStrategy::from)
-            .map(strategy -> new SSLTrustConfiguration(strategy, getSSLTrustStore(configuration)));
+            .orElse(SSLValidationStrategy.DEFAULT);
+
+        HostNameVerifier hostNameVerifier = Optional
+            .ofNullable(configuration.getString(ELASTICSEARCH_HTTPS_HOSTNAME_VERIFIER))
+            .map(HostNameVerifier::from)
+            .orElse(HostNameVerifier.DEFAULT);
+
+        return SSLConfiguration.builder()
+            .sslStrategy(sslStrategy, getSSLTrustStore(configuration))
+            .hostNameVerifier(hostNameVerifier)
+            .build();
     }
 
     private static Optional<SSLTrustStore> getSSLTrustStore(Configuration configuration) {
@@ -455,10 +539,10 @@ public class ElasticSearchConfiguration {
     private final Duration requestTimeout;
     private final HostScheme hostScheme;
     private final Optional<Credential> credential;
-    private final SSLTrustConfiguration sslTrustConfiguration;
+    private final SSLConfiguration sslConfiguration;
 
     private ElasticSearchConfiguration(ImmutableList<Host> hosts, int nbShards, int nbReplica, int waitForActiveShards, int minDelay, int maxRetries, Duration requestTimeout,
-                                       HostScheme hostScheme, Optional<Credential> credential, SSLTrustConfiguration sslTrustConfiguration) {
+                                       HostScheme hostScheme, Optional<Credential> credential, SSLConfiguration sslConfiguration) {
         this.hosts = hosts;
         this.nbShards = nbShards;
         this.nbReplica = nbReplica;
@@ -468,7 +552,7 @@ public class ElasticSearchConfiguration {
         this.requestTimeout = requestTimeout;
         this.hostScheme = hostScheme;
         this.credential = credential;
-        this.sslTrustConfiguration = sslTrustConfiguration;
+        this.sslConfiguration = sslConfiguration;
     }
 
     public ImmutableList<Host> getHosts() {
@@ -507,8 +591,8 @@ public class ElasticSearchConfiguration {
         return credential;
     }
 
-    public SSLTrustConfiguration getSslTrustConfiguration() {
-        return sslTrustConfiguration;
+    public SSLConfiguration getSslConfiguration() {
+        return sslConfiguration;
     }
 
     @Override
@@ -525,7 +609,7 @@ public class ElasticSearchConfiguration {
                 && Objects.equals(this.requestTimeout, that.requestTimeout)
                 && Objects.equals(this.hostScheme, that.hostScheme)
                 && Objects.equals(this.credential, that.credential)
-                && Objects.equals(this.sslTrustConfiguration, that.sslTrustConfiguration);
+                && Objects.equals(this.sslConfiguration, that.sslConfiguration);
         }
         return false;
     }
@@ -533,6 +617,6 @@ public class ElasticSearchConfiguration {
     @Override
     public final int hashCode() {
         return Objects.hash(hosts, nbShards, nbReplica, waitForActiveShards, minDelay, maxRetries, requestTimeout,
-            hostScheme, credential, sslTrustConfiguration);
+            hostScheme, credential, sslConfiguration);
     }
 }
diff --git a/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ClientProviderImplConnectionAuthESIgnoreSSLCheckTest.java b/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ClientProviderImplConnectionAuthESIgnoreSSLCheckTest.java
index 1de1996..a97c6a3 100644
--- a/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ClientProviderImplConnectionAuthESIgnoreSSLCheckTest.java
+++ b/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ClientProviderImplConnectionAuthESIgnoreSSLCheckTest.java
@@ -24,7 +24,7 @@ import static org.apache.james.backends.es.ElasticSearchClusterExtension.Elastic
 import java.util.Optional;
 
 import org.apache.james.backends.es.ElasticSearchConfiguration.HostScheme;
-import org.apache.james.backends.es.ElasticSearchConfiguration.SSLTrustConfiguration;
+import org.apache.james.backends.es.ElasticSearchConfiguration.SSLConfiguration;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 class ClientProviderImplConnectionAuthESIgnoreSSLCheckTest implements ClientProviderImplConnectionContract {
@@ -39,6 +39,9 @@ class ClientProviderImplConnectionAuthESIgnoreSSLCheckTest implements ClientProv
         return ElasticSearchConfiguration.builder()
             .credential(Optional.of(DockerElasticSearch.WithAuth.DEFAULT_CREDENTIAL))
             .hostScheme(Optional.of(HostScheme.HTTPS))
-            .sslTrustConfiguration(SSLTrustConfiguration.ignore());
+            .sslTrustConfiguration(SSLConfiguration.builder()
+                .strategyIgnore()
+                .acceptAnyHostNameVerifier()
+                .build());
     }
 }
diff --git a/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ClientProviderImplConnectionAuthESOverrideTrustStoreTest.java b/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ClientProviderImplConnectionAuthESOverrideTrustStoreTest.java
index 5b92cf7..7493681 100644
--- a/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ClientProviderImplConnectionAuthESOverrideTrustStoreTest.java
+++ b/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ClientProviderImplConnectionAuthESOverrideTrustStoreTest.java
@@ -21,8 +21,8 @@ package org.apache.james.backends.es;
 
 import java.util.Optional;
 
-import org.apache.james.backends.es.ElasticSearchConfiguration.SSLTrustConfiguration;
-import org.apache.james.backends.es.ElasticSearchConfiguration.SSLTrustConfiguration.SSLTrustStore;
+import org.apache.james.backends.es.ElasticSearchConfiguration.SSLConfiguration;
+import org.apache.james.backends.es.ElasticSearchConfiguration.SSLConfiguration.SSLTrustStore;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 public class ClientProviderImplConnectionAuthESOverrideTrustStoreTest implements ClientProviderImplConnectionContract {
@@ -40,7 +40,9 @@ public class ClientProviderImplConnectionAuthESOverrideTrustStoreTest implements
         return ElasticSearchConfiguration.builder()
             .credential(Optional.of(DockerElasticSearch.WithAuth.DEFAULT_CREDENTIAL))
             .hostScheme(Optional.of(ElasticSearchConfiguration.HostScheme.HTTPS))
-            .sslTrustConfiguration(SSLTrustConfiguration.override(
-                SSLTrustStore.of(TRUST_STORE_FILE_PATH, TRUST_STORE_PASSWORD)));
+            .sslTrustConfiguration(SSLConfiguration.builder()
+                .strategyOverride(SSLTrustStore.of(TRUST_STORE_FILE_PATH, TRUST_STORE_PASSWORD))
+                .acceptAnyHostNameVerifier()
+                .build());
     }
 }
\ No newline at end of file
diff --git a/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ElasticSearchConfigurationTest.java b/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ElasticSearchConfigurationTest.java
index cf50942..35bf3f4 100644
--- a/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ElasticSearchConfigurationTest.java
+++ b/backends-common/elasticsearch/src/test/java/org/apache/james/backends/es/ElasticSearchConfigurationTest.java
@@ -29,8 +29,8 @@ import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler;
 import org.apache.commons.configuration2.ex.ConfigurationException;
 import org.apache.james.backends.es.ElasticSearchConfiguration.Credential;
 import org.apache.james.backends.es.ElasticSearchConfiguration.HostScheme;
-import org.apache.james.backends.es.ElasticSearchConfiguration.SSLTrustConfiguration;
-import org.apache.james.backends.es.ElasticSearchConfiguration.SSLTrustConfiguration.SSLTrustStore;
+import org.apache.james.backends.es.ElasticSearchConfiguration.SSLConfiguration;
+import org.apache.james.backends.es.ElasticSearchConfiguration.SSLConfiguration.SSLTrustStore;
 import org.apache.james.util.Host;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
@@ -62,7 +62,7 @@ class ElasticSearchConfigurationTest {
     }
 
     @Nested
-    class SSLTrustConfigurationTest {
+    class SSLConfigurationTest {
 
         @Test
         void sslTrustStoreShouldMatchBeanContact() {
@@ -72,45 +72,112 @@ class ElasticSearchConfigurationTest {
 
         @Test
         void shouldMatchBeanContact() {
-            EqualsVerifier.forClass(SSLTrustConfiguration.class)
+            EqualsVerifier.forClass(SSLConfiguration.class)
                 .verify();
         }
 
+        @Test
+        void getSSLConfigurationShouldReturnDefaultValueWhenEmpty() throws Exception {
+            PropertiesConfiguration configuration = new PropertiesConfiguration();
+            configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+            assertThat(ElasticSearchConfiguration.fromProperties(configuration)
+                    .getSslConfiguration())
+                .isEqualTo(SSLConfiguration.defaultBehavior());
+        }
+
+        @Test
+        void getSSLConfigurationShouldReturnConfiguredValue() throws Exception {
+            PropertiesConfiguration configuration = new PropertiesConfiguration();
+            configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+            String trustStorePath = "src/test/resources/auth-es/server.jks";
+            String trustStorePassword = "secret";
+
+            configuration.addProperty("elasticsearch.hostScheme.https.sslValidationStrategy", "override");
+            configuration.addProperty("elasticsearch.hostScheme.https.trustStorePath", trustStorePath);
+            configuration.addProperty("elasticsearch.hostScheme.https.trustStorePassword", trustStorePassword);
+            configuration.addProperty("elasticsearch.hostScheme.https.hostNameVerifier", "default");
+
+            assertThat(ElasticSearchConfiguration.fromProperties(configuration)
+                    .getSslConfiguration())
+                .isEqualTo(SSLConfiguration.builder()
+                    .strategyOverride(SSLTrustStore.of(trustStorePath, trustStorePassword))
+                    .defaultHostNameVerifier()
+                    .build());
+        }
+
         @Nested
         class WithSSLValidationStrategy {
 
             @Test
-            void getSSLConfigurationShouldReturnDefaultValueWhenEmpty() throws Exception {
+            void getSSLConfigurationShouldAcceptCaseInsensitiveStrategy() throws Exception {
                 PropertiesConfiguration configuration = new PropertiesConfiguration();
                 configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
 
+                configuration.addProperty("elasticsearch.hostScheme.https.sslValidationStrategy", "DEfault");
+
                 assertThat(ElasticSearchConfiguration.fromProperties(configuration)
-                        .getSslTrustConfiguration())
-                    .isEqualTo(SSLTrustConfiguration.defaultBehavior());
+                        .getSslConfiguration())
+                    .isEqualTo(SSLConfiguration.defaultBehavior());
             }
 
             @Test
-            void getSSLConfigurationShouldAcceptCaseInsensitiveStrategy() throws Exception {
+            void fromPropertiesShouldThrowWhenInvalidStrategy() throws Exception {
                 PropertiesConfiguration configuration = new PropertiesConfiguration();
                 configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
 
-                configuration.addProperty("elasticsearch.hostScheme.https.sslValidationStrategy", "DEfault");
+                configuration.addProperty("elasticsearch.hostScheme.https.sslValidationStrategy", "invalid");
+
+                assertThatThrownBy(() -> ElasticSearchConfiguration.fromProperties(configuration))
+                    .isInstanceOf(IllegalArgumentException.class)
+                    .hasMessage("invalid strategy 'invalid'");
+            }
+        }
+
+        @Nested
+        class WithHostNameVerifier {
+
+            @Test
+            void getSSLConfigurationShouldReturnConfiguredValue() throws Exception {
+                PropertiesConfiguration configuration = new PropertiesConfiguration();
+                configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+                configuration.addProperty("elasticsearch.hostScheme.https.hostNameVerifier", "DEFAULT");
 
                 assertThat(ElasticSearchConfiguration.fromProperties(configuration)
-                        .getSslTrustConfiguration())
-                    .isEqualTo(SSLTrustConfiguration.defaultBehavior());
+                        .getSslConfiguration())
+                    .isEqualTo(SSLConfiguration.builder()
+                        .strategyDefault()
+                        .defaultHostNameVerifier()
+                        .build());
             }
 
             @Test
-            void fromPropertiesShouldThrowWhenInvalidStrategy() throws Exception {
+            void getSSLConfigurationShouldAcceptCaseInsensitiveVerifier() throws Exception {
                 PropertiesConfiguration configuration = new PropertiesConfiguration();
                 configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
 
-                configuration.addProperty("elasticsearch.hostScheme.https.sslValidationStrategy", "invalid");
+                configuration.addProperty("elasticsearch.hostScheme.https.hostNameVerifier", "Accept_Any_Hostname");
+
+                assertThat(ElasticSearchConfiguration.fromProperties(configuration)
+                        .getSslConfiguration())
+                    .isEqualTo(SSLConfiguration.builder()
+                        .strategyDefault()
+                        .acceptAnyHostNameVerifier()
+                        .build());
+            }
+
+            @Test
+            void fromPropertiesShouldThrowWhenInvalidVerifier() throws Exception {
+                PropertiesConfiguration configuration = new PropertiesConfiguration();
+                configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+                configuration.addProperty("elasticsearch.hostScheme.https.hostNameVerifier", "invalid");
 
                 assertThatThrownBy(() -> ElasticSearchConfiguration.fromProperties(configuration))
                     .isInstanceOf(IllegalArgumentException.class)
-                    .hasMessage("invalid strategy 'invalid'");
+                    .hasMessage("invalid HostNameVerifier 'invalid'");
             }
         }
 
@@ -125,8 +192,8 @@ class ElasticSearchConfigurationTest {
                 configuration.addProperty("elasticsearch.hostScheme.https.sslValidationStrategy", "default");
 
                 assertThat(ElasticSearchConfiguration.fromProperties(configuration)
-                        .getSslTrustConfiguration())
-                    .isEqualTo(SSLTrustConfiguration.defaultBehavior());
+                        .getSslConfiguration())
+                    .isEqualTo(SSLConfiguration.defaultBehavior());
             }
         }
 
@@ -141,8 +208,11 @@ class ElasticSearchConfigurationTest {
                 configuration.addProperty("elasticsearch.hostScheme.https.sslValidationStrategy", "ignore");
 
                 assertThat(ElasticSearchConfiguration.fromProperties(configuration)
-                        .getSslTrustConfiguration())
-                    .isEqualTo(SSLTrustConfiguration.ignore());
+                        .getSslConfiguration())
+                    .isEqualTo(SSLConfiguration.builder()
+                        .strategyIgnore()
+                        .defaultHostNameVerifier()
+                        .build());
             }
         }
 
@@ -206,18 +276,20 @@ class ElasticSearchConfigurationTest {
                 PropertiesConfiguration configuration = new PropertiesConfiguration();
                 configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
 
-                String strategy = "override";
                 String trustStorePath = "src/test/resources/auth-es/server.jks";
                 String trustStorePassword = "secret";
 
-                configuration.addProperty("elasticsearch.hostScheme.https.sslValidationStrategy", strategy);
+                configuration.addProperty("elasticsearch.hostScheme.https.sslValidationStrategy", "override");
                 configuration.addProperty("elasticsearch.hostScheme.https.trustStorePath", trustStorePath);
                 configuration.addProperty("elasticsearch.hostScheme.https.trustStorePassword", trustStorePassword);
+                configuration.addProperty("elasticsearch.hostScheme.https.hostNameVerifier", "default");
 
                 assertThat(ElasticSearchConfiguration.fromProperties(configuration)
-                        .getSslTrustConfiguration())
-                    .isEqualTo(SSLTrustConfiguration.override(
-                        SSLTrustStore.of(trustStorePath, trustStorePassword)));
+                        .getSslConfiguration())
+                    .isEqualTo(SSLConfiguration.builder()
+                        .strategyOverride(SSLTrustStore.of(trustStorePath, trustStorePassword))
+                        .defaultHostNameVerifier()
+                        .build());
             }
         }
     }
diff --git a/dockerfiles/run/guice/cassandra-ldap/destination/conf/elasticsearch.properties b/dockerfiles/run/guice/cassandra-ldap/destination/conf/elasticsearch.properties
index b2eefc5..74e5a3d 100644
--- a/dockerfiles/run/guice/cassandra-ldap/destination/conf/elasticsearch.properties
+++ b/dockerfiles/run/guice/cassandra-ldap/destination/conf/elasticsearch.properties
@@ -45,6 +45,12 @@ elasticsearch.port=9200
 # You need to specify both trustStorePath and trustStorePassword
 # elasticsearch.hostScheme.https.trustStorePassword=myJKSPassword
 
+# Optional. default is `default`
+# Configure Elasticsearch rest client to use host name verifier during SSL handshake
+# default: using the default hostname verifier provided by apache http client.
+# accept_any_hostname: accept any hostname (not recommended).
+# elasticsearch.hostScheme.https.hostNameVerifier=default
+
 # Optional.
 # Basic auth username to access elasticsearch.
 # Ignore elasticsearch.user and elasticsearch.password to not be using authentication (default behaviour).
diff --git a/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/elasticsearch.properties b/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/elasticsearch.properties
index eecf449..0ea86db 100644
--- a/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/elasticsearch.properties
+++ b/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/elasticsearch.properties
@@ -44,6 +44,12 @@ elasticsearch.port=9200
 # You need to specify both trustStorePath and trustStorePassword
 # elasticsearch.hostScheme.https.trustStorePassword=myJKSPassword
 
+# Optional. default is `default`
+# Configure Elasticsearch rest client to use host name verifier during SSL handshake
+# default: using the default hostname verifier provided by apache http client.
+# accept_any_hostname: accept any hostname (not recommended).
+# elasticsearch.hostScheme.https.hostNameVerifier=default
+
 # Optional.
 # Basic auth username to access elasticsearch.
 # Ignore elasticsearch.user and elasticsearch.password to not be using authentication (default behaviour).
diff --git a/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/elasticsearch.properties b/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/elasticsearch.properties
index eecf449..16ea216 100644
--- a/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/elasticsearch.properties
+++ b/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/elasticsearch.properties
@@ -44,6 +44,12 @@ elasticsearch.port=9200
 # You need to specify both trustStorePath and trustStorePassword
 # elasticsearch.hostScheme.https.trustStorePassword=myJKSPassword
 
+# Optional. default is `default`
+# Configure Elasticsearch rest client to use host name verifier during SSL handshake
+# default: using the default hostname verifier provided by apache http client.
+# accept_any_hostname: accept any host (not recommended).
+# elasticsearch.hostScheme.https.hostNameVerifier=default
+
 # Optional.
 # Basic auth username to access elasticsearch.
 # Ignore elasticsearch.user and elasticsearch.password to not be using authentication (default behaviour).
diff --git a/dockerfiles/run/guice/cassandra/destination/conf/elasticsearch.properties b/dockerfiles/run/guice/cassandra/destination/conf/elasticsearch.properties
index b2eefc5..74e5a3d 100644
--- a/dockerfiles/run/guice/cassandra/destination/conf/elasticsearch.properties
+++ b/dockerfiles/run/guice/cassandra/destination/conf/elasticsearch.properties
@@ -45,6 +45,12 @@ elasticsearch.port=9200
 # You need to specify both trustStorePath and trustStorePassword
 # elasticsearch.hostScheme.https.trustStorePassword=myJKSPassword
 
+# Optional. default is `default`
+# Configure Elasticsearch rest client to use host name verifier during SSL handshake
+# default: using the default hostname verifier provided by apache http client.
+# accept_any_hostname: accept any hostname (not recommended).
+# elasticsearch.hostScheme.https.hostNameVerifier=default
+
 # Optional.
 # Basic auth username to access elasticsearch.
 # Ignore elasticsearch.user and elasticsearch.password to not be using authentication (default behaviour).
diff --git a/src/site/xdoc/server/config-elasticsearch.xml b/src/site/xdoc/server/config-elasticsearch.xml
index f763272..789f044 100644
--- a/src/site/xdoc/server/config-elasticsearch.xml
+++ b/src/site/xdoc/server/config-elasticsearch.xml
@@ -252,6 +252,21 @@
               Once you chose <strong>override</strong>, you need to specify both trustStorePath and trustStorePassword.
             </dd>
         </dl>
+
+        <p>
+            During SSL handshaking, the client can determine whether accept or reject connecting to a remote server by its hostname.
+            You can configure to use which HostNameVerifier in the client.
+        </p>
+        <dl>
+            <dt><strong>elasticsearch.hostScheme.https.hostNameVerifier</strong></dt>
+            <dd>
+              Optional. Default is <strong>default</strong>.
+            </dd>
+            <dd>
+              default: using the default hostname verifier provided by apache http client.
+              accept_any_hostname: accept any host (not recommended).
+            </dd>
+        </dl>
     </section>
 </body>
 


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org