SSL/TLS: Allow control of SSL per profile

SSL can now be enabled or disabled per profile. This allows to have both
secured and unsecured client connections.

Closes elastic/elasticsearch#612

Original commit: elastic/x-pack-elasticsearch@53a7efa5b1
This commit is contained in:
jaymode 2015-01-22 07:39:37 -05:00
parent 2c55d85aa5
commit c5028f7384
3 changed files with 110 additions and 1 deletions

View File

@ -66,7 +66,8 @@ public class NettySecuredTransport extends NettyTransport {
@Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = super.getPipeline();
if (ssl) {
boolean profileSsl = profileSettings.getAsBoolean("shield.ssl", ssl);
if (profileSsl) {
SSLEngine serverEngine;
if (profileSettings.get("shield.truststore.path") != null) {
serverEngine = sslService.createSSLEngine(profileSettings.getByPrefix("shield."));

View File

@ -0,0 +1,80 @@
/*
* 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.transport.netty;
import org.elasticsearch.Version;
import org.elasticsearch.common.netty.OpenChannelsHandler;
import org.elasticsearch.common.netty.channel.ChannelPipelineFactory;
import org.elasticsearch.common.netty.handler.ssl.SslHandler;
import org.elasticsearch.common.network.NetworkService;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.shield.ssl.SSLService;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.netty.NettyTransport;
import org.junit.Before;
import org.junit.Test;
import java.lang.reflect.Field;
import java.nio.file.Path;
import java.nio.file.Paths;
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
import static org.mockito.Mockito.mock;
import static org.hamcrest.Matchers.*;
public class NettySecuredTransportTests extends ElasticsearchTestCase {
private SSLService sslService;
@Before
public void createSSLService() throws Exception {
Path testnodeStore = Paths.get(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks").toURI());
sslService = new SSLService(settingsBuilder()
.put("shield.ssl.keystore.path", testnodeStore)
.put("shield.ssl.keystore.password", "testnode")
.build());
}
@Test
public void testThatSSLCanBeDisabledByProfile() throws Exception {
Settings settings = ImmutableSettings.builder().put("shield.transport.ssl", true).build();
NettySecuredTransport transport = new NettySecuredTransport(settings, mock(ThreadPool.class), mock(NetworkService.class), mock(BigArrays.class), Version.CURRENT, null, sslService);
setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", ImmutableSettings.builder().put("shield.ssl", false).build());
assertThat(factory.getPipeline().get(SslHandler.class), nullValue());
}
@Test
public void testThatSSLCanBeEnabledByProfile() throws Exception {
Settings settings = ImmutableSettings.builder().put("shield.transport.ssl", false).build();
NettySecuredTransport transport = new NettySecuredTransport(settings, mock(ThreadPool.class), mock(NetworkService.class), mock(BigArrays.class), Version.CURRENT, null, sslService);
setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", ImmutableSettings.builder().put("shield.ssl", true).build());
assertThat(factory.getPipeline().get(SslHandler.class), notNullValue());
}
@Test
public void testThatProfileTakesDefaultSSLSetting() throws Exception {
Settings settings = ImmutableSettings.builder().put("shield.transport.ssl", true).build();
NettySecuredTransport transport = new NettySecuredTransport(settings, mock(ThreadPool.class), mock(NetworkService.class), mock(BigArrays.class), Version.CURRENT, null, sslService);
setOpenChannelsHandlerToMock(transport);
ChannelPipelineFactory factory = transport.configureServerChannelPipelineFactory("client", ImmutableSettings.EMPTY);
assertThat(factory.getPipeline().get(SslHandler.class), notNullValue());
}
/*
* We don't really need to start Netty for these tests, but we can't create a pipeline
* with a null handler. So we set it to a mock for this test using reflection.
*/
private void setOpenChannelsHandlerToMock(NettyTransport transport) throws Exception {
Field serverOpenChannels = NettyTransport.class.getDeclaredField("serverOpenChannels");
serverOpenChannels.setAccessible(true);
serverOpenChannels.set(transport, mock(OpenChannelsHandler.class));
}
}

View File

@ -23,21 +23,26 @@ import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilde
import static org.elasticsearch.shield.transport.support.TransportProfileUtil.getProfilePort;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope;
import static org.elasticsearch.test.ShieldSettingsSource.DEFAULT_USER_NAME;
import static org.elasticsearch.test.ShieldSettingsSource.DEFAULT_PASSWORD;
import static org.hamcrest.CoreMatchers.is;
@ClusterScope(scope = Scope.SUITE)
public class SslMultiPortTests extends ShieldIntegrationTest {
private static int randomClientPort;
private static int randomNonSslPort;
@BeforeClass
public static void getRandomPort() {
randomClientPort = randomIntBetween(49000, 65500); // ephemeral port
randomNonSslPort = randomIntBetween(49000, 65500);
}
@Override
protected Settings nodeSettings(int nodeOrdinal) {
String randomClientPortRange = randomClientPort + "-" + (randomClientPort+100);
String randomNonSslPortRange = randomNonSslPort + "-" + (randomNonSslPort+100);
File store;
try {
@ -54,6 +59,9 @@ public class SslMultiPortTests extends ShieldIntegrationTest {
.put("transport.profiles.client.bind_host", "localhost") // make sure this is "localhost", no matter if ipv4 or ipv6, but be consistent
.put("transport.profiles.client.shield.truststore.path", store.getAbsolutePath()) // settings for client truststore
.put("transport.profiles.client.shield.truststore.password", "testnode-client-profile")
.put("transport.profiles.no_ssl.port", randomNonSslPortRange)
.put("transport.profiles.no_ssl.bind_host", "localhost")
.put("transport.profiles.no_ssl.shield.ssl", "false")
.build();
}
@ -102,4 +110,24 @@ public class SslMultiPortTests extends ShieldIntegrationTest {
transportClient.admin().cluster().prepareHealth().get();
}
}
@Test
public void testThatTransportClientCanConnectToNoSslProfile() throws Exception {
Settings settings = ImmutableSettings.builder()
.put("shield.user", DEFAULT_USER_NAME + ":" + DEFAULT_PASSWORD)
.put("cluster.name", internalCluster().getClusterName())
.build();
try (TransportClient transportClient = new TransportClient(settings, false)) {
transportClient.addTransportAddress(new InetSocketTransportAddress("localhost", getProfilePort("no_ssl", internalCluster())));
assertGreenClusterState(transportClient);
}
}
@Test(expected = NoNodeAvailableException.class)
public void testThatStandardTransportClientCannotConnectToNoSslProfile() throws Exception {
try (TransportClient transportClient = createTransportClient(ImmutableSettings.EMPTY)) {
transportClient.addTransportAddress(new InetSocketTransportAddress("localhost", getProfilePort("no_ssl", internalCluster())));
assertGreenClusterState(transportClient);
}
}
}