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:
c-a-m 2014-11-13 11:10:07 -07:00
parent a224b54973
commit eaac7ff250
8 changed files with 80 additions and 70 deletions

View File

@ -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
throw new ShieldSettingsException("Configured ldap protocols are not all equal " +
"(ldaps://.. and ldap://..): [" + Strings.arrayToCommaDelimitedString(ldapUrls) + "]");
}
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;
}
}

View File

@ -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));
}
}

View File

@ -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() {

View File

@ -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();

View File

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

View File

@ -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,19 +26,15 @@ 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()
new SSLService(settingsBuilder()
.put("shield.ssl.protocol", "non-existing")
.put("shield.ssl.keystore", testnodeStore.getPath())
.put("shield.ssl.keystore_password", "testnode")
.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));
}
}

View File

@ -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();
}

View File

@ -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());