Testing: Create ShieldIntegrationTest that other test inherit from

This integration test contains all of the configuration needed to run with shield
and will be the base for further improvements.

Closes elastic/elasticsearch#36
Relates elastic/elasticsearch#31

Original commit: elastic/x-pack-elasticsearch@fe77f4d32b
This commit is contained in:
Alexander Reelsen 2014-08-28 09:28:51 +02:00
parent fcd3a89a3d
commit d0e377ff76
9 changed files with 325 additions and 343 deletions

View File

@ -26,29 +26,35 @@ import org.elasticsearch.shield.transport.netty.NettySecuredTransportModule;
public class SecurityModule extends AbstractModule implements SpawnModules, PreProcessModule {
private final Settings settings;
private final boolean isClient;
private final boolean isShieldEnabled;
public SecurityModule(Settings settings) {
this.settings = settings;
this.isClient = settings.getAsBoolean("node.client", false);
this.isShieldEnabled = settings.getComponentSettings(SecurityModule.class).getAsBoolean("enabled", true);
}
@Override
public void processModule(Module module) {
if (module instanceof ActionModule) {
if (module instanceof ActionModule && isShieldEnabled && !isClient) {
((ActionModule) module).registerFilter(SecurityFilter.Action.class);
}
}
@Override
public Iterable<? extends Module> spawnModules() {
// don't spawn module in client mode
if (settings.getAsBoolean("node.client", false)) {
// don't spawn modules if shield is explicitly disabled
if (!isShieldEnabled) {
return ImmutableList.of();
}
// don't spawn modules if shield is explicitly disabled
if (!settings.getComponentSettings(SecurityModule.class).getAsBoolean("enabled", true)) {
return ImmutableList.of();
// spawn needed parts in client mode
if (isClient) {
return ImmutableList.of(
new N2NModule(),
new SecuredTransportModule()
);
}
return ImmutableList.of(
@ -58,7 +64,7 @@ public class SecurityModule extends AbstractModule implements SpawnModules, PreP
new N2NModule(),
new NettySecuredHttpServerTransportModule(),
new NettySecuredTransportModule(),
new SecuredTransportModule(settings));
new SecuredTransportModule());
}
@Override

View File

@ -6,7 +6,6 @@
package org.elasticsearch.shield.transport;
import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.SecurityFilter;
/**
@ -14,16 +13,8 @@ import org.elasticsearch.shield.SecurityFilter;
*/
public class SecuredTransportModule extends AbstractModule {
private final Settings settings;
public SecuredTransportModule(Settings settings) {
this.settings = settings;
}
@Override
protected void configure() {
if (!settings.getAsBoolean("node.client", false)) {
bind(TransportFilter.class).to(SecurityFilter.Transport.class).asEagerSingleton();
}
bind(TransportFilter.class).to(SecurityFilter.Transport.class).asEagerSingleton();
}
}

View File

@ -30,10 +30,10 @@ public class SecuredMessageChannelHandler extends MessageChannelHandler {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
logger.debug("SSL / TLS handshake completed for the channel.");
logger.debug("SSL / TLS handshake completed for channel", ctx.getName());
ctx.sendUpstream(e);
} else {
logger.error("SSL / TLS handshake failed, closing the channel");
logger.error("SSL / TLS handshake failed, closing channel", ctx.getName());
future.getChannel().close();
throw new ElasticsearchSSLException("SSL / TLS handshake failed, closing the channel", future.getCause());
}

View File

@ -52,9 +52,6 @@ public class IPFilteringN2NAuthenticatorTests extends ElasticsearchTestCase {
@Before
public void init() throws Exception {
configFile = temporaryFolder.newFile();
Settings resourceWatcherServiceSettings = settingsBuilder().put("watcher.interval.medium", TimeValue.timeValueMillis(200)).build();
resourceWatcherService = new ResourceWatcherService(resourceWatcherServiceSettings, new ThreadPool("resourceWatcher")).start();
settings = settingsBuilder().put("shield.n2n.file", configFile.getPath()).build();
}
@After
@ -66,75 +63,75 @@ public class IPFilteringN2NAuthenticatorTests extends ElasticsearchTestCase {
public void testThatIpV4AddressesCanBeProcessed() throws Exception {
writeConfigFile("allow: 127.0.0.1\ndeny: 10.0.0.0/8");
assertAddressIsAllowed(ipFilteringN2NAuthenticator, "127.0.0.1");
assertAddressIsDenied(ipFilteringN2NAuthenticator, "10.2.3.4");
assertAddressIsAllowed("127.0.0.1");
assertAddressIsDenied("10.2.3.4");
}
@Test
public void testThatIpV6AddressesCanBeProcessed() throws Exception {
writeConfigFile("allow: 2001:0db8:1234::/48\ndeny: 1234:0db8:85a3:0000:0000:8a2e:0370:7334");
assertAddressIsAllowed(ipFilteringN2NAuthenticator, "2001:0db8:1234:0000:0000:8a2e:0370:7334");
assertAddressIsDenied(ipFilteringN2NAuthenticator, "1234:0db8:85a3:0000:0000:8a2e:0370:7334");
assertAddressIsAllowed("2001:0db8:1234:0000:0000:8a2e:0370:7334");
assertAddressIsDenied("1234:0db8:85a3:0000:0000:8a2e:0370:7334");
}
@Test
public void testThatHostnamesCanBeProcessed() throws Exception {
writeConfigFile("allow: localhost\ndeny: '*.google.com'");
assertAddressIsAllowed(ipFilteringN2NAuthenticator, "127.0.0.1");
assertAddressIsDenied(ipFilteringN2NAuthenticator, "173.194.70.100");
assertAddressIsAllowed("127.0.0.1");
assertAddressIsDenied("173.194.70.100");
}
@Test
public void testThatFileDeletionResultsInAllowingAll() throws Exception {
writeConfigFile("allow: 127.0.0.1");
assertAddressIsAllowed(ipFilteringN2NAuthenticator, "127.0.0.1");
assertAddressIsAllowed("127.0.0.1");
configFile.delete();
assertThat(configFile.exists(), is(false));
sleep(250);
assertAddressIsDenied(ipFilteringN2NAuthenticator, "127.0.0.1");
assertAddressIsDenied("127.0.0.1");
}
@Test
public void testThatAnAllowAllAuthenticatorWorks() throws Exception {
writeConfigFile("allow: all");
assertAddressIsAllowed(ipFilteringN2NAuthenticator, "127.0.0.1");
assertAddressIsAllowed(ipFilteringN2NAuthenticator, "173.194.70.100");
assertAddressIsAllowed("127.0.0.1");
assertAddressIsAllowed("173.194.70.100");
}
@Test
public void testThatCommaSeparatedValuesWork() throws Exception {
writeConfigFile("allow: 192.168.23.0/24, localhost\ndeny: all");
assertAddressIsAllowed(ipFilteringN2NAuthenticator, "192.168.23.1");
assertAddressIsAllowed(ipFilteringN2NAuthenticator, "127.0.0.1");
assertAddressIsDenied(ipFilteringN2NAuthenticator, "10.1.2.3");
assertAddressIsAllowed("192.168.23.1");
assertAddressIsAllowed("127.0.0.1");
assertAddressIsDenied("10.1.2.3");
}
@Test
public void testThatOrderIsImportant() throws Exception {
writeConfigFile("deny: localhost\nallow: localhost");
assertAddressIsDenied(ipFilteringN2NAuthenticator, "127.0.0.1");
assertAddressIsDenied("127.0.0.1");
}
@Test
public void testThatOrderIsImportantViceVersa() throws Exception {
writeConfigFile("allow: localhost\ndeny: localhost");
assertAddressIsAllowed(ipFilteringN2NAuthenticator, "127.0.0.1");
assertAddressIsAllowed("127.0.0.1");
}
@Test
public void testThatEmptyFileDoesNotLeadIntoLoop() throws Exception {
writeConfigFile("# \n\n");
assertAddressIsDenied(ipFilteringN2NAuthenticator, "127.0.0.1");
assertAddressIsDenied("127.0.0.1");
}
@Test(expected = ElasticsearchParseException.class)
@ -145,17 +142,20 @@ public class IPFilteringN2NAuthenticatorTests extends ElasticsearchTestCase {
private void writeConfigFile(String data) throws IOException {
Files.write(data.getBytes(Charsets.UTF_8), configFile);
Settings resourceWatcherServiceSettings = settingsBuilder().put("watcher.interval.medium", TimeValue.timeValueMillis(200)).build();
resourceWatcherService = new ResourceWatcherService(resourceWatcherServiceSettings, new ThreadPool("resourceWatcher")).start();
settings = settingsBuilder().put("shield.n2n.file", configFile.getPath()).build();
ipFilteringN2NAuthenticator = new IPFilteringN2NAuthenticator(settings, new Environment(), resourceWatcherService);
}
private void assertAddressIsAllowed(IPFilteringN2NAuthenticator ipFilteringN2NAuthenticator, String ... inetAddresses) {
private void assertAddressIsAllowed(String ... inetAddresses) {
for (String inetAddress : inetAddresses) {
String message = String.format(Locale.ROOT, "Expected address %s to be allowed", inetAddress);
assertThat(message, ipFilteringN2NAuthenticator.authenticate(NULL_PRINCIPAL, InetAddresses.forString(inetAddress), 1024), is(true));
}
}
private void assertAddressIsDenied(IPFilteringN2NAuthenticator ipFilteringN2NAuthenticator, String ... inetAddresses) {
private void assertAddressIsDenied(String ... inetAddresses) {
for (String inetAddress : inetAddresses) {
String message = String.format(Locale.ROOT, "Expected address %s to be denied", inetAddress);
assertThat(message, ipFilteringN2NAuthenticator.authenticate(NULL_PRINCIPAL, InetAddresses.forString(inetAddress), 1024), is(false));

View File

@ -7,56 +7,48 @@ package org.elasticsearch.shield.n2n;
import com.google.common.base.Charsets;
import com.google.common.net.InetAddresses;
import org.elasticsearch.common.os.OsUtils;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.shield.plugin.SecurityPlugin;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.shield.test.ShieldIntegrationTest;
import org.elasticsearch.transport.Transport;
import org.junit.Ignore;
import org.junit.Test;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.net.SocketException;
import java.net.URL;
import java.util.Locale;
import static org.apache.lucene.util.LuceneTestCase.AwaitsFix;
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
/**
*
*/
@ElasticsearchIntegrationTest.ClusterScope(scope = ElasticsearchIntegrationTest.Scope.SUITE, numDataNodes = 1, transportClientRatio = 0.0, numClientNodes = 0)
@AwaitsFix(bugUrl = "https://github.com/elasticsearch/elasticsearch-shield/issues/36")
public class IpFilteringIntegrationTests extends ElasticsearchIntegrationTest {
// no client nodes, no transport nodes, as they all get rejected on network connections
@ClusterScope(scope = Scope.SUITE, numDataNodes = 1, numClientNodes = 0, transportClientRatio = 0.0)
public class IpFilteringIntegrationTests extends ShieldIntegrationTest {
private static final String CONFIG_IPFILTER_DENY_ALL = "deny: all\n";
@Override
protected Settings nodeSettings(int nodeOrdinal) {
ImmutableSettings.Builder builder = settingsBuilder()
.put(super.nodeSettings(nodeOrdinal))
.put("discovery.zen.ping.multicast.ping.enabled", false)
.put("node.mode", "network")
// todo http tests fail without an explicit IP (needs investigation)
.put("network.host", randomBoolean() ? "127.0.0.1" : "::1")
.put("plugin.types", SecurityPlugin.class.getName());
//.put("shield.n2n.file", configFile.getPath())
File folder = newFolder();
if (OsUtils.MAC) {
builder.put("network.host", randomBoolean() ? "127.0.0.1" : "::1");
}
return builder.build();
return settingsBuilder()
.put(super.nodeSettings(nodeOrdinal))
.put("shield.n2n.file", writeFile(folder, "ip_filter.yml", CONFIG_IPFILTER_DENY_ALL))
.build();
}
@Test(expected = SocketException.class)
public void testThatIpFilteringIsIntegratedIntoNettyPipelineViaHttp() throws Exception {
TransportAddress transportAddress = internalCluster().getInstance(HttpServerTransport.class).boundAddress().boundAddress();
TransportAddress transportAddress = internalCluster().getDataNodeInstance(HttpServerTransport.class).boundAddress().boundAddress();
assertThat(transportAddress, is(instanceOf(InetSocketTransportAddress.class)));
InetSocketTransportAddress inetSocketTransportAddress = (InetSocketTransportAddress) transportAddress;
String url = String.format(Locale.ROOT, "http://%s:%s/", InetAddresses.toUriString(inetSocketTransportAddress.address().getAddress()), inetSocketTransportAddress.address().getPort());
@ -67,17 +59,22 @@ public class IpFilteringIntegrationTests extends ElasticsearchIntegrationTest {
logger.info("HTTP connection response code [{}]", connection.getResponseCode());
}
@Ignore("Need to investigate further, why this does not fail")
@Test(expected = SocketException.class)
public void testThatIpFilteringIsIntegratedIntoNettyPipelineViaTransportClient() throws Exception {
InetSocketTransportAddress transportAddress = (InetSocketTransportAddress) internalCluster().getDataNodeInstance(Transport.class).boundAddress().boundAddress();
// TODO: This works and I do not understand why, telnet breaks...
Socket socket = new Socket(transportAddress.address().getAddress(), transportAddress.address().getPort());
socket.getOutputStream().write("foo".getBytes(Charsets.UTF_8));
socket.getOutputStream().flush();
socket.getInputStream().close();
assertThat(socket.isConnected(), is(true));
socket.close();
try (Socket socket = new Socket()) {
logger.info("Connecting to {}", transportAddress.address());
socket.connect(transportAddress.address(), 500);
assertThat(socket.isConnected(), is(true));
try (OutputStream os = socket.getOutputStream()) {
os.write("foo".getBytes(Charsets.UTF_8));
os.flush();
}
try (InputStream is = socket.getInputStream()) {
is.read();
}
}
}
}

View File

@ -7,101 +7,23 @@ package org.elasticsearch.shield.plugin;
import org.elasticsearch.action.admin.cluster.node.info.NodeInfo;
import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
import org.elasticsearch.common.os.OsUtils;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.transport.SecuredTransportService;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.test.junit.annotations.TestLogging;
import org.elasticsearch.transport.TransportModule;
import org.junit.Rule;
import org.elasticsearch.shield.test.ShieldIntegrationTest;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.apache.lucene.util.LuceneTestCase.AwaitsFix;
import static org.elasticsearch.shield.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
/**
*
*/
@ClusterScope(scope = Scope.SUITE, numDataNodes = 2, randomDynamicTemplates = false)
@AwaitsFix(bugUrl = "https://github.com/elasticsearch/elasticsearch-shield/issues/36")
public class ShieldPluginTests extends ElasticsearchIntegrationTest {
@Rule
public TemporaryFolder tmpFolder = new TemporaryFolder();
@Override
protected Settings nodeSettings(int nodeOrdinal) {
File folder = newFolder();
ImmutableSettings.Builder builder = ImmutableSettings.builder()
.put("plugin.types", SecurityPlugin.class.getName())
.put(super.nodeSettings(nodeOrdinal))
.put("shield.audit.enabled", true)
.put("shield.authc.esusers.files.users", copyFile(folder, "users"))
.put("shield.authc.esusers.files.users_roles", copyFile(folder, "users_roles"))
.put("shield.authz.store.files.roles", copyFile(folder, "roles.yml"))
.put("shield.n2n.file", copyFile(folder, "ip_filter.yml"))
.put(TransportModule.TRANSPORT_SERVICE_TYPE_KEY, SecuredTransportService.class.getName())
// for the test internal node clients
.put("request.headers.Authorization", basicAuthHeaderValue("test_user", "changeme".toCharArray()));
if (OsUtils.MAC) {
builder.put("network.host", randomBoolean() ? "127.0.0.1" : "::1");
}
return builder.build();
}
@Override
protected Settings transportClientSettings() {
return ImmutableSettings.builder()
.put("request.headers.Authorization", basicAuthHeaderValue("test_user", "changeme".toCharArray()))
.build();
}
public class ShieldPluginTests extends ShieldIntegrationTest {
@Test
@TestLogging("_root:INFO,plugins.PluginsService:TRACE")
public void testThatPluginIsLoaded() {
logger.info("--> Getting nodes info");
NodesInfoResponse nodeInfos = internalCluster().transportClient().admin().cluster().prepareNodesInfo().get();
logger.info("--> Checking nodes info");
logger.info("--> Checking nodes info that shield plugin is loaded");
for (NodeInfo nodeInfo : nodeInfos.getNodes()) {
assertThat(nodeInfo.getPlugins().getInfos(), hasSize(1));
assertThat(nodeInfo.getPlugins().getInfos().get(0).getName(), is(SecurityPlugin.NAME));
}
}
private File newFolder() {
try {
return tmpFolder.newFolder();
} catch (IOException ioe) {
logger.error("could not create temporary folder", ioe);
fail("could not create temporary folder");
return null;
}
}
private String copyFile(File folder, String name) {
Path file = folder.toPath().resolve(name);
try {
Files.copy(getClass().getResourceAsStream(name), file);
} catch (IOException ioe) {
logger.error("could not copy temporary configuration file [" + name + "]", ioe);
fail("could not copy temporary configuration file [" + name + "]");
}
return file.toAbsolutePath().toString();
}
}

View File

@ -0,0 +1,167 @@
/*
* 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.test;
import com.google.common.base.Charsets;
import com.google.common.net.InetAddresses;
import org.apache.lucene.util.AbstractRandomizedTest;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.os.OsUtils;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.plugins.PluginsService;
import org.elasticsearch.shield.plugin.SecurityPlugin;
import org.elasticsearch.shield.transport.netty.NettySecuredTransport;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.transport.TransportModule;
import org.junit.ClassRule;
import org.junit.Ignore;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
import static org.elasticsearch.shield.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
@Ignore
@AbstractRandomizedTest.Integration
@ClusterScope(scope = Scope.SUITE, numDataNodes = 1, numClientNodes = 0)
public abstract class ShieldIntegrationTest extends ElasticsearchIntegrationTest {
private static final String DEFAULT_USER_NAME = "test_user";
private static final String DEFAULT_PASSWORD = "changeme";
private static final String DEFAULT_ROLE = "user";
public static final String CONFIG_IPFILTER_ALLOW_ALL = "allow: all\n";
public static final String CONFIG_STANDARD_USER = DEFAULT_USER_NAME + ":{plain}" + DEFAULT_PASSWORD + "\n";
public static final String CONFIG_STANDARD_USER_ROLES = DEFAULT_USER_NAME + ":" + DEFAULT_ROLE + "\n";
public static final String CONFIG_ROLE_ALLOW_ALL = "user:\n" +
" cluster: ALL\n" +
" indices:\n" +
" '.*': ALL\n";
@ClassRule
public static TemporaryFolder tmpFolder = new TemporaryFolder();
@Override
protected Settings nodeSettings(int nodeOrdinal) {
File folder = newFolder();
ImmutableSettings.Builder builder = ImmutableSettings.builder()
.put("request.headers.Authorization", basicAuthHeaderValue(getClientUsername(), getClientPassword().toCharArray()))
.put("discovery.zen.ping.multicast.enabled", false)
.put("discovery.type", "zen")
.put("node.mode", "network")
.put("plugin.types", SecurityPlugin.class.getName())
.put("shield.authc.esusers.files.users", writeFile(folder, "users", CONFIG_STANDARD_USER))
.put("shield.authc.esusers.files.users_roles", writeFile(folder, "users_roles", CONFIG_STANDARD_USER_ROLES))
.put("shield.authz.store.files.roles", writeFile(folder, "roles.yml", CONFIG_ROLE_ALLOW_ALL))
.put("shield.n2n.file", writeFile(folder, "ip_filter.yml", CONFIG_IPFILTER_ALLOW_ALL))
.put(getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks", "testnode"))
.put("shield.audit.enabled", true);
if (OsUtils.MAC) {
builder.put("network.host", randomBoolean() ? "127.0.0.1" : "::1");
}
return builder.build();
}
@Override
protected Settings transportClientSettings() {
return ImmutableSettings.builder()
.put("request.headers.Authorization", basicAuthHeaderValue(getClientUsername(), getClientPassword().toCharArray()))
.put(TransportModule.TRANSPORT_TYPE_KEY, NettySecuredTransport.class.getName())
.put("plugins." + PluginsService.LOAD_PLUGIN_FROM_CLASSPATH, false)
.put("node.mode", "network")
.put("discovery.zen.ping.multicast.ping.enabled", false)
.putArray("discovery.zen.ping.unicast.hosts", getUnicastHostAddress())
.put(getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks", "testclient"))
.build();
}
protected String writeFile(File folder, String name, String content) {
Path file = folder.toPath().resolve(name);
try {
com.google.common.io.Files.write(content.getBytes(Charsets.UTF_8), file.toFile());
} catch (IOException e) {
throw new ElasticsearchException("Error writing file in test", e);
}
return file.toFile().getAbsolutePath();
}
protected String getUnicastHostAddress() {
TransportAddress transportAddress = internalCluster().getDataNodeInstance(Transport.class).boundAddress().publishAddress();
assertThat(transportAddress, instanceOf(InetSocketTransportAddress.class));
InetSocketTransportAddress address = (InetSocketTransportAddress) transportAddress;
return InetAddresses.toAddrString(address.address().getAddress()) + ":" + address.address().getPort();
}
protected String getClientUsername() {
return DEFAULT_USER_NAME;
};
protected String getClientPassword() {
return DEFAULT_PASSWORD;
}
protected Settings getSSLSettingsForStore(String resourcePathToStore, String password) {
File store;
try {
store = new File(getClass().getResource(resourcePathToStore).toURI());
assertThat(store.exists(), is(true));
} catch (Exception e) {
throw new RuntimeException(e);
}
ImmutableSettings.Builder builder = settingsBuilder()
.put("shield.transport.ssl", true)
.put("shield.transport.ssl.keystore", store.getPath())
.put("shield.transport.ssl.keystore_password", password)
.put("shield.transport.ssl.truststore", store.getPath())
.put("shield.transport.ssl.truststore_password", password)
.put("shield.http.ssl", true)
.put("shield.http.ssl.require.client.auth", false)
.put("shield.http.ssl.keystore", store.getPath())
.put("shield.http.ssl.keystore_password", password)
.put("shield.http.ssl.truststore", store.getPath())
.put("shield.http.ssl.truststore_password", password);
return builder.build();
}
protected File newFolder() {
try {
return tmpFolder.newFolder();
} catch (IOException ioe) {
logger.error("could not create temporary folder", ioe);
fail("could not create temporary folder");
return null;
}
}
protected String copyFile(File folder, String name) {
Path file = folder.toPath().resolve(name);
try {
Files.copy(getClass().getResourceAsStream(name), file);
} catch (IOException ioe) {
logger.error("could not copy temporary configuration file [" + name + "]", ioe);
fail("could not copy temporary configuration file [" + name + "]");
}
return file.toAbsolutePath().toString();
}
}

View File

@ -5,117 +5,76 @@
*/
package org.elasticsearch.shield.transport.ssl;
import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.base.Charsets;
import com.google.common.io.Files;
import com.google.common.net.InetAddresses;
import com.google.common.base.Charsets;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthStatus;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.NoNodeAvailableException;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.os.OsUtils;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.net.InetAddresses;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.node.Node;
import org.elasticsearch.node.NodeBuilder;
import org.elasticsearch.shield.plugin.SecurityPlugin;
import org.elasticsearch.plugins.PluginsService;
import org.elasticsearch.shield.test.ShieldIntegrationTest;
import org.elasticsearch.shield.transport.netty.NettySecuredTransport;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.test.junit.annotations.TestLogging;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.transport.TransportModule;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import javax.net.ssl.*;
import java.io.File;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Locale;
import static org.apache.lucene.util.LuceneTestCase.AwaitsFix;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope;
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
import static org.elasticsearch.shield.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoTimeout;
import static org.hamcrest.Matchers.*;
@ClusterScope(scope = Scope.SUITE, numDataNodes = 1, transportClientRatio = 0.0, numClientNodes = 0)
@AwaitsFix(bugUrl = "https://github.com/elasticsearch/elasticsearch-shield/issues/36")
public class SslIntegrationTests extends ElasticsearchIntegrationTest {
@ClassRule
public static TemporaryFolder temporaryFolder = new TemporaryFolder();
private static File ipFilterFile;
@BeforeClass
public static void writeAllowAllIpFilterFile() throws Exception {
ipFilterFile = temporaryFolder.newFile();
Files.write("allow: all\n".getBytes(com.google.common.base.Charsets.UTF_8), ipFilterFile);
}
@Override
protected Settings nodeSettings(int nodeOrdinal) {
File testnodeStore;
try {
testnodeStore = new File(getClass().getResource("certs/simple/testnode.jks").toURI());
assertThat(testnodeStore.exists(), is(true));
} catch (Exception e) {
throw new RuntimeException(e);
}
ImmutableSettings.Builder builder = ImmutableSettings.settingsBuilder()
.put(super.nodeSettings(nodeOrdinal))
.put("discovery.zen.ping.multicast.ping.enabled", false)
//
.put("shield.authz.file.roles", "not/existing")
// needed to ensure that netty transport is started
.put("node.mode", "network")
.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.require.client.auth", false)
.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("plugin.types", SecurityPlugin.class.getName())
.put("shield.n2n.file", ipFilterFile.getPath());
if (OsUtils.MAC) {
builder.put("network.host", randomBoolean() ? "127.0.0.1" : "::1");
}
return builder.build();
}
public class SslIntegrationTests extends ShieldIntegrationTest {
@Test
@TestLogging("_root:INFO,org.elasticsearch.test:TRACE, org.elasticsearch.client.transport:DEBUG,org.elasticsearch.shield:TRACE")
public void testThatTransportClientCanConnectToNodeViaSsl() throws Exception {
TransportClient transportClient = new TransportClient(getSettings("transport_client").build(), false);
TransportAddress transportAddress = internalCluster().getInstance(Transport.class).boundAddress().boundAddress();
transportClient.addTransportAddress(transportAddress);
public void testThatInternallyCreatedTransportClientCanConnect() throws Exception {
Client transportClient = internalCluster().transportClient();
assertGreenClusterState(transportClient);
}
@Test(expected = ElasticsearchSSLException.class)
@TestLogging("_root:INFO,org.elasticsearch.client.transport:DEBUG")
public void testThatUnconfiguredCipchersAreRejected() {
// some randomly taken ciphers from SSLContext.getDefault().getSocketFactory().getSupportedCipherSuites()
// could be really randomized
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();
@Test
public void testThatProgrammaticallyCreatedTransportClientCanConnect() throws Exception {
Settings settings = settingsBuilder()
.put(transportClientSettings())
.put("name", "programmatic_transport_client_")
.put("cluster.name", internalCluster().getClusterName())
.build();
TransportClient transportClient = new TransportClient(customSettings);
try (TransportClient transportClient = new TransportClient(settings, false)) {
TransportAddress transportAddress = internalCluster().getInstance(Transport.class).boundAddress().boundAddress();
transportClient.addTransportAddress(transportAddress);
assertGreenClusterState(transportClient);
}
try (TransportClient transportClient = new TransportClient(settings, true)) {
TransportAddress transportAddress = internalCluster().getInstance(Transport.class).boundAddress().boundAddress();
transportClient.addTransportAddress(transportAddress);
assertGreenClusterState(transportClient);
}
}
// no SSL exception as this is the exception is returned when connecting
@Test(expected = NoNodeAvailableException.class)
public void testThatUnconfiguredCipchersAreRejected() {
TransportClient transportClient = new TransportClient(settingsBuilder()
.put(transportClientSettings())
.put("name", "programmatic_transport_client")
.put("cluster.name", internalCluster().getClusterName())
.putArray("shield.transport.ssl.ciphers", new String[]{"TLS_ECDH_anon_WITH_RC4_128_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA"})
.build());
TransportAddress transportAddress = internalCluster().getInstance(Transport.class).boundAddress().boundAddress();
transportClient.addTransportAddress(transportAddress);
@ -124,9 +83,19 @@ public class SslIntegrationTests extends ElasticsearchIntegrationTest {
}
@Test
@TestLogging("_root:DEBUG")
public void testConnectNodeWorks() throws Exception {
try (Node node = NodeBuilder.nodeBuilder().settings(getSettings("ssl_node")).node().start()) {
Settings settings = settingsBuilder()
.put("name", "programmatic_node")
.put("cluster.name", internalCluster().getClusterName())
.put("request.headers.Authorization", basicAuthHeaderValue(getClientUsername(), getClientPassword().toCharArray()))
.put(TransportModule.TRANSPORT_TYPE_KEY, NettySecuredTransport.class.getName())
.put("plugins." + PluginsService.LOAD_PLUGIN_FROM_CLASSPATH, false)
.put(getSSLSettingsForStore("certs/simple/testclient.jks", "testclient"))
.build();
try (Node node = NodeBuilder.nodeBuilder().settings(settings).node()) {
try (Client client = node.client()) {
assertGreenClusterState(client);
}
@ -135,34 +104,30 @@ public class SslIntegrationTests extends ElasticsearchIntegrationTest {
@Test
public void testConnectNodeClientWorks() throws Exception {
// no multicast, good old discovery
TransportAddress transportAddress = internalCluster().getInstance(Transport.class).boundAddress().boundAddress();
assertThat(transportAddress, is(instanceOf(InetSocketTransportAddress.class)));
InetSocketTransportAddress inetSocketTransportAddress = (InetSocketTransportAddress) transportAddress;
Settings.Builder settingsBuilder = getSettings("node_client")
.put("node.client", true)
.put("discovery.zen.ping.multicast.ping.enabled", false)
.put("discovery.zen.ping.unicast.hosts", inetSocketTransportAddress.address().getHostString() + ":" + inetSocketTransportAddress.address().getPort());
Settings settings = settingsBuilder()
.put("name", "programmatic_node_client")
.put("cluster.name", internalCluster().getClusterName())
.put("node.mode", "network")
try (Node node = NodeBuilder.nodeBuilder().settings(settingsBuilder).node().start()) {
.put("discovery.zen.ping.multicast.enabled", false)
.put("discovery.type", "zen")
.putArray("discovery.zen.ping.unicast.hosts", getUnicastHostAddress())
.put("request.headers.Authorization", basicAuthHeaderValue(getClientUsername(), getClientPassword().toCharArray()))
.put(TransportModule.TRANSPORT_TYPE_KEY, NettySecuredTransport.class.getName())
.put("plugins." + PluginsService.LOAD_PLUGIN_FROM_CLASSPATH, false)
.put("shield.n2n.file", writeFile(newFolder(), "ip_filter.yml", ShieldIntegrationTest.CONFIG_IPFILTER_ALLOW_ALL))
.put(getSSLSettingsForStore("certs/simple/testclient.jks", "testclient"))
.build();
try (Node node = NodeBuilder.nodeBuilder().settings(settings).client(true).node()) {
try (Client client = node.client()) {
assertGreenClusterState(client);
}
}
}
@Test(expected = ElasticsearchSSLException.class)
public void testConnectNodeFailsWithWrongCipher() throws Exception {
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("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
public void testThatConnectionToHTTPWorks() throws Exception {
TrustManager[] trustAllCerts = new TrustManager[]{
@ -206,36 +171,9 @@ public class SslIntegrationTests extends ElasticsearchIntegrationTest {
assertThat(data, containsString("You Know, for Search"));
}
private ImmutableSettings.Builder getSettings(String name) {
File testClientKeyStore;
File testClientTrustStore;
try {
testClientKeyStore = new File(getClass().getResource("certs/simple/testclient.jks").toURI());
testClientTrustStore = new File(getClass().getResource("certs/simple/testclient.jks").toURI());
} catch (Exception e) {
throw new RuntimeException(e);
}
assertThat(testClientKeyStore.exists(), is(true));
assertThat(testClientTrustStore.exists(), is(true));
return ImmutableSettings.settingsBuilder()
.put("node.name", name)
.put("plugins.load_classpath_plugins", false)
.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, NettySecuredTransport.class.getName())
.put("shield.n2n.file", ipFilterFile.getPath())
.put("cluster.name", internalCluster().getClusterName());
}
private void assertGreenClusterState(Client client) {
ClusterHealthResponse clusterHealthResponse = client.admin().cluster().prepareHealth().get();
assertNoTimeout(clusterHealthResponse);
assertThat(clusterHealthResponse.getStatus(), is(ClusterHealthStatus.GREEN));
}
}

View File

@ -6,22 +6,15 @@
package org.elasticsearch.shield.transport.ssl;
import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.base.Charsets;
import com.google.common.io.Files;
import com.google.common.net.InetAddresses;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.os.OsUtils;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.http.HttpServerTransport;
import org.elasticsearch.shield.plugin.SecurityPlugin;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.test.junit.annotations.TestLogging;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.elasticsearch.shield.test.ShieldIntegrationTest;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import javax.net.ssl.*;
import java.io.File;
@ -33,15 +26,16 @@ import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Locale;
import static org.apache.lucene.util.LuceneTestCase.AwaitsFix;
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope;
import static org.hamcrest.Matchers.*;
/**
*
*/
@ElasticsearchIntegrationTest.ClusterScope(scope = ElasticsearchIntegrationTest.Scope.SUITE, numDataNodes = 1, transportClientRatio = 0.0, numClientNodes = 0)
@AwaitsFix(bugUrl = "https://github.com/elasticsearch/elasticsearch-shield/issues/36")
public class SslRequireAuthTests extends ElasticsearchIntegrationTest {
@ClusterScope(scope = Scope.SUITE, numDataNodes = 1, numClientNodes = 0)
public class SslRequireAuthTests extends ShieldIntegrationTest {
public static final HostnameVerifier HOSTNAME_VERIFIER = new HostnameVerifier() {
@Override
@ -50,54 +44,22 @@ public class SslRequireAuthTests extends ElasticsearchIntegrationTest {
}
};
@ClassRule
public static TemporaryFolder temporaryFolder = new TemporaryFolder();
private static File ipFilterFile;
@BeforeClass
public static void writeAllowAllIpFilterFile() throws Exception {
ipFilterFile = temporaryFolder.newFile();
Files.write("allow: all\n".getBytes(com.google.common.base.Charsets.UTF_8), ipFilterFile);
@Override
protected Settings nodeSettings(int nodeOrdinal) {
return settingsBuilder()
.put(super.nodeSettings(nodeOrdinal))
.put(getSSLSettingsForStore("certs/simple/testnode.jks", "testnode"))
.put("shield.transport.ssl.require.client.auth", true)
.put("shield.http.ssl.require.client.auth", true)
.build();
}
@Override
protected Settings nodeSettings(int nodeOrdinal) {
File testnodeStore;
try {
testnodeStore = new File(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks").toURI());
assertThat(testnodeStore.exists(), is(true));
} catch (Exception e) {
throw new RuntimeException(e);
}
ImmutableSettings.Builder builder = ImmutableSettings.settingsBuilder()
.put(super.nodeSettings(nodeOrdinal))
.put("discovery.zen.ping.multicast.ping.enabled", false)
// prevents exception until parsing has been fixed in PR
.put("shield.authz.file.roles", "not/existing")
// needed to ensure that netty transport is started
.put("node.mode", "network")
.put("shield.transport.ssl", true)
.put("shield.transport.ssl.require.client.auth", 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.require.client.auth", 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")
.put("plugin.types", SecurityPlugin.class.getName())
.put("shield.n2n.file", ipFilterFile.getPath());
if (OsUtils.MAC) {
builder.put("network.host", randomBoolean() ? "127.0.0.1" : "::1");
}
return builder.build();
protected Settings transportClientSettings() {
return ImmutableSettings.builder()
.put(super.transportClientSettings())
.put(getSSLSettingsForStore("certs/simple/testclient.jks", "testclient"))
.build();
}
@ -131,7 +93,6 @@ public class SslRequireAuthTests extends ElasticsearchIntegrationTest {
}
@Test
@TestLogging("_root:DEBUG")
public void testThatConnectionToHTTPWorks() throws Exception {
File store = new File(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks").toURI());