SSL: Renamed settings, added SSLConfig tests

Settings for SSL now all start with `shield` as well. Changed documentation and tests to reflect this.

Original commit: elastic/x-pack-elasticsearch@9dd3bc865e
This commit is contained in:
Alexander Reelsen 2014-07-30 10:33:31 +02:00
parent 3a4af4c7bc
commit a6bf836ae8
5 changed files with 120 additions and 56 deletions

View File

@ -12,6 +12,7 @@ import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import javax.net.ssl.*;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.util.Arrays;
@ -34,14 +35,22 @@ public class SSLConfig {
}
public SSLConfig(Settings componentSettings, Settings settings) {
this.clientAuth = componentSettings.getAsBoolean("client.auth", settings.getAsBoolean("ssl.client.auth", true));
String keyStore = componentSettings.get("keystore", settings.get("ssl.keystore", System.getProperty("javax.net.ssl.keyStore")));
String keyStorePassword = componentSettings.get("keystore_password", settings.get("ssl.keystore_password", System.getProperty("javax.net.ssl.keyStorePassword")));
String keyStoreAlgorithm = componentSettings.get("keystore_algorithm", settings.get("ssl.keystore_algorithm", System.getProperty("ssl.KeyManagerFactory.algorithm")));
String trustStore = componentSettings.get("truststore", settings.get("ssl.truststore", System.getProperty("javax.net.ssl.trustStore")));
String trustStorePassword = componentSettings.get("truststore_password", settings.get("ssl.truststore_password", System.getProperty("javax.net.ssl.trustStorePassword")));
String trustStoreAlgorithm = componentSettings.get("truststore_algorithm", settings.get("ssl.truststore_algorithm", System.getProperty("ssl.TrustManagerFactory.algorithm")));
this.ciphers = componentSettings.getAsArray("ciphers", settings.getAsArray("ssl.ciphers", DEFAULT_CIPHERS));
this.clientAuth = componentSettings.getAsBoolean("client.auth", settings.getAsBoolean("shield.ssl.client.auth", true));
String keyStore = componentSettings.get("keystore", settings.get("shield.ssl.keystore", System.getProperty("javax.net.ssl.keyStore")));
String keyStorePassword = componentSettings.get("keystore_password", settings.get("shield.ssl.keystore_password", System.getProperty("javax.net.ssl.keyStorePassword")));
String keyStoreAlgorithm = componentSettings.get("keystore_algorithm", settings.get("shield.ssl.keystore_algorithm", System.getProperty("ssl.KeyManagerFactory.algorithm")));
String trustStore = componentSettings.get("truststore", settings.get("shield.ssl.truststore", System.getProperty("javax.net.ssl.trustStore")));
String trustStorePassword = componentSettings.get("truststore_password", settings.get("shield.ssl.truststore_password", System.getProperty("javax.net.ssl.trustStorePassword")));
String trustStoreAlgorithm = componentSettings.get("truststore_algorithm", settings.get("shield.ssl.truststore_algorithm", System.getProperty("ssl.TrustManagerFactory.algorithm")));
this.ciphers = componentSettings.getAsArray("ciphers", settings.getAsArray("shield.ssl.ciphers", DEFAULT_CIPHERS));
if (keyStore == null) {
throw new ElasticsearchException("SSL Enabled, but keystore unconfigured");
}
if (trustStore == null) {
throw new ElasticsearchException("SSL Enabled, but truststore unconfigured");
}
if (keyStoreAlgorithm == null) {
keyStoreAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
@ -53,6 +62,14 @@ public class SSLConfig {
logger.debug("using keyStore[{}], keyAlgorithm[{}], trustStore[{}], trustAlgorithm[{}]", keyStore, keyStoreAlgorithm, trustStore, trustStoreAlgorithm);
if (!new File(keyStore).exists()) {
throw new ElasticsearchSSLException("Keystore at path ["+ keyStore +"] does not exist");
}
if (!new File(trustStore).exists()) {
throw new ElasticsearchSSLException("Truststore at path ["+ keyStore +"] does not exist");
}
KeyStore ks = null;
KeyManagerFactory kmf = null;
try (FileInputStream in = new FileInputStream(keyStore)){
@ -64,7 +81,7 @@ public class SSLConfig {
kmf = KeyManagerFactory.getInstance(keyStoreAlgorithm);
kmf.init(ks, keyStorePassword.toCharArray());
} catch (Exception e) {
throw new ElasticsearchException("Failed to initialize a KeyManagerFactory", e);
throw new ElasticsearchSSLException("Failed to initialize a KeyManagerFactory", e);
}
TrustManager[] trustManagers = null;
@ -84,10 +101,11 @@ public class SSLConfig {
// Initialize sslContext
try {
sslContext = SSLContext.getInstance("TLS");
String algorithm = componentSettings.get("context_algorithm", settings.get("shield.ssl.context_algorithm", "TLS"));
sslContext = SSLContext.getInstance(algorithm);
sslContext.init(kmf.getKeyManagers(), trustManagers, null);
} catch (Exception e) {
throw new Error("Failed to initialize the SSLContext", e);
throw new ElasticsearchSSLException("Failed to initialize the SSLContext", e);
}
}

View File

@ -27,7 +27,7 @@ public class NettySSLHttpServerTransport extends NettyHttpServerTransport {
@Inject
public NettySSLHttpServerTransport(Settings settings, NetworkService networkService, BigArrays bigArrays) {
super(settings, networkService, bigArrays);
this.ssl = settings.getAsBoolean("http.ssl", false);
this.ssl = settings.getAsBoolean("shield.http.ssl", false);
}
@Override
@ -45,7 +45,7 @@ public class NettySSLHttpServerTransport extends NettyHttpServerTransport {
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = super.getPipeline();
if (ssl) {
SSLConfig sslConfig = new SSLConfig(settings.getByPrefix("http.ssl."));
SSLConfig sslConfig = new SSLConfig(settings.getByPrefix("shield.http.ssl."));
SSLEngine engine = sslConfig.createSSLEngine();
engine.setUseClientMode(false);
// TODO MAKE ME CONFIGURABLE

View File

@ -29,7 +29,7 @@ public class NettySSLTransport extends NettyTransport {
@Inject
public NettySSLTransport(Settings settings, ThreadPool threadPool, NetworkService networkService, BigArrays bigArrays, Version version) {
super(settings, threadPool, networkService, bigArrays, version);
this.ssl = settings.getAsBoolean("transport.tcp.ssl", false);
this.ssl = settings.getAsBoolean("shield.transport.ssl", false);
}
@Override
@ -48,7 +48,7 @@ public class NettySSLTransport extends NettyTransport {
public SslServerChannelPipelineFactory(NettyTransport nettyTransport) {
super(nettyTransport);
sslConfig = new SSLConfig(settings.getByPrefix("transport.tcp.ssl."));
sslConfig = new SSLConfig(settings.getByPrefix("shield.transport.ssl."));
// try to create an SSL engine, so that exceptions lead to early exit
sslConfig.createSSLEngine();
}
@ -73,7 +73,7 @@ public class NettySSLTransport extends NettyTransport {
public SslClientChannelPipelineFactory(NettyTransport transport) {
super(transport);
sslConfig = new SSLConfig(settings.getByPrefix("transport.tcp.ssl."));
sslConfig = new SSLConfig(settings.getByPrefix("shield.transport.ssl."));
// try to create an SSL engine, so that exceptions lead to early exit
sslConfig.createSSLEngine();
}

View File

@ -0,0 +1,68 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield.ssl;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Before;
import org.junit.Test;
import javax.net.ssl.SSLEngine;
import java.io.File;
import java.security.NoSuchAlgorithmException;
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
public class SSLConfigTests extends ElasticsearchTestCase {
File testnodeStore;
@Before
public void setup() throws Exception {
testnodeStore = new File(getClass().getResource("/certs/simple/testnode.jks").toURI());
}
@Test
public void testThatInvalidContextAlgoThrowsException() throws Exception {
try {
new SSLConfig(ImmutableSettings.EMPTY,
settingsBuilder()
.put("shield.ssl.context_algorithm", "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) {
assertThat(e.getRootCause(), instanceOf(NoSuchAlgorithmException.class));
}
}
@Test
public void testThatExactConfigOverwritesDefaultConfig() throws Exception {
Settings concreteSettings = settingsBuilder()
.put("ciphers", "TLS_RSA_WITH_AES_128_CBC_SHA")
.build();
Settings genericSettings = settingsBuilder()
.putArray("shield.ssl.ciphers", "TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA")
.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();
SSLConfig sslConfig = new SSLConfig(concreteSettings, genericSettings);
SSLEngine sslEngine = sslConfig.createSSLEngine();
assertThat(sslEngine.getEnabledCipherSuites().length, is(1));
}
}

View File

@ -42,31 +42,9 @@ import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoTimeout;
import static org.hamcrest.Matchers.*;
/**
* Created a testnode cert and a test client cert, which is imported into the keystore
*
* keytool -genkeypair -alias testnode -keystore testnode.jks -keyalg RSA -storepass testnode -keypass testnode -dname "cn=Elasticsearch Test Node, ou=elasticsearch, o=org"
* keytool -export -alias testnode -keystore testnode.jks -rfc -file testnode.cert -storepass testnode
*
* keytool -genkeypair -alias testclient -keystore testclient.jks -keyalg RSA -storepass testclient -keypass testclient -dname "cn=Elasticsearch Test Client, ou=elasticsearch, o=org"
* keytool -export -alias testclient -keystore testclient.jks -rfc -file testclient.cert -storepass testclient
*
* keytool -import -trustcacerts -alias testclient -file testclient.cert -keystore testnode.jks -storepass testnode -noprompt
* keytool -import -trustcacerts -alias testnode -file testnode.cert -keystore testclient.jks -storepass testclient -noprompt
*
*/
@ClusterScope(scope = Scope.SUITE, numDataNodes = 1, transportClientRatio = 0.0, numClientNodes = 0)
public class SslIntegrationTests extends ElasticsearchIntegrationTest {
/*
# transport.tcp.ssl.keystore: /path/to/the/keystore
# transport.tcp.ssl.keystore_password: password
# transport.tcp.ssl.keystore_algorithm: SunX509
#
# transport.tcp.ssl.truststore: /path/to/the/truststore
# transport.tcp.ssl.truststore_password: password
# transport.tcp.ssl.truststore_algorithm: PKIX
*/
@Override
protected Settings nodeSettings(int nodeOrdinal) {
File testnodeStore;
@ -82,16 +60,16 @@ public class SslIntegrationTests extends ElasticsearchIntegrationTest {
.put("discovery.zen.ping.multicast.ping.enabled", false)
// needed to ensure that netty transport is started
.put("node.mode", "network")
.put("transport.tcp.ssl", true)
.put("transport.tcp.ssl.keystore", testnodeStore.getPath())
.put("transport.tcp.ssl.keystore_password", "testnode")
.put("transport.tcp.ssl.truststore", testnodeStore.getPath())
.put("transport.tcp.ssl.truststore_password", "testnode")
.put("http.ssl", true)
.put("http.ssl.keystore", testnodeStore.getPath())
.put("http.ssl.keystore_password", "testnode")
.put("http.ssl.truststore", testnodeStore.getPath())
.put("http.ssl.truststore_password", "testnode")
.put("shield.transport.ssl", true)
.put("shield.transport.ssl.keystore", testnodeStore.getPath())
.put("shield.transport.ssl.keystore_password", "testnode")
.put("shield.transport.ssl.truststore", testnodeStore.getPath())
.put("shield.transport.ssl.truststore_password", "testnode")
.put("shield.http.ssl", true)
.put("shield.http.ssl.keystore", testnodeStore.getPath())
.put("shield.http.ssl.keystore_password", "testnode")
.put("shield.http.ssl.truststore", testnodeStore.getPath())
.put("shield.http.ssl.truststore_password", "testnode")
// SSL SETUP
.put("http.type", NettySSLHttpServerTransportModule.class.getName())
.put(TransportModule.TRANSPORT_TYPE_KEY, NettySSLTransportModule.class.getName())
@ -124,7 +102,7 @@ public class SslIntegrationTests extends ElasticsearchIntegrationTest {
public void testThatUnconfiguredCipchersAreRejected() {
// some randomly taken ciphers from SSLContext.getDefault().getSocketFactory().getSupportedCipherSuites()
// could be really randomized
Settings customSettings = getSettings("transport_client").put("transport.tcp.ssl.ciphers", new String[]{"TLS_ECDH_anon_WITH_RC4_128_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA"}).build();
Settings customSettings = getSettings("transport_client").put("shield.transport.ssl.ciphers", new String[]{"TLS_ECDH_anon_WITH_RC4_128_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA"}).build();
TransportClient transportClient = new TransportClient(customSettings);
@ -163,13 +141,13 @@ public class SslIntegrationTests extends ElasticsearchIntegrationTest {
@Test(expected = ElasticsearchSSLException.class)
public void testConnectNodeFailsWithWrongCipher() throws Exception {
Settings customSettings = getSettings("ssl_node").put("transport.tcp.ssl.ciphers", new String[]{"TLS_ECDH_anon_WITH_RC4_128_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA"}).build();
Settings customSettings = getSettings("ssl_node").put("shield.transport.ssl.ciphers", new String[]{"TLS_ECDH_anon_WITH_RC4_128_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA"}).build();
NodeBuilder.nodeBuilder().settings(customSettings).node().start();
}
@Test(expected = ElasticsearchSSLException.class)
public void testConnectNodeClientFailsWithWrongCipher() throws Exception {
Settings customSettings = getSettings("ssl_node").put("node.client", true).put("transport.tcp.ssl.ciphers", new String[]{"TLS_ECDH_anon_WITH_RC4_128_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA"}).build();
Settings customSettings = getSettings("ssl_node").put("node.client", true).put("shield.transport.ssl.ciphers", new String[]{"TLS_ECDH_anon_WITH_RC4_128_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA"}).build();
NodeBuilder.nodeBuilder().settings(customSettings).node().start();
}
@ -230,11 +208,11 @@ public class SslIntegrationTests extends ElasticsearchIntegrationTest {
return ImmutableSettings.settingsBuilder()
.put("node.name", name)
.put("transport.tcp.ssl", true)
.put("transport.tcp.ssl.keystore", testClientKeyStore.getPath())
.put("transport.tcp.ssl.keystore_password", "testclient")
.put("transport.tcp.ssl.truststore", testClientTrustStore .getPath())
.put("transport.tcp.ssl.truststore_password", "testclient")
.put("shield.transport.ssl", true)
.put("shield.transport.ssl.keystore", testClientKeyStore.getPath())
.put("shield.transport.ssl.keystore_password", "testclient")
.put("shield.transport.ssl.truststore", testClientTrustStore .getPath())
.put("shield.transport.ssl.truststore_password", "testclient")
.put("discovery.zen.ping.multicast.ping.enabled", false)
.put(TransportModule.TRANSPORT_TYPE_KEY, NettySSLTransportModule.class.getName())
//.put("plugin.types", SecurityPlugin.class.getName())