This adds a static clear method fot LdapSSLSocketFactory for testing
This clears the LdapSSLSocketFactory "singleton" for reproducible tests. Fixes https://github.com/elasticsearch/elasticsearch-shield/issues/336 Original commit: elastic/x-pack-elasticsearch@baa3a2ce60
This commit is contained in:
parent
a224b54973
commit
eaac7ff250
|
@ -18,7 +18,11 @@ import java.io.IOException;
|
|||
import java.io.Serializable;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.elasticsearch.common.base.Predicates.contains;
|
||||
import static org.elasticsearch.common.collect.Iterables.all;
|
||||
|
||||
/**
|
||||
* This factory is needed for JNDI configuration for LDAP connections. It wraps a single instance of a static
|
||||
|
@ -31,6 +35,9 @@ import java.util.Locale;
|
|||
public class LdapSslSocketFactory extends SocketFactory {
|
||||
|
||||
static final String JAVA_NAMING_LDAP_FACTORY_SOCKET = "java.naming.ldap.factory.socket";
|
||||
private static final Pattern STARTS_WITH_LDAPS = Pattern.compile("^ldaps:.*", Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern STARTS_WITH_LDAP = Pattern.compile("^ldap:.*", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private static ESLogger logger = ESLoggerFactory.getLogger(LdapSslSocketFactory.class.getName());
|
||||
|
||||
private static LdapSslSocketFactory instance;
|
||||
|
@ -48,7 +55,16 @@ public class LdapSslSocketFactory extends SocketFactory {
|
|||
} else {
|
||||
instance = new LdapSslSocketFactory(ssl.getSSLSocketFactory());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This clears the static factory. There are threading issues with this. But for
|
||||
* testing this is useful.
|
||||
*/
|
||||
@Deprecated
|
||||
static void clear() {
|
||||
logger.error("clear should only be called by tests");
|
||||
instance = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -118,14 +134,15 @@ public class LdapSslSocketFactory extends SocketFactory {
|
|||
if (ldapUrls.length == 0) {
|
||||
return true;
|
||||
}
|
||||
boolean secureProtocol = ldapUrls[0].toLowerCase(Locale.getDefault()).startsWith("ldaps://");
|
||||
for(String url: ldapUrls){
|
||||
if (secureProtocol != url.toLowerCase(Locale.getDefault()).startsWith("ldaps://")) {
|
||||
//this is because LdapSSLSocketFactory produces only SSL sockets and not clear text sockets
|
||||
|
||||
boolean allSecure = all(asList(ldapUrls), contains(STARTS_WITH_LDAPS));
|
||||
boolean allClear = all(asList(ldapUrls), contains(STARTS_WITH_LDAP));
|
||||
|
||||
if (!allSecure && !allClear) {
|
||||
//No mixing is allowed because LdapSSLSocketFactory produces only SSL sockets and not clear text sockets
|
||||
throw new ShieldSettingsException("Configured ldap protocols are not all equal " +
|
||||
"(ldaps://.. and ldap://..): [" + Strings.arrayToCommaDelimitedString(ldapUrls) + "]");
|
||||
}
|
||||
}
|
||||
return secureProtocol;
|
||||
return allSecure;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,9 @@ import java.util.Arrays;
|
|||
*/
|
||||
public class SSLService extends AbstractComponent {
|
||||
static final String[] DEFAULT_CIPHERS = new String[] { "TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA" };
|
||||
public static final String SHIELD_TRANSPORT_SSL = "shield.transport.ssl";
|
||||
public static final String SHIELD_HTTP_SSL = "shield.http.ssl";
|
||||
public static final String SHIELD_AUTHC_LDAP_URL = "shield.authc.ldap.url";
|
||||
private final TrustManagerFactory trustFactory;
|
||||
private final SSLContext sslContext;
|
||||
private final String[] ciphers;
|
||||
|
@ -113,7 +116,7 @@ public class SSLService extends AbstractComponent {
|
|||
* - sslEngine.setNeedClientAuth(true)
|
||||
* Client-to-Node:
|
||||
* - sslEngine.setUseClientMode(true)
|
||||
* Node-from-Client (inbound):
|
||||
* Http Client-to-Node (inbound):
|
||||
* - sslEngine.setUserClientMode(false)
|
||||
* - sslEngine.setNeedClientAuth(false)
|
||||
* @return
|
||||
|
@ -129,9 +132,9 @@ public class SSLService extends AbstractComponent {
|
|||
}
|
||||
|
||||
public static boolean isSSLEnabled(Settings settings) {
|
||||
return settings.getAsBoolean("shield.transport.ssl", false) ||
|
||||
settings.getAsBoolean("shield.http.ssl", false) ||
|
||||
(LdapSslSocketFactory.secureUrls(settings.getAsArray("shield.authc.ldap.url")) &&
|
||||
return settings.getAsBoolean(SHIELD_TRANSPORT_SSL, false) ||
|
||||
settings.getAsBoolean(SHIELD_HTTP_SSL, false) ||
|
||||
(LdapSslSocketFactory.secureUrls(settings.getAsArray(SHIELD_AUTHC_LDAP_URL)) &&
|
||||
LdapModule.enabled(settings));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.elasticsearch.shield.ssl.SSLService;
|
|||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -37,6 +38,10 @@ public class ActiveDirectoryFactoryTests extends ElasticsearchTestCase {
|
|||
.build()));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void clearTrustStore() {
|
||||
LdapSslSocketFactory.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdAuth() {
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.elasticsearch.shield.ShieldSettingsException;
|
|||
import org.elasticsearch.shield.ssl.SSLService;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -31,8 +32,13 @@ public class LdapSslSocketFactoryTests extends ElasticsearchTestCase {
|
|||
.build()));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void clearTrustStore() {
|
||||
LdapSslSocketFactory.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigure_1https(){
|
||||
public void testConfigure_1ldaps(){
|
||||
String[] urls = new String[]{"ldaps://example.com:636"};
|
||||
|
||||
ImmutableMap.Builder<String, Serializable> builder = ImmutableMap.<String, Serializable>builder();
|
||||
|
@ -43,7 +49,7 @@ public class LdapSslSocketFactoryTests extends ElasticsearchTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testConfigure_2https(){
|
||||
public void testConfigure_2ldaps(){
|
||||
String[] urls = new String[]{"ldaps://primary.example.com:636", "LDAPS://secondary.example.com:10636"};
|
||||
|
||||
ImmutableMap.Builder<String, Serializable> builder = ImmutableMap.<String, Serializable>builder();
|
||||
|
@ -53,7 +59,7 @@ public class LdapSslSocketFactoryTests extends ElasticsearchTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testConfigure_2http(){
|
||||
public void testConfigure_2ldap(){
|
||||
String[] urls = new String[]{"ldap://primary.example.com:392", "LDAP://secondary.example.com:10392"};
|
||||
|
||||
ImmutableMap.Builder<String, Serializable> builder = ImmutableMap.<String, Serializable>builder();
|
||||
|
@ -63,7 +69,7 @@ public class LdapSslSocketFactoryTests extends ElasticsearchTestCase {
|
|||
}
|
||||
|
||||
@Test(expected = ShieldSettingsException.class)
|
||||
public void testConfigure_1httpS_1http(){
|
||||
public void testConfigure_1ldaps_1ldap(){
|
||||
String[] urls = new String[]{"LDAPS://primary.example.com:636", "ldap://secondary.example.com:392"};
|
||||
|
||||
ImmutableMap.Builder<String, Serializable> builder = ImmutableMap.<String, Serializable>builder();
|
||||
|
@ -71,7 +77,7 @@ public class LdapSslSocketFactoryTests extends ElasticsearchTestCase {
|
|||
}
|
||||
|
||||
@Test(expected = ShieldSettingsException.class)
|
||||
public void testConfigure_1http_1https(){
|
||||
public void testConfigure_1ldap_1ldaps(){
|
||||
String[] urls = new String[]{"ldap://primary.example.com:392", "ldaps://secondary.example.com:636"};
|
||||
|
||||
ImmutableMap.Builder<String, Serializable> builder = ImmutableMap.<String, Serializable>builder();
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.elasticsearch.shield.authc.support.SecuredStringTests;
|
|||
import org.elasticsearch.shield.ssl.SSLService;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -34,6 +35,11 @@ public class OpenLdapTests extends ElasticsearchTestCase {
|
|||
.build()));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void clearTrustStore() {
|
||||
LdapSslSocketFactory.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_standardLdapConnection_uid(){
|
||||
//openldap does not use cn as naming attributes by default
|
||||
|
|
|
@ -7,17 +7,15 @@ package org.elasticsearch.shield.ssl;
|
|||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.Matchers.arrayContaining;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class SSLServiceTests extends ElasticsearchTestCase {
|
||||
|
||||
|
@ -28,9 +26,8 @@ public class SSLServiceTests extends ElasticsearchTestCase {
|
|||
testnodeStore = new File(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks").toURI());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(expected = ElasticsearchSSLException.class)
|
||||
public void testThatInvalidProtocolThrowsException() throws Exception {
|
||||
try {
|
||||
new SSLService(settingsBuilder()
|
||||
.put("shield.ssl.protocol", "non-existing")
|
||||
.put("shield.ssl.keystore", testnodeStore.getPath())
|
||||
|
@ -38,9 +35,6 @@ public class SSLServiceTests extends ElasticsearchTestCase {
|
|||
.put("shield.ssl.truststore", testnodeStore.getPath())
|
||||
.put("shield.ssl.truststore_password", "testnode")
|
||||
.build());
|
||||
} catch (ElasticsearchSSLException e) {
|
||||
Assert.assertThat(e.getRootCause(), Matchers.instanceOf(NoSuchAlgorithmException.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Test @Ignore //TODO it appears that setting a specific protocol doesn't make a difference
|
||||
|
@ -52,13 +46,13 @@ public class SSLServiceTests extends ElasticsearchTestCase {
|
|||
.put("shield.ssl.truststore", testnodeStore.getPath())
|
||||
.put("shield.ssl.truststore_password", "testnode")
|
||||
.build());
|
||||
Assert.assertThat(ssl.createSSLEngine().getSSLParameters().getProtocols(), Matchers.arrayContaining("TLSv1.2"));
|
||||
assertThat(ssl.createSSLEngine().getSSLParameters().getProtocols(), arrayContaining("TLSv1.2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSSLEnabled_allDefaults(){
|
||||
public void testIsSSLDisabled_allDefaults(){
|
||||
Settings settings = settingsBuilder().build();
|
||||
assertThat(SSLService.isSSLEnabled(settings), is(false));
|
||||
assertSSLDisabled(settings);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -70,7 +64,7 @@ public class SSLServiceTests extends ElasticsearchTestCase {
|
|||
.put("shield.authc.ldap.mode", "ldap")
|
||||
.put("shield.authc.ldap.url", "ldap://example.com:389")
|
||||
.build();
|
||||
assertThat(SSLService.isSSLEnabled(settings), is(false));
|
||||
assertSSLDisabled(settings);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -78,9 +72,9 @@ public class SSLServiceTests extends ElasticsearchTestCase {
|
|||
Settings settings = settingsBuilder()
|
||||
.put("shield.transport.ssl", false)
|
||||
.put("shield.http.ssl", false)
|
||||
.put("shield.authc.ldap.mode", "active_dir") //default for missing URL is
|
||||
.put("shield.authc.ldap.mode", "active_dir") //SSL is on by default for a missing URL with active directory
|
||||
.build();
|
||||
assertThat(SSLService.isSSLEnabled(settings), is(true));
|
||||
assertSSLEnabled(settings);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -91,7 +85,7 @@ public class SSLServiceTests extends ElasticsearchTestCase {
|
|||
.put("shield.authc.ldap.mode", "ldap")
|
||||
.put("shield.authc.ldap.url", "ldaps://example.com:636")
|
||||
.build();
|
||||
assertThat(SSLService.isSSLEnabled(settings), is(true));
|
||||
assertSSLEnabled(settings);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -103,7 +97,7 @@ public class SSLServiceTests extends ElasticsearchTestCase {
|
|||
.put("shield.authc.ldap.mode", "ldap")
|
||||
.put("shield.authc.ldap.url", "ldap://example.com:389")
|
||||
.build();
|
||||
assertThat(SSLService.isSSLEnabled(settings), is(true));
|
||||
assertSSLEnabled(settings);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -114,6 +108,14 @@ public class SSLServiceTests extends ElasticsearchTestCase {
|
|||
.put("shield.authc.ldap.mode", "ldap")
|
||||
.put("shield.authc.ldap.url", "ldap://example.com:389")
|
||||
.build();
|
||||
assertSSLEnabled(settings);
|
||||
}
|
||||
|
||||
private void assertSSLEnabled(Settings settings) {
|
||||
assertThat(SSLService.isSSLEnabled(settings), is(true));
|
||||
}
|
||||
|
||||
private void assertSSLDisabled(Settings settings) {
|
||||
assertThat(SSLService.isSSLEnabled(settings), is(false));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,10 +160,13 @@ public abstract class ShieldIntegrationTest extends ElasticsearchIntegrationTest
|
|||
.put("shield.transport.ssl", true)
|
||||
.put("shield.ssl.keystore", store.getPath())
|
||||
.put("shield.ssl.keystore_password", password)
|
||||
.put("shield.ssl.truststore", store.getPath())
|
||||
.put("shield.ssl.truststore_password", password)
|
||||
.put("shield.http.ssl", true);
|
||||
|
||||
if (randomBoolean()) {
|
||||
builder.put("shield.ssl.truststore", store.getPath())
|
||||
.put("shield.ssl.truststore_password", password);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ import org.elasticsearch.http.HttpServerTransport;
|
|||
import org.elasticsearch.node.internal.InternalNode;
|
||||
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
|
||||
import org.elasticsearch.shield.test.ShieldIntegrationTest;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.net.ssl.*;
|
||||
|
@ -66,37 +65,6 @@ public class SslRequireAuthTests extends ShieldIntegrationTest {
|
|||
.build();
|
||||
}
|
||||
|
||||
|
||||
@Ignore //clients-certs are no longer configured for http
|
||||
@Test(expected = SSLHandshakeException.class)
|
||||
public void testThatRequireClientAuthRejectsWithoutCert() throws Exception {
|
||||
TrustManager[] trustAllCerts = new TrustManager[]{
|
||||
new X509TrustManager() {
|
||||
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void checkClientTrusted(
|
||||
java.security.cert.X509Certificate[] certs, String authType) {
|
||||
}
|
||||
|
||||
public void checkServerTrusted(
|
||||
java.security.cert.X509Certificate[] certs, String authType) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setupTrustManagers(trustAllCerts);
|
||||
|
||||
TransportAddress transportAddress = internalCluster().getInstance(HttpServerTransport.class).boundAddress().boundAddress();
|
||||
assertThat(transportAddress, is(instanceOf(InetSocketTransportAddress.class)));
|
||||
InetSocketTransportAddress inetSocketTransportAddress = (InetSocketTransportAddress) transportAddress;
|
||||
String url = String.format(Locale.ROOT, "https://%s:%s/", InetAddresses.toUriString(inetSocketTransportAddress.address().getAddress()), inetSocketTransportAddress.address().getPort());
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||
connection.connect();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatConnectionToHTTPWorks() throws Exception {
|
||||
File store = new File(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks").toURI());
|
||||
|
|
Loading…
Reference in New Issue