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:
parent
3a4af4c7bc
commit
a6bf836ae8
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -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())
|
||||
|
|
Loading…
Reference in New Issue