diff --git a/qa/pom.xml b/qa/pom.xml index 3444a8d3f7c..a2e3b1d0dbf 100644 --- a/qa/pom.xml +++ b/qa/pom.xml @@ -321,6 +321,7 @@ smoke-test-watcher-with-shield shield-example-realm shield-tribe-node-tests + shield-client-tests diff --git a/qa/shield-client-tests/integration-tests.xml b/qa/shield-client-tests/integration-tests.xml new file mode 100644 index 00000000000..56f8290b01c --- /dev/null +++ b/qa/shield-client-tests/integration-tests.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + Waiting for elasticsearch to become available on port @{port}... + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Adding shield users... + + + + + + + + + + + + + + + + + + + + + + + + Checking we can connect with basic auth on port ${integ.http.port}... + + + + + diff --git a/qa/shield-client-tests/pom.xml b/qa/shield-client-tests/pom.xml new file mode 100644 index 00000000000..a8a6240010d --- /dev/null +++ b/qa/shield-client-tests/pom.xml @@ -0,0 +1,144 @@ + + + + x-plugins-qa + org.elasticsearch.qa + 3.0.0-SNAPSHOT + + 4.0.0 + + shield-client-tests + QA: Shield transport client tests + Run tests with a Transport Client for communication with a Shield enabled cluster + + + true + false + ${project.basedir}/integration-tests.xml + license,shield + + + + + org.elasticsearch.plugin + shield + ${elasticsearch.version} + test + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + org.apache.maven.plugins + maven-dependency-plugin + + + integ-setup-dependencies + pre-integration-test + + copy + + + ${skip.integ.tests} + true + ${integ.deps}/plugins + + + + + org.elasticsearch.distribution.zip + elasticsearch + ${elasticsearch.version} + zip + true + ${integ.deps} + + + + + org.elasticsearch.plugin + license + ${elasticsearch.version} + zip + true + + + + org.elasticsearch.plugin + shield + ${elasticsearch.version} + zip + true + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + + integ-setup + pre-integration-test + + run + + + + + + + + + + ${skip.integ.tests} + + + + + integ-teardown + post-integration-test + + run + + + + + + ${skip.integ.tests} + + + + + + ant-contrib + ant-contrib + 1.0b3 + + + ant + ant + + + + + org.apache.ant + ant-nodeps + 1.8.1 + + + + + + diff --git a/qa/shield-client-tests/src/test/java/org/elasticsearch/shield/qa/ShieldTransportClientIT.java b/qa/shield-client-tests/src/test/java/org/elasticsearch/shield/qa/ShieldTransportClientIT.java new file mode 100644 index 00000000000..e2c6ce6adc9 --- /dev/null +++ b/qa/shield-client-tests/src/test/java/org/elasticsearch/shield/qa/ShieldTransportClientIT.java @@ -0,0 +1,120 @@ +/* + * 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.qa; + +import org.elasticsearch.ElasticsearchSecurityException; +import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; +import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; +import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; +import org.elasticsearch.client.transport.TransportClient; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.transport.TransportAddress; +import org.elasticsearch.plugins.Plugin; +import org.elasticsearch.shield.ShieldPlugin; +import org.elasticsearch.shield.authc.support.SecuredString; +import org.elasticsearch.test.ESIntegTestCase; +import org.junit.Test; + +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import static org.elasticsearch.shield.authc.support.UsernamePasswordToken.basicAuthHeaderValue; +import static org.hamcrest.Matchers.*; + +/** + * Integration tests that test a transport client with Shield being loaded that connect to an external cluster + */ +public class ShieldTransportClientIT extends ESIntegTestCase { + + static final String ADMIN_USER_PW = "test_user:changeme"; + static final String TRANSPORT_USER_PW = "transport:changeme"; + + @Override + protected Settings externalClusterClientSettings() { + return Settings.builder() + .put("shield.user", ADMIN_USER_PW) + .build(); + } + + @Override + protected Collection> transportClientPlugins() { + return Collections.singletonList(ShieldPlugin.class); + } + + @Test + public void testThatTransportClientWithoutAuthenticationDoesNotWork() throws Exception { + try (TransportClient client = transportClient(Settings.EMPTY)) { + boolean connected = awaitBusy(() -> { + return client.connectedNodes().size() > 0; + }, 5L, TimeUnit.SECONDS); + + assertThat(connected, is(false)); + } + } + + @Test + public void testThatTransportClientAuthenticationWithTransportClientRole() throws Exception { + Settings settings = Settings.builder() + .put("shield.user", TRANSPORT_USER_PW) + .build(); + try (TransportClient client = transportClient(settings)) { + boolean connected = awaitBusy(() -> { + return client.connectedNodes().size() > 0; + }, 5L, TimeUnit.SECONDS); + + assertThat(connected, is(true)); + + // this checks that the transport client is really running in a limited state + try { + client.admin().cluster().prepareHealth().get(); + fail("the transport user should not be be able to get health!"); + } catch (ElasticsearchSecurityException e) { + assertThat(e.toString(), containsString("unauthorized")); + } + } + } + + @Test + public void testTransportClientWithAdminUser() throws Exception { + final boolean useTransportUser = randomBoolean(); + Settings settings = Settings.builder() + .put("shield.user", useTransportUser ? TRANSPORT_USER_PW : ADMIN_USER_PW) + .build(); + try (TransportClient client = transportClient(settings)) { + boolean connected = awaitBusy(() -> { + return client.connectedNodes().size() > 0; + }, 5L, TimeUnit.SECONDS); + + assertThat(connected, is(true)); + + // this checks that the transport client is really running in a limited state + ClusterHealthResponse response; + if (useTransportUser) { + response = client.admin().cluster().prepareHealth().putHeader("Authorization", basicAuthHeaderValue("test_user", new SecuredString("changeme".toCharArray()))).get(); + } else { + response = client.admin().cluster().prepareHealth().get(); + } + + assertThat(response.isTimedOut(), is(false)); + } + } + + TransportClient transportClient(Settings extraSettings) { + NodesInfoResponse nodeInfos = client().admin().cluster().prepareNodesInfo().get(); + NodeInfo[] nodes = nodeInfos.getNodes(); + assertTrue(nodes.length > 0); + TransportAddress publishAddress = randomFrom(nodes).getTransport().address().publishAddress(); + String clusterName = nodeInfos.getClusterNameAsString(); + + Settings settings = Settings.builder() + .put(extraSettings) + .put("cluster.name", clusterName) + .build(); + + return TransportClient.builder().settings(settings).addPlugin(ShieldPlugin.class).build().addTransportAddress(publishAddress); + } +} diff --git a/shield/config/shield/roles.yml b/shield/config/shield/roles.yml index 406931d4ea4..d35711cf9bf 100644 --- a/shield/config/shield/roles.yml +++ b/shield/config/shield/roles.yml @@ -21,18 +21,9 @@ user: # Defines the required permissions for transport clients transport_client: cluster: - - cluster:monitor/nodes/info - #uncomment the following for sniffing - #- cluster:monitor/state - -# The required role for kibana 3 users -kibana3: - cluster: cluster:monitor/nodes/info - indices: - '*': - privileges: indices:data/read/search, indices:data/read/get, indices:admin/get - 'kibana-int': - privileges: indices:data/read/search, indices:data/read/get, indices:data/write/delete, indices:data/write/index, create_index + - cluster:monitor/nodes/liveness + #uncomment the following for sniffing + #- cluster:monitor/state # The required permissions for kibana 4 users. kibana4: @@ -60,18 +51,3 @@ logstash: indices: 'logstash-*': privileges: indices:data/write/bulk, indices:data/write/delete, indices:data/write/update, indices:data/read/search, indices:data/read/scroll, create_index - -# Marvel role, allowing all operations -# on the marvel indices -marvel_user: - cluster: cluster:monitor/nodes/info, cluster:admin/plugin/license/get - indices: - '.marvel-*': - privileges: all - -# Marvel Agent users -marvel_agent: - cluster: indices:admin/template/get, indices:admin/template/put - indices: - '.marvel-*': - privileges: indices:data/write/bulk, create_index diff --git a/shield/docs/public/release-notes.asciidoc b/shield/docs/public/release-notes.asciidoc index 652e4bfcb1f..f633edcb977 100644 --- a/shield/docs/public/release-notes.asciidoc +++ b/shield/docs/public/release-notes.asciidoc @@ -34,7 +34,7 @@ The default role definitions in the `roles.yml` file may need to be changed to e applications such as Marvel and Kibana. Any role changes will be found in `roles.yml.new` after upgrading to the new version of Shield. We recommend copying the changes listed below to your `roles.yml` file. -* added[2.0.0-beta2] The permission on all the roles are updated to the verbose format to make it easer to enable field level and document level security. +* added[2.0.0-beta2] The permission on all the roles are updated to the verbose format to make it easier to enable field level and document level security. `transport_client` role updated to work with Elasticsearch 2.0.0-beta2. `kibana3`, `marvel_user`, and `marvel_agent` roles removed. * added[1.1.0] `kibana4_server` role added that defines the minimum set of permissions necessary for the Kibana 4 server. * added[1.0.1] `kibana4` role updated to work with new features in Kibana 4 RC1