Updated licensing behaviour
- on license expiration, we only block cluster stats/health and indices stats. - depend on the latest snapshot of the licensing plugin that supports registrations of expiration callbacks - registering expiration callbacks to periodically log and warn about license expiration (pre and post expiration) Original commit: elastic/x-pack-elasticsearch@5aee30fac4
This commit is contained in:
parent
27fd142e0c
commit
ac6b82ef7c
2
pom.xml
2
pom.xml
|
@ -49,7 +49,7 @@
|
|||
<lucene.version>4.10.2</lucene.version>
|
||||
<lucene.maven.version>4.10.2</lucene.maven.version>
|
||||
<elasticsearch.version>1.4.2</elasticsearch.version>
|
||||
<license.plugin.version>1.0.0-beta2</license.plugin.version>
|
||||
<license.plugin.version>1.0.0-SNAPSHOT</license.plugin.version>
|
||||
|
||||
<tests.jvms>auto</tests.jvms>
|
||||
<tests.shuffle>true</tests.shuffle>
|
||||
|
|
|
@ -18,7 +18,6 @@ import org.elasticsearch.common.component.AbstractComponent;
|
|||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.core.LicenseExpiredException;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.shield.User;
|
||||
import org.elasticsearch.shield.audit.AuditTrail;
|
||||
import org.elasticsearch.shield.authc.AuthenticationService;
|
||||
|
@ -38,7 +37,7 @@ import java.util.List;
|
|||
*/
|
||||
public class ShieldActionFilter extends AbstractComponent implements ActionFilter {
|
||||
|
||||
private static final Predicate<String> READ_ACTION_MATCHER = Privilege.Index.READ.predicate();
|
||||
private static final Predicate<String> LICESE_EXPIRATION_ACTION_MATCHER = Privilege.HEALTH_AND_STATS.predicate();
|
||||
|
||||
private final AuthenticationService authcService;
|
||||
private final AuthorizationService authzService;
|
||||
|
@ -57,14 +56,14 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte
|
|||
this.signatureService = signatureService;
|
||||
this.auditTrail = auditTrail;
|
||||
this.actionMapper = actionMapper;
|
||||
licenseEventsNotifier.register(new LicensesClientService.Listener() {
|
||||
licenseEventsNotifier.register(new LicenseEventsNotifier.Listener() {
|
||||
@Override
|
||||
public void onEnabled() {
|
||||
public void enabled() {
|
||||
licenseEnabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled() {
|
||||
public void disabled() {
|
||||
licenseEnabled = false;
|
||||
}
|
||||
});
|
||||
|
@ -77,8 +76,10 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte
|
|||
A functional requirement - when the license of shield is disabled (invalid/expires), shield will continue
|
||||
to operate normally, except all read operations will be blocked.
|
||||
*/
|
||||
if (!licenseEnabled && READ_ACTION_MATCHER.apply(action)) {
|
||||
logger.error("blocking read operation [{}] due to disabled license", action);
|
||||
if (!licenseEnabled && LICESE_EXPIRATION_ACTION_MATCHER.apply(action)) {
|
||||
logger.error("blocking [{}] operation due to expired license. Cluster health, cluster stats and indices stats \n" +
|
||||
"operations are blocked on shield license expiration. All data operations (read and write) continue to work. \n" +
|
||||
"If you have a new license, please update it. Otherwise, please reach out to your support contact.", action);
|
||||
throw new LicenseExpiredException(LicenseService.FEATURE_NAME);
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ public abstract class Privilege<P extends Privilege<P>> {
|
|||
static final String SUB_ACTION_SUFFIX_PATTERN = "*";
|
||||
|
||||
public static final System SYSTEM = new System();
|
||||
public static final General HEALTH_AND_STATS = new General("health_and_stats", "cluster:monitor/health*", "cluster:monitor/stats*", "indices:monitor/stats*");
|
||||
|
||||
protected final Name name;
|
||||
|
||||
|
@ -106,6 +107,34 @@ public abstract class Privilege<P extends Privilege<P>> {
|
|||
}
|
||||
}
|
||||
|
||||
public static class General extends AutomatonPrivilege<General> {
|
||||
|
||||
private static final General NONE = new General(Name.NONE, Automata.makeEmpty());
|
||||
|
||||
public General(String name, String... patterns) {
|
||||
super(name, patterns);
|
||||
}
|
||||
|
||||
public General(Name name, String... patterns) {
|
||||
super(name, patterns);
|
||||
}
|
||||
|
||||
public General(Name name, Automaton automaton) {
|
||||
super(name, automaton);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected General create(Name name, Automaton automaton) {
|
||||
return new General(name, automaton);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected General none() {
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class Index extends AutomatonPrivilege<Index> {
|
||||
|
||||
public static final Index NONE = new Index(Name.NONE, Automata.makeEmpty());
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
*/
|
||||
package org.elasticsearch.shield.license;
|
||||
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -22,22 +20,28 @@ import java.util.Set;
|
|||
*/
|
||||
public class LicenseEventsNotifier {
|
||||
|
||||
private final Set<LicensesClientService.Listener> listeners = new HashSet<>();
|
||||
private final Set<Listener> listeners = new HashSet<>();
|
||||
|
||||
public void register(LicensesClientService.Listener listener) {
|
||||
public void register(Listener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
protected void notifyEnabled() {
|
||||
for (LicensesClientService.Listener listener : listeners) {
|
||||
listener.onEnabled();
|
||||
for (Listener listener : listeners) {
|
||||
listener.enabled();
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyDisabled() {
|
||||
for (LicensesClientService.Listener listener : listeners) {
|
||||
listener.onDisabled();
|
||||
for (Listener listener : listeners) {
|
||||
listener.disabled();
|
||||
}
|
||||
}
|
||||
|
||||
public static interface Listener {
|
||||
|
||||
void enabled();
|
||||
|
||||
void disabled();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,14 +6,21 @@
|
|||
package org.elasticsearch.shield.license;
|
||||
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.collect.ImmutableList;
|
||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
import org.elasticsearch.shield.ShieldPlugin;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -24,8 +31,11 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
|
|||
private static final LicensesService.TrialLicenseOptions TRIAL_LICENSE_OPTIONS =
|
||||
new LicensesService.TrialLicenseOptions(TimeValue.timeValueHours(30 * 24), 1000);
|
||||
|
||||
private static final FormatDateTimeFormatter DATE_FORMATTER = Joda.forPattern("EEEE, MMMMM dd, yyyy", Locale.ROOT);
|
||||
|
||||
private final LicensesClientService licensesClientService;
|
||||
private final LicenseEventsNotifier notifier;
|
||||
private final Collection<LicensesService.ExpirationCallback> expirationLoggers;
|
||||
|
||||
private boolean enabled = false;
|
||||
|
||||
|
@ -34,6 +44,41 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
|
|||
super(settings);
|
||||
this.licensesClientService = licensesClientService;
|
||||
this.notifier = notifier;
|
||||
this.expirationLoggers = ImmutableList.of(
|
||||
new LicensesService.ExpirationCallback.Pre(days(7), days(30), days(1)) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
logger.error("\n" +
|
||||
"#\n" +
|
||||
"# Shield license will expire on [{}]. Cluster health, cluster stats and indices stats operations are\n" +
|
||||
"# blocked on Shield license expiration. All data operations (read and write) continue to work. If you\n" +
|
||||
"# have a new license, please update it. Otherwise, please reach out to your support contact.\n" +
|
||||
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
|
||||
}
|
||||
},
|
||||
new LicensesService.ExpirationCallback.Pre(days(0), days(7), minutes(10)) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
logger.error("\n" +
|
||||
"#\n" +
|
||||
"# Shield license will expire on [{}]. Cluster health, cluster stats and indices stats operations are\n" +
|
||||
"# blocked on Shield license expiration. All data operations (read and write) continue to work. If you\n" +
|
||||
"# have a new license, please update it. Otherwise, please reach out to your support contact.\n" +
|
||||
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
|
||||
}
|
||||
},
|
||||
new LicensesService.ExpirationCallback.Post(days(0), null, minutes(10)) {
|
||||
@Override
|
||||
public void on(License license, LicensesService.ExpirationStatus status) {
|
||||
logger.error("\n" +
|
||||
"#\n" +
|
||||
"# SHIELD LICENSE EXPIRED ON [{}]! CLUSTER HEALTH, CLUSTER STATS AND INDICES STATS OPERATIONS ARE\n" +
|
||||
"# NOW BLOCKED. ALL DATA OPERATIONS (READ AND WRITE) CONTINUE TO WORK. IF YOU HAVE A NEW LICENSE, PLEASE\n" +
|
||||
"# UPDATE IT. OTHERWISE, PLEASE REACH OUT TO YOUR SUPPORT CONTACT.\n" +
|
||||
"#", DATE_FORMATTER.printer().print(license.expiryDate()));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public synchronized boolean enabled() {
|
||||
|
@ -43,10 +88,10 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
|
|||
@Override
|
||||
protected void doStart() throws ElasticsearchException {
|
||||
if (settings.getGroups("tribe", true).isEmpty()) {
|
||||
licensesClientService.register(FEATURE_NAME, TRIAL_LICENSE_OPTIONS, new InternalListener());
|
||||
licensesClientService.register(FEATURE_NAME, TRIAL_LICENSE_OPTIONS, expirationLoggers, new InternalListener());
|
||||
} else {
|
||||
//TODO currently we disable licensing on tribe node. remove this once es core supports merging cluster
|
||||
new InternalListener().onEnabled();
|
||||
new InternalListener().onEnabled(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,22 +103,33 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> {
|
|||
protected void doClose() throws ElasticsearchException {
|
||||
}
|
||||
|
||||
static TimeValue days(int days) {
|
||||
return TimeValue.timeValueHours(days * 24);
|
||||
}
|
||||
|
||||
static TimeValue minutes(int minutes) {
|
||||
return TimeValue.timeValueMinutes(minutes);
|
||||
}
|
||||
|
||||
class InternalListener implements LicensesClientService.Listener {
|
||||
|
||||
@Override
|
||||
public void onEnabled() {
|
||||
public void onEnabled(License license) {
|
||||
synchronized (LicenseService.this) {
|
||||
logger.info("enabling license for [{}]", FEATURE_NAME);
|
||||
enabled = true;
|
||||
notifier.notifyEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled() {
|
||||
public void onDisabled(License license) {
|
||||
synchronized (LicenseService.this) {
|
||||
logger.info("DISABLING LICENSE FOR [{}]", FEATURE_NAME);
|
||||
enabled = false;
|
||||
notifier.notifyDisabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
*/
|
||||
package org.elasticsearch.integration;
|
||||
|
||||
import org.elasticsearch.action.get.GetResponse;
|
||||
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
|
||||
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsIndices;
|
||||
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsResponse;
|
||||
import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
|
||||
import org.elasticsearch.action.index.IndexResponse;
|
||||
import org.elasticsearch.action.search.SearchResponse;
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.common.collect.ImmutableSet;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
|
@ -15,6 +17,7 @@ import org.elasticsearch.common.inject.AbstractModule;
|
|||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.inject.Module;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.LicenseExpiredException;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.license.plugin.core.LicensesService;
|
||||
|
@ -31,12 +34,9 @@ import java.util.Collection;
|
|||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.SUITE;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -44,6 +44,19 @@ import static org.hamcrest.Matchers.is;
|
|||
@ClusterScope(scope = SUITE)
|
||||
public class LicensingTests extends ShieldIntegrationTest {
|
||||
|
||||
static final License DUMMY_LICENSE = License.builder()
|
||||
.feature(LicenseService.FEATURE_NAME)
|
||||
.expiryDate(System.currentTimeMillis())
|
||||
.issueDate(System.currentTimeMillis())
|
||||
.issuedTo("LicensingTests")
|
||||
.issuer("test")
|
||||
.maxNodes(Integer.MAX_VALUE)
|
||||
.signature("_signature")
|
||||
.type("test_license_for_shield")
|
||||
.subscriptionType("all_is_good")
|
||||
.uid(String.valueOf(CHILD_JVM_ID) + System.identityHashCode(LicensingTests.class))
|
||||
.build();
|
||||
|
||||
public static final String ROLES =
|
||||
ShieldSettingsSource.DEFAULT_ROLE + ":\n" +
|
||||
" cluster: all\n" +
|
||||
|
@ -116,17 +129,26 @@ public class LicensingTests extends ShieldIntegrationTest {
|
|||
Client client = internalCluster().transportClient();
|
||||
|
||||
disableLicensing();
|
||||
|
||||
try {
|
||||
client.prepareSearch().setQuery(matchAllQuery()).get();
|
||||
fail("expected an license expired exception when running a search with disabled license");
|
||||
client.admin().indices().prepareStats().get();
|
||||
fail("expected an license expired exception when executing an index stats action");
|
||||
} catch (LicenseExpiredException lee) {
|
||||
// expected
|
||||
assertThat(lee.feature(), equalTo(LicenseService.FEATURE_NAME));
|
||||
}
|
||||
|
||||
try {
|
||||
client.prepareGet("test1", "type", indexResponse.getId()).get();
|
||||
fail("expected an license expired exception when running a get with disabled license");
|
||||
client.admin().cluster().prepareClusterStats().get();
|
||||
fail("expected an license expired exception when executing cluster stats action");
|
||||
} catch (LicenseExpiredException lee) {
|
||||
// expected
|
||||
assertThat(lee.feature(), equalTo(LicenseService.FEATURE_NAME));
|
||||
}
|
||||
|
||||
try {
|
||||
client.admin().cluster().prepareHealth().get();
|
||||
fail("expected an license expired exception when executing cluster health action");
|
||||
} catch (LicenseExpiredException lee) {
|
||||
// expected
|
||||
assertThat(lee.feature(), equalTo(LicenseService.FEATURE_NAME));
|
||||
|
@ -134,27 +156,17 @@ public class LicensingTests extends ShieldIntegrationTest {
|
|||
|
||||
enableLicensing();
|
||||
|
||||
SearchResponse searchResponse = client.prepareSearch().setQuery(matchAllQuery()).get();
|
||||
assertNoFailures(searchResponse);
|
||||
assertHitCount(searchResponse, 2);
|
||||
IndicesStatsResponse indicesStatsResponse = client.admin().indices().prepareStats().get();
|
||||
assertNoFailures(indicesStatsResponse);
|
||||
|
||||
GetResponse getResponse = client.prepareGet("test1", "type", indexResponse.getId()).get();
|
||||
assertThat(getResponse.getId(), equalTo(indexResponse.getId()));
|
||||
ClusterStatsResponse clusterStatsNodeResponse = client.admin().cluster().prepareClusterStats().get();
|
||||
assertThat(clusterStatsNodeResponse, notNullValue());
|
||||
ClusterStatsIndices indices = clusterStatsNodeResponse.getIndicesStats();
|
||||
assertThat(indices, notNullValue());
|
||||
assertThat(indices.getIndexCount(), is(2));
|
||||
|
||||
enableLicensing();
|
||||
indexResponse = index("test", "type", jsonBuilder()
|
||||
.startObject()
|
||||
.field("name", "value2")
|
||||
.endObject());
|
||||
assertThat(indexResponse.isCreated(), is(true));
|
||||
|
||||
disableLicensing();
|
||||
|
||||
indexResponse = index("test", "type", jsonBuilder()
|
||||
.startObject()
|
||||
.field("name", "value3")
|
||||
.endObject());
|
||||
assertThat(indexResponse.isCreated(), is(true));
|
||||
ClusterHealthResponse clusterIndexHealth = client.admin().cluster().prepareHealth().get();
|
||||
assertThat(clusterIndexHealth, notNullValue());
|
||||
}
|
||||
|
||||
public static void disableLicensing() {
|
||||
|
@ -208,20 +220,20 @@ public class LicensingTests extends ShieldIntegrationTest {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void register(String feature, LicensesService.TrialLicenseOptions trialLicenseOptions, Listener listener) {
|
||||
public void register(String s, LicensesService.TrialLicenseOptions trialLicenseOptions, Collection<LicensesService.ExpirationCallback> collection, Listener listener) {
|
||||
listeners.add(listener);
|
||||
enable();
|
||||
}
|
||||
|
||||
void enable() {
|
||||
for (Listener listener : listeners) {
|
||||
listener.onEnabled();
|
||||
listener.onEnabled(DUMMY_LICENSE);
|
||||
}
|
||||
}
|
||||
|
||||
void disable() {
|
||||
for (Listener listener : listeners) {
|
||||
listener.onDisabled();
|
||||
listener.onDisabled(DUMMY_LICENSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.elasticsearch.test.rest.json.JsonPath;
|
|||
import org.elasticsearch.transport.Transport;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.After;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -49,6 +50,15 @@ public class ShieldPluginEnabledDisabledTests extends ShieldIntegrationTest {
|
|||
enabled = randomBoolean();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() throws Exception {
|
||||
// now that on a disabled license we block cluster health/stats and indices stats, we need
|
||||
// to make sure that after the tests (which disable the license for testing purposes) we
|
||||
// reenabled the license, so the internal cluster will be cleaned appropriately.
|
||||
logger.info("cleanup: enabling licensing...");
|
||||
LicensingTests.enableLicensing();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Settings nodeSettings(int nodeOrdinal) {
|
||||
logger.info("******* shield is " + (enabled ? "enabled" : "disabled"));
|
||||
|
|
|
@ -10,15 +10,14 @@ import org.elasticsearch.action.ActionRequest;
|
|||
import org.elasticsearch.action.search.SearchScrollRequest;
|
||||
import org.elasticsearch.action.support.ActionFilterChain;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.license.plugin.core.LicensesClientService;
|
||||
import org.elasticsearch.shield.User;
|
||||
import org.elasticsearch.shield.audit.AuditTrail;
|
||||
import org.elasticsearch.shield.authc.AuthenticationService;
|
||||
import org.elasticsearch.shield.authz.AuthorizationException;
|
||||
import org.elasticsearch.shield.authz.AuthorizationService;
|
||||
import org.elasticsearch.shield.license.LicenseEventsNotifier;
|
||||
import org.elasticsearch.shield.signature.SignatureService;
|
||||
import org.elasticsearch.shield.signature.SignatureException;
|
||||
import org.elasticsearch.shield.signature.SignatureService;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -110,8 +109,8 @@ public class ShieldActionFilterTests extends ElasticsearchTestCase {
|
|||
|
||||
private class MockLicenseEventsNotifier extends LicenseEventsNotifier {
|
||||
@Override
|
||||
public void register(LicensesClientService.Listener listener) {
|
||||
listener.onEnabled();
|
||||
public void register(MockLicenseEventsNotifier.Listener listener) {
|
||||
listener.enabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ package org.elasticsearch.shield.test;
|
|||
|
||||
import com.google.common.base.Charsets;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.io.FileSystemUtils;
|
||||
import org.elasticsearch.common.io.Streams;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -15,6 +16,20 @@ import java.nio.file.Path;
|
|||
|
||||
public class ShieldTestUtils {
|
||||
|
||||
public static File createFolder(File parent, String name) {
|
||||
File createdFolder = new File(parent, name);
|
||||
//the directory might exist e.g. if the global cluster gets restarted, then we recreate the directory as well
|
||||
if (createdFolder.exists()) {
|
||||
if (!FileSystemUtils.deleteRecursively(createdFolder)) {
|
||||
throw new RuntimeException("could not delete existing temporary folder: " + createdFolder.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
if (!createdFolder.mkdir()) {
|
||||
throw new RuntimeException("could not create temporary folder: " + createdFolder.getAbsolutePath());
|
||||
}
|
||||
return createdFolder;
|
||||
}
|
||||
|
||||
public static String writeFile(File folder, String name, byte[] content) {
|
||||
Path file = folder.toPath().resolve(name);
|
||||
try {
|
||||
|
|
|
@ -5,13 +5,16 @@
|
|||
*/
|
||||
package org.elasticsearch.shield.transport;
|
||||
|
||||
import org.elasticsearch.ElasticsearchTimeoutException;
|
||||
import com.carrotsearch.ant.tasks.junit4.dependencies.com.google.common.collect.ImmutableMap;
|
||||
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.common.unit.TimeValue;
|
||||
import org.elasticsearch.discovery.MasterNotDiscoveredException;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.elasticsearch.node.internal.InternalNode;
|
||||
import org.elasticsearch.shield.authc.esusers.ESUsersRealm;
|
||||
import org.elasticsearch.shield.signature.InternalSignatureService;
|
||||
import org.elasticsearch.test.ShieldIntegrationTest;
|
||||
import org.elasticsearch.test.ShieldSettingsSource;
|
||||
|
@ -24,6 +27,8 @@ import java.net.InetSocketAddress;
|
|||
|
||||
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
|
||||
import static org.elasticsearch.node.NodeBuilder.nodeBuilder;
|
||||
import static org.elasticsearch.shield.test.ShieldTestUtils.createFolder;
|
||||
import static org.elasticsearch.shield.test.ShieldTestUtils.writeFile;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
|
@ -108,11 +113,19 @@ public class ServerTransportFilterIntegrationTest extends ShieldIntegrationTest
|
|||
Settings dataNodeSettings = internalCluster().getDataNodeInstance(Settings.class);
|
||||
String systemKeyFile = dataNodeSettings.get(InternalSignatureService.FILE_SETTING);
|
||||
|
||||
File folder = createFolder(globalTempDir(), getClass().getSimpleName() + "-" + randomAsciiOfLength(10));
|
||||
|
||||
// test that starting up a node works
|
||||
Settings nodeSettings = settingsBuilder()
|
||||
.put("shield.authc.realms.esusers.type", ESUsersRealm.TYPE)
|
||||
.put("shield.authc.realms.esusers.order", 0)
|
||||
.put("shield.authc.realms.esusers.files.users", writeFile(folder, "users", configUsers()))
|
||||
.put("shield.authc.realms.esusers.files.users_roles", writeFile(folder, "users_roles", configUsersRoles()))
|
||||
.put("shield.authz.store.files.roles", writeFile(folder, "roles.yml", configRoles()))
|
||||
.put(ShieldSettingsSource.getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks", "testnode"))
|
||||
.put("node.mode", "network")
|
||||
.put("node.name", "my-test-node")
|
||||
.put("shield.user", "test_user:changeme")
|
||||
.put("cluster.name", internalCluster().getClusterName())
|
||||
.put("discovery.zen.ping.multicast.enabled", false)
|
||||
.put("discovery.zen.ping.unicast.hosts", "localhost:" + randomClientPort)
|
||||
|
@ -127,9 +140,21 @@ public class ServerTransportFilterIntegrationTest extends ShieldIntegrationTest
|
|||
|
||||
// assert that node is not connected by waiting for the timeout
|
||||
try {
|
||||
node.client().admin().cluster().prepareHealth().get("1s");
|
||||
fail("Expected timeout exception due to node unable to connect");
|
||||
} catch (ElasticsearchTimeoutException e) {}
|
||||
// updating cluster settings requires a master. since the node should not be able to
|
||||
// connect to the cluster, there should be no master, and therefore this
|
||||
// operation should fail. we can't use cluster health/stats here to and
|
||||
// wait for a timeout, because as long as the node is not connected to the cluster
|
||||
// the license is disabled and therefore blocking health & stats calls.
|
||||
node.client().admin().cluster().prepareUpdateSettings()
|
||||
.setTransientSettings(ImmutableMap.of("key", "value"))
|
||||
.setMasterNodeTimeout(TimeValue.timeValueSeconds(2))
|
||||
.get();
|
||||
fail("Expected to fail update settings as the node should not be able to connect to the cluster, and therefore there should be no master");
|
||||
} catch (MasterNotDiscoveredException e) {
|
||||
// expected
|
||||
logger.error("expected: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ import static org.hamcrest.Matchers.instanceOf;
|
|||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
// no client nodes, no transport clients, as they all get rejected on network connections
|
||||
@ClusterScope(scope = Scope.SUITE, numDataNodes = 1, numClientNodes = 0, transportClientRatio = 0.0)
|
||||
@ClusterScope(scope = Scope.SUITE, numDataNodes = 0, numClientNodes = 0, transportClientRatio = 0.0)
|
||||
public class IpFilteringIntegrationTests extends ShieldIntegrationTest {
|
||||
|
||||
private static int randomClientPort;
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.elasticsearch.shield.authc.esusers.ESUsersRealm;
|
|||
import org.elasticsearch.shield.authc.support.SecuredString;
|
||||
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
|
||||
import org.elasticsearch.shield.signature.InternalSignatureService;
|
||||
import org.elasticsearch.shield.test.ShieldTestUtils;
|
||||
import org.elasticsearch.shield.transport.netty.NettySecuredTransport;
|
||||
import org.elasticsearch.test.discovery.ClusterDiscoveryConfiguration;
|
||||
|
||||
|
@ -107,7 +108,7 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
|
|||
|
||||
@Override
|
||||
public Settings node(int nodeOrdinal) {
|
||||
File folder = createFolder(parentFolder, subfolderPrefix + "-" + nodeOrdinal);
|
||||
File folder = ShieldTestUtils.createFolder(parentFolder, subfolderPrefix + "-" + nodeOrdinal);
|
||||
ImmutableSettings.Builder builder = ImmutableSettings.builder().put(super.node(nodeOrdinal))
|
||||
.put("plugin.types", ShieldPlugin.class.getName() + "," + licensePluginClass().getName())
|
||||
.put("shield.audit.enabled", randomBoolean())
|
||||
|
@ -184,20 +185,6 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
|
|||
}
|
||||
}
|
||||
|
||||
private static File createFolder(File parent, String name) {
|
||||
File createdFolder = new File(parent, name);
|
||||
//the directory might exist e.g. if the global cluster gets restarted, then we recreate the directory as well
|
||||
if (createdFolder.exists()) {
|
||||
if (!FileSystemUtils.deleteRecursively(createdFolder)) {
|
||||
throw new RuntimeException("could not delete existing temporary folder: " + createdFolder.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
if (!createdFolder.mkdir()) {
|
||||
throw new RuntimeException("could not create temporary folder: " + createdFolder.getAbsolutePath());
|
||||
}
|
||||
return createdFolder;
|
||||
}
|
||||
|
||||
private static byte[] generateKey() {
|
||||
try {
|
||||
return InternalSignatureService.generateKey();
|
||||
|
|
Loading…
Reference in New Issue