shield: enable and disable features based on license type
Shield now supports the ability to disable or enable individual features based on the type of license that is currently installed. The change replaces the LicenseService in shield with a ShieldLicensee that is notified on changes to the license. The ShieldLicensee then updates a ShieldLicenseState object, which contains the logic and methods to check for features being enabled or disabled. The ShieldLicenseState object is used by consumers to check the status of a feature. The decoupling of the feature enablement from the ShieldLicensee class was done to work around circular dependency issues. Closes elastic/elasticsearch#689 Original commit: elastic/x-pack-elasticsearch@442514496d
This commit is contained in:
parent
704f9b12bc
commit
28948f8930
|
@ -52,6 +52,9 @@ public class MarvelLicensee extends AbstractLicenseeComponent<MarvelLicensee> im
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean collectionEnabled() {
|
public boolean collectionEnabled() {
|
||||||
|
// when checking multiple parts of the status, we should get a local reference to the status object since it is
|
||||||
|
// volatile and can change between check statements...
|
||||||
|
Status status = this.status;
|
||||||
return status.getMode() != License.OperationMode.NONE &&
|
return status.getMode() != License.OperationMode.NONE &&
|
||||||
status.getLicenseState() != LicenseState.DISABLED;
|
status.getLicenseState() != LicenseState.DISABLED;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ package org.elasticsearch.shield;
|
||||||
|
|
||||||
import org.elasticsearch.common.inject.util.Providers;
|
import org.elasticsearch.common.inject.util.Providers;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.shield.license.LicenseService;
|
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||||
import org.elasticsearch.shield.support.AbstractShieldModule;
|
import org.elasticsearch.shield.support.AbstractShieldModule;
|
||||||
|
|
||||||
public class ShieldDisabledModule extends AbstractShieldModule {
|
public class ShieldDisabledModule extends AbstractShieldModule {
|
||||||
|
@ -21,7 +21,7 @@ public class ShieldDisabledModule extends AbstractShieldModule {
|
||||||
assert !shieldEnabled : "shield disabled module should only get loaded with shield disabled";
|
assert !shieldEnabled : "shield disabled module should only get loaded with shield disabled";
|
||||||
if (!clientMode) {
|
if (!clientMode) {
|
||||||
// required by the shield info rest action (when shield is disabled)
|
// required by the shield info rest action (when shield is disabled)
|
||||||
bind(LicenseService.class).toProvider(Providers.<LicenseService>of(null));
|
bind(ShieldLicenseState.class).toProvider(Providers.<ShieldLicenseState>of(null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,12 +32,11 @@ import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
|
||||||
import org.elasticsearch.shield.authz.AuthorizationModule;
|
import org.elasticsearch.shield.authz.AuthorizationModule;
|
||||||
import org.elasticsearch.index.SearcherWrapperInstaller;
|
import org.elasticsearch.index.SearcherWrapperInstaller;
|
||||||
import org.elasticsearch.shield.authz.accesscontrol.OptOutQueryCache;
|
import org.elasticsearch.shield.authz.accesscontrol.OptOutQueryCache;
|
||||||
import org.elasticsearch.shield.authz.accesscontrol.ShieldIndexSearcherWrapper;
|
|
||||||
import org.elasticsearch.shield.authz.store.FileRolesStore;
|
import org.elasticsearch.shield.authz.store.FileRolesStore;
|
||||||
import org.elasticsearch.shield.crypto.CryptoModule;
|
import org.elasticsearch.shield.crypto.CryptoModule;
|
||||||
import org.elasticsearch.shield.crypto.InternalCryptoService;
|
import org.elasticsearch.shield.crypto.InternalCryptoService;
|
||||||
import org.elasticsearch.shield.license.LicenseModule;
|
import org.elasticsearch.shield.license.LicenseModule;
|
||||||
import org.elasticsearch.shield.license.LicenseService;
|
import org.elasticsearch.shield.license.ShieldLicensee;
|
||||||
import org.elasticsearch.shield.rest.ShieldRestModule;
|
import org.elasticsearch.shield.rest.ShieldRestModule;
|
||||||
import org.elasticsearch.shield.rest.action.RestShieldInfoAction;
|
import org.elasticsearch.shield.rest.action.RestShieldInfoAction;
|
||||||
import org.elasticsearch.shield.rest.action.authc.cache.RestClearRealmCacheAction;
|
import org.elasticsearch.shield.rest.action.authc.cache.RestClearRealmCacheAction;
|
||||||
|
@ -125,7 +124,7 @@ public class ShieldPlugin extends Plugin {
|
||||||
@Override
|
@Override
|
||||||
public Collection<Class<? extends LifecycleComponent>> nodeServices() {
|
public Collection<Class<? extends LifecycleComponent>> nodeServices() {
|
||||||
if (enabled && clientMode == false) {
|
if (enabled && clientMode == false) {
|
||||||
return Arrays.<Class<? extends LifecycleComponent>>asList(LicenseService.class, InternalCryptoService.class, FileRolesStore.class, Realms.class, IPFilter.class);
|
return Arrays.<Class<? extends LifecycleComponent>>asList(ShieldLicensee.class, InternalCryptoService.class, FileRolesStore.class, Realms.class, IPFilter.class);
|
||||||
}
|
}
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,8 @@ import org.elasticsearch.action.support.ActionFilterChain;
|
||||||
import org.elasticsearch.common.component.AbstractComponent;
|
import org.elasticsearch.common.component.AbstractComponent;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
|
||||||
import org.elasticsearch.license.plugin.core.LicenseUtils;
|
import org.elasticsearch.license.plugin.core.LicenseUtils;
|
||||||
|
import org.elasticsearch.shield.ShieldPlugin;
|
||||||
import org.elasticsearch.shield.User;
|
import org.elasticsearch.shield.User;
|
||||||
import org.elasticsearch.shield.action.interceptor.RequestInterceptor;
|
import org.elasticsearch.shield.action.interceptor.RequestInterceptor;
|
||||||
import org.elasticsearch.shield.audit.AuditTrail;
|
import org.elasticsearch.shield.audit.AuditTrail;
|
||||||
|
@ -25,8 +25,7 @@ import org.elasticsearch.shield.authc.AuthenticationService;
|
||||||
import org.elasticsearch.shield.authz.AuthorizationService;
|
import org.elasticsearch.shield.authz.AuthorizationService;
|
||||||
import org.elasticsearch.shield.authz.Privilege;
|
import org.elasticsearch.shield.authz.Privilege;
|
||||||
import org.elasticsearch.shield.crypto.CryptoService;
|
import org.elasticsearch.shield.crypto.CryptoService;
|
||||||
import org.elasticsearch.shield.license.LicenseEventsNotifier;
|
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||||
import org.elasticsearch.shield.license.LicenseService;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -49,24 +48,18 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte
|
||||||
private final AuditTrail auditTrail;
|
private final AuditTrail auditTrail;
|
||||||
private final ShieldActionMapper actionMapper;
|
private final ShieldActionMapper actionMapper;
|
||||||
private final Set<RequestInterceptor> requestInterceptors;
|
private final Set<RequestInterceptor> requestInterceptors;
|
||||||
|
private final ShieldLicenseState licenseState;
|
||||||
private volatile boolean licenseEnabled = true;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ShieldActionFilter(Settings settings, AuthenticationService authcService, AuthorizationService authzService, CryptoService cryptoService,
|
public ShieldActionFilter(Settings settings, AuthenticationService authcService, AuthorizationService authzService, CryptoService cryptoService,
|
||||||
AuditTrail auditTrail, LicenseEventsNotifier licenseEventsNotifier, ShieldActionMapper actionMapper, Set<RequestInterceptor> requestInterceptors) {
|
AuditTrail auditTrail, ShieldLicenseState licenseState, ShieldActionMapper actionMapper, Set<RequestInterceptor> requestInterceptors) {
|
||||||
super(settings);
|
super(settings);
|
||||||
this.authcService = authcService;
|
this.authcService = authcService;
|
||||||
this.authzService = authzService;
|
this.authzService = authzService;
|
||||||
this.cryptoService = cryptoService;
|
this.cryptoService = cryptoService;
|
||||||
this.auditTrail = auditTrail;
|
this.auditTrail = auditTrail;
|
||||||
this.actionMapper = actionMapper;
|
this.actionMapper = actionMapper;
|
||||||
licenseEventsNotifier.register(new LicenseEventsNotifier.Listener() {
|
this.licenseState = licenseState;
|
||||||
@Override
|
|
||||||
public void notify(LicenseState state) {
|
|
||||||
licenseEnabled = state != LicenseState.DISABLED;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.requestInterceptors = requestInterceptors;
|
this.requestInterceptors = requestInterceptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,14 +70,15 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte
|
||||||
A functional requirement - when the license of shield is disabled (invalid/expires), shield will continue
|
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.
|
to operate normally, except all read operations will be blocked.
|
||||||
*/
|
*/
|
||||||
if (!licenseEnabled && LICENSE_EXPIRATION_ACTION_MATCHER.test(action)) {
|
if (!licenseState.statsAndHealthEnabled() && LICENSE_EXPIRATION_ACTION_MATCHER.test(action)) {
|
||||||
logger.error("blocking [{}] operation due to expired license. Cluster health, cluster stats and indices stats \n" +
|
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" +
|
"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);
|
"If you have a new license, please update it. Otherwise, please reach out to your support contact.", action);
|
||||||
throw LicenseUtils.newExpirationException(LicenseService.FEATURE_NAME);
|
throw LicenseUtils.newExpirationException(ShieldPlugin.NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (licenseState.securityEnabled()) {
|
||||||
/**
|
/**
|
||||||
here we fallback on the system user. Internal system requests are requests that are triggered by
|
here we fallback on the system user. Internal system requests are requests that are triggered by
|
||||||
the system itself (e.g. pings, update mappings, share relocation, etc...) and were not originated
|
the system itself (e.g. pings, update mappings, share relocation, etc...) and were not originated
|
||||||
|
@ -107,6 +101,9 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chain.proceed(action, request, new SigningListener(this, listener));
|
chain.proceed(action, request, new SigningListener(this, listener));
|
||||||
|
} else {
|
||||||
|
chain.proceed(action, request, listener);
|
||||||
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
listener.onFailure(t);
|
listener.onFailure(t);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.elasticsearch.index.shard.ShardId;
|
||||||
import org.elasticsearch.index.shard.ShardUtils;
|
import org.elasticsearch.index.shard.ShardUtils;
|
||||||
import org.elasticsearch.shield.authz.InternalAuthorizationService;
|
import org.elasticsearch.shield.authz.InternalAuthorizationService;
|
||||||
import org.elasticsearch.shield.authz.accesscontrol.DocumentSubsetReader.DocumentSubsetDirectoryReader;
|
import org.elasticsearch.shield.authz.accesscontrol.DocumentSubsetReader.DocumentSubsetDirectoryReader;
|
||||||
|
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||||
import org.elasticsearch.shield.support.Exceptions;
|
import org.elasticsearch.shield.support.Exceptions;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -54,13 +55,16 @@ public final class ShieldIndexSearcherWrapper extends AbstractComponent implemen
|
||||||
private final Set<String> allowedMetaFields;
|
private final Set<String> allowedMetaFields;
|
||||||
private final IndexQueryParserService parserService;
|
private final IndexQueryParserService parserService;
|
||||||
private final BitsetFilterCache bitsetFilterCache;
|
private final BitsetFilterCache bitsetFilterCache;
|
||||||
|
private final ShieldLicenseState shieldLicenseState;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ShieldIndexSearcherWrapper(@IndexSettings Settings indexSettings, IndexQueryParserService parserService, MapperService mapperService, BitsetFilterCache bitsetFilterCache) {
|
public ShieldIndexSearcherWrapper(@IndexSettings Settings indexSettings, IndexQueryParserService parserService,
|
||||||
|
MapperService mapperService, BitsetFilterCache bitsetFilterCache, ShieldLicenseState shieldLicenseState) {
|
||||||
super(indexSettings);
|
super(indexSettings);
|
||||||
this.mapperService = mapperService;
|
this.mapperService = mapperService;
|
||||||
this.parserService = parserService;
|
this.parserService = parserService;
|
||||||
this.bitsetFilterCache = bitsetFilterCache;
|
this.bitsetFilterCache = bitsetFilterCache;
|
||||||
|
this.shieldLicenseState = shieldLicenseState;
|
||||||
|
|
||||||
Set<String> allowedMetaFields = new HashSet<>();
|
Set<String> allowedMetaFields = new HashSet<>();
|
||||||
allowedMetaFields.addAll(Arrays.asList(MapperService.getAllMetaFields()));
|
allowedMetaFields.addAll(Arrays.asList(MapperService.getAllMetaFields()));
|
||||||
|
@ -73,6 +77,10 @@ public final class ShieldIndexSearcherWrapper extends AbstractComponent implemen
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DirectoryReader wrap(DirectoryReader reader) {
|
public DirectoryReader wrap(DirectoryReader reader) {
|
||||||
|
if (shieldLicenseState.documentAndFieldLevelSecurityEnabled() == false) {
|
||||||
|
return reader;
|
||||||
|
}
|
||||||
|
|
||||||
final Set<String> allowedMetaFields = this.allowedMetaFields;
|
final Set<String> allowedMetaFields = this.allowedMetaFields;
|
||||||
try {
|
try {
|
||||||
RequestContext context = RequestContext.current();
|
RequestContext context = RequestContext.current();
|
||||||
|
@ -124,6 +132,10 @@ public final class ShieldIndexSearcherWrapper extends AbstractComponent implemen
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IndexSearcher wrap(EngineConfig engineConfig, IndexSearcher searcher) throws EngineException {
|
public IndexSearcher wrap(EngineConfig engineConfig, IndexSearcher searcher) throws EngineException {
|
||||||
|
if (shieldLicenseState.documentAndFieldLevelSecurityEnabled() == false) {
|
||||||
|
return searcher;
|
||||||
|
}
|
||||||
|
|
||||||
final DirectoryReader directoryReader = (DirectoryReader) searcher.getIndexReader();
|
final DirectoryReader directoryReader = (DirectoryReader) searcher.getIndexReader();
|
||||||
if (directoryReader instanceof DocumentSubsetDirectoryReader) {
|
if (directoryReader instanceof DocumentSubsetDirectoryReader) {
|
||||||
// The reasons why we return a custom searcher:
|
// The reasons why we return a custom searcher:
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.license;
|
|
||||||
|
|
||||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serves as a registry of license event listeners and enables notifying them about the
|
|
||||||
* different events.
|
|
||||||
*
|
|
||||||
* This class is required to serves as a bridge between the license service and any other
|
|
||||||
* service that needs to recieve license events. The reason for that is that some services
|
|
||||||
* that require such notifications also serves as a dependency for the licensing service
|
|
||||||
* which introdues a circular dependency in guice (e.g. TransportService). This class then
|
|
||||||
* serves as a bridge between the different services to eliminate such circular dependencies.
|
|
||||||
*/
|
|
||||||
public class LicenseEventsNotifier {
|
|
||||||
|
|
||||||
private final Set<Listener> listeners = new HashSet<>();
|
|
||||||
|
|
||||||
public void register(Listener listener) {
|
|
||||||
listeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void notify(LicenseState state) {
|
|
||||||
for (Listener listener : listeners) {
|
|
||||||
listener.notify(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static interface Listener {
|
|
||||||
|
|
||||||
void notify(LicenseState state);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,8 +20,8 @@ public class LicenseModule extends AbstractShieldModule.Node {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configureNode() {
|
protected void configureNode() {
|
||||||
bind(LicenseService.class).asEagerSingleton();
|
bind(ShieldLicensee.class).asEagerSingleton();
|
||||||
bind(LicenseEventsNotifier.class).asEagerSingleton();
|
bind(ShieldLicenseState.class).asEagerSingleton();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyLicensePlugin() {
|
private void verifyLicensePlugin() {
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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.license;
|
||||||
|
|
||||||
|
import org.elasticsearch.license.core.License.OperationMode;
|
||||||
|
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||||
|
import org.elasticsearch.license.plugin.core.Licensee.Status;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class serves to decouple shield code that needs to check the license state from the {@link ShieldLicensee} as the
|
||||||
|
* tight coupling causes issues with guice injection and circular dependencies
|
||||||
|
*/
|
||||||
|
public class ShieldLicenseState {
|
||||||
|
|
||||||
|
// if we start disabled then we can emit false disabled messages and block legitimate requests...
|
||||||
|
protected volatile Status status = new Status(OperationMode.TRIAL, LicenseState.ENABLED);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the license allows for security features to be enabled (authc, authz, ip filter, audit, etc)
|
||||||
|
*/
|
||||||
|
public boolean securityEnabled() {
|
||||||
|
return status.getMode() != OperationMode.BASIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the stats and health API calls should be allowed. If a license is expired and past the grace
|
||||||
|
* period then we deny these calls.
|
||||||
|
*
|
||||||
|
* @return true if the license allows for the stats and health apis to be used.
|
||||||
|
*/
|
||||||
|
public boolean statsAndHealthEnabled() {
|
||||||
|
return status.getLicenseState() != LicenseState.DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the license enables DLS and FLS
|
||||||
|
*/
|
||||||
|
public boolean documentAndFieldLevelSecurityEnabled() {
|
||||||
|
Status status = this.status;
|
||||||
|
return status.getMode() == OperationMode.PLATINUM || status.getMode() == OperationMode.TRIAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateStatus(Status status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,37 +7,33 @@ package org.elasticsearch.shield.license;
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchException;
|
import org.elasticsearch.ElasticsearchException;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.license.core.License;
|
import org.elasticsearch.license.core.License;
|
||||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
import org.elasticsearch.license.core.License.OperationMode;
|
||||||
import org.elasticsearch.license.plugin.core.Licensee;
|
import org.elasticsearch.license.plugin.core.*;
|
||||||
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
|
|
||||||
import org.elasticsearch.shield.ShieldPlugin;
|
import org.elasticsearch.shield.ShieldPlugin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class LicenseService extends AbstractLifecycleComponent<LicenseService> implements Licensee {
|
public class ShieldLicensee extends AbstractLicenseeComponent<ShieldLicensee> implements Licensee {
|
||||||
|
|
||||||
public static final String FEATURE_NAME = ShieldPlugin.NAME;
|
private final boolean isTribeNode;
|
||||||
|
private final ShieldLicenseState shieldLicenseState;
|
||||||
private final LicenseeRegistry licenseeRegistry;
|
|
||||||
private final LicenseEventsNotifier notifier;
|
|
||||||
|
|
||||||
private volatile LicenseState state = LicenseState.DISABLED;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public LicenseService(Settings settings, LicenseeRegistry licenseeRegistry, LicenseEventsNotifier notifier) {
|
public ShieldLicensee(Settings settings, LicenseeRegistry clientService,
|
||||||
super(settings);
|
LicensesManagerService managerService, ShieldLicenseState shieldLicenseState) {
|
||||||
this.licenseeRegistry = licenseeRegistry;
|
super(settings, ShieldPlugin.NAME, clientService, managerService);
|
||||||
this.notifier = notifier;
|
add(new Listener() {
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String id() {
|
public void onChange(License license, Status status) {
|
||||||
return FEATURE_NAME;
|
shieldLicenseState.updateStatus(status);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.shieldLicenseState = shieldLicenseState;
|
||||||
|
this.isTribeNode = settings.getGroups("tribe", true).isEmpty() == false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -78,31 +74,13 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> i
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChange(License license, LicenseState state) {
|
protected void doStart() throws ElasticsearchException {;
|
||||||
synchronized (this) {
|
if (isTribeNode) {
|
||||||
this.state = state;
|
|
||||||
notifier.notify(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public LicenseState state() {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doStart() throws ElasticsearchException {
|
|
||||||
if (settings.getGroups("tribe", true).isEmpty()) {
|
|
||||||
licenseeRegistry.register(this);
|
|
||||||
} else {
|
|
||||||
//TODO currently we disable licensing on tribe node. remove this once es core supports merging cluster
|
//TODO currently we disable licensing on tribe node. remove this once es core supports merging cluster
|
||||||
onChange(null, LicenseState.ENABLED);
|
this.status = new Status(OperationMode.TRIAL, LicenseState.ENABLED);
|
||||||
|
shieldLicenseState.updateStatus(status);
|
||||||
|
} else {
|
||||||
|
super.doStart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doStop() throws ElasticsearchException {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doClose() throws ElasticsearchException {
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -13,6 +13,7 @@ import org.elasticsearch.http.netty.NettyHttpRequest;
|
||||||
import org.elasticsearch.rest.*;
|
import org.elasticsearch.rest.*;
|
||||||
import org.elasticsearch.shield.authc.AuthenticationService;
|
import org.elasticsearch.shield.authc.AuthenticationService;
|
||||||
import org.elasticsearch.shield.authc.pki.PkiRealm;
|
import org.elasticsearch.shield.authc.pki.PkiRealm;
|
||||||
|
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||||
import org.elasticsearch.shield.transport.SSLClientAuth;
|
import org.elasticsearch.shield.transport.SSLClientAuth;
|
||||||
import org.elasticsearch.shield.transport.netty.ShieldNettyHttpServerTransport;
|
import org.elasticsearch.shield.transport.netty.ShieldNettyHttpServerTransport;
|
||||||
import org.jboss.netty.handler.ssl.SslHandler;
|
import org.jboss.netty.handler.ssl.SslHandler;
|
||||||
|
@ -28,11 +29,13 @@ public class ShieldRestFilter extends RestFilter {
|
||||||
|
|
||||||
private final AuthenticationService service;
|
private final AuthenticationService service;
|
||||||
private final ESLogger logger;
|
private final ESLogger logger;
|
||||||
|
private final ShieldLicenseState licenseState;
|
||||||
private final boolean extractClientCertificate;
|
private final boolean extractClientCertificate;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ShieldRestFilter(AuthenticationService service, RestController controller, Settings settings) {
|
public ShieldRestFilter(AuthenticationService service, RestController controller, Settings settings, ShieldLicenseState licenseState) {
|
||||||
this.service = service;
|
this.service = service;
|
||||||
|
this.licenseState = licenseState;
|
||||||
controller.registerFilter(this);
|
controller.registerFilter(this);
|
||||||
boolean ssl = settings.getAsBoolean(ShieldNettyHttpServerTransport.HTTP_SSL_SETTING, ShieldNettyHttpServerTransport.HTTP_SSL_DEFAULT);
|
boolean ssl = settings.getAsBoolean(ShieldNettyHttpServerTransport.HTTP_SSL_SETTING, ShieldNettyHttpServerTransport.HTTP_SSL_DEFAULT);
|
||||||
extractClientCertificate = ssl && SSLClientAuth.parse(settings.get(ShieldNettyHttpServerTransport.HTTP_CLIENT_AUTH_SETTING), ShieldNettyHttpServerTransport.HTTP_CLIENT_AUTH_DEFAULT).enabled();
|
extractClientCertificate = ssl && SSLClientAuth.parse(settings.get(ShieldNettyHttpServerTransport.HTTP_CLIENT_AUTH_SETTING), ShieldNettyHttpServerTransport.HTTP_CLIENT_AUTH_DEFAULT).enabled();
|
||||||
|
@ -47,6 +50,7 @@ public class ShieldRestFilter extends RestFilter {
|
||||||
@Override
|
@Override
|
||||||
public void process(RestRequest request, RestChannel channel, RestFilterChain filterChain) throws Exception {
|
public void process(RestRequest request, RestChannel channel, RestFilterChain filterChain) throws Exception {
|
||||||
|
|
||||||
|
if (licenseState.securityEnabled()) {
|
||||||
// CORS - allow for preflight unauthenticated OPTIONS request
|
// CORS - allow for preflight unauthenticated OPTIONS request
|
||||||
if (request.method() != RestRequest.Method.OPTIONS) {
|
if (request.method() != RestRequest.Method.OPTIONS) {
|
||||||
if (extractClientCertificate) {
|
if (extractClientCertificate) {
|
||||||
|
@ -56,6 +60,7 @@ public class ShieldRestFilter extends RestFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoteHostHeader.process(request);
|
RemoteHostHeader.process(request);
|
||||||
|
}
|
||||||
|
|
||||||
filterChain.continueProcessing(request, channel);
|
filterChain.continueProcessing(request, channel);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,10 @@ import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.inject.internal.Nullable;
|
import org.elasticsearch.common.inject.internal.Nullable;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
|
||||||
import org.elasticsearch.rest.*;
|
import org.elasticsearch.rest.*;
|
||||||
import org.elasticsearch.shield.ShieldBuild;
|
import org.elasticsearch.shield.ShieldBuild;
|
||||||
import org.elasticsearch.shield.ShieldPlugin;
|
import org.elasticsearch.shield.ShieldPlugin;
|
||||||
import org.elasticsearch.shield.license.LicenseService;
|
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||||
|
|
||||||
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
||||||
import static org.elasticsearch.rest.RestRequest.Method.HEAD;
|
import static org.elasticsearch.rest.RestRequest.Method.HEAD;
|
||||||
|
@ -24,14 +23,14 @@ import static org.elasticsearch.rest.RestRequest.Method.HEAD;
|
||||||
public class RestShieldInfoAction extends BaseRestHandler {
|
public class RestShieldInfoAction extends BaseRestHandler {
|
||||||
|
|
||||||
private final ClusterName clusterName;
|
private final ClusterName clusterName;
|
||||||
private final LicenseService licenseService;
|
private final ShieldLicenseState shieldLicenseState;
|
||||||
private final boolean shieldEnabled;
|
private final boolean shieldEnabled;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public RestShieldInfoAction(Settings settings, RestController controller, Client client, ClusterName clusterName, @Nullable LicenseService licenseService) {
|
public RestShieldInfoAction(Settings settings, RestController controller, Client client, ClusterName clusterName, @Nullable ShieldLicenseState licenseState) {
|
||||||
super(settings, controller, client);
|
super(settings, controller, client);
|
||||||
this.clusterName = clusterName;
|
this.clusterName = clusterName;
|
||||||
this.licenseService = licenseService;
|
this.shieldLicenseState = licenseState;
|
||||||
this.shieldEnabled = ShieldPlugin.shieldEnabled(settings);
|
this.shieldEnabled = ShieldPlugin.shieldEnabled(settings);
|
||||||
controller.registerHandler(GET, "/_shield", this);
|
controller.registerHandler(GET, "/_shield", this);
|
||||||
controller.registerHandler(HEAD, "/_shield", this);
|
controller.registerHandler(HEAD, "/_shield", this);
|
||||||
|
@ -72,7 +71,10 @@ public class RestShieldInfoAction extends BaseRestHandler {
|
||||||
|
|
||||||
private Status resolveStatus() {
|
private Status resolveStatus() {
|
||||||
if (shieldEnabled) {
|
if (shieldEnabled) {
|
||||||
if (licenseService.state() != LicenseState.DISABLED) {
|
assert shieldLicenseState != null;
|
||||||
|
// TODO this is error prone since the state could change between checks. We can also make this status better
|
||||||
|
// but we may remove this endpoint since it no longer serves much purpose
|
||||||
|
if (shieldLicenseState.securityEnabled() && shieldLicenseState.statsAndHealthEnabled()) {
|
||||||
return Status.ENABLED;
|
return Status.ENABLED;
|
||||||
}
|
}
|
||||||
return Status.UNLICENSED;
|
return Status.UNLICENSED;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import org.elasticsearch.shield.action.ShieldActionMapper;
|
||||||
import org.elasticsearch.shield.authc.AuthenticationService;
|
import org.elasticsearch.shield.authc.AuthenticationService;
|
||||||
import org.elasticsearch.shield.authz.AuthorizationService;
|
import org.elasticsearch.shield.authz.AuthorizationService;
|
||||||
import org.elasticsearch.shield.authz.accesscontrol.RequestContext;
|
import org.elasticsearch.shield.authz.accesscontrol.RequestContext;
|
||||||
|
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||||
import org.elasticsearch.shield.transport.netty.ShieldNettyTransport;
|
import org.elasticsearch.shield.transport.netty.ShieldNettyTransport;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
import org.elasticsearch.transport.*;
|
import org.elasticsearch.transport.*;
|
||||||
|
@ -35,6 +36,7 @@ public class ShieldServerTransportService extends TransportService {
|
||||||
protected final AuthorizationService authzService;
|
protected final AuthorizationService authzService;
|
||||||
protected final ShieldActionMapper actionMapper;
|
protected final ShieldActionMapper actionMapper;
|
||||||
protected final ClientTransportFilter clientFilter;
|
protected final ClientTransportFilter clientFilter;
|
||||||
|
protected final ShieldLicenseState licenseState;
|
||||||
|
|
||||||
protected final Map<String, ServerTransportFilter> profileFilters;
|
protected final Map<String, ServerTransportFilter> profileFilters;
|
||||||
|
|
||||||
|
@ -43,12 +45,14 @@ public class ShieldServerTransportService extends TransportService {
|
||||||
AuthenticationService authcService,
|
AuthenticationService authcService,
|
||||||
AuthorizationService authzService,
|
AuthorizationService authzService,
|
||||||
ShieldActionMapper actionMapper,
|
ShieldActionMapper actionMapper,
|
||||||
ClientTransportFilter clientTransportFilter) {
|
ClientTransportFilter clientTransportFilter,
|
||||||
|
ShieldLicenseState licenseState) {
|
||||||
super(settings, transport, threadPool);
|
super(settings, transport, threadPool);
|
||||||
this.authcService = authcService;
|
this.authcService = authcService;
|
||||||
this.authzService = authzService;
|
this.authzService = authzService;
|
||||||
this.actionMapper = actionMapper;
|
this.actionMapper = actionMapper;
|
||||||
this.clientFilter = clientTransportFilter;
|
this.clientFilter = clientTransportFilter;
|
||||||
|
this.licenseState = licenseState;
|
||||||
this.profileFilters = initializeProfileFilters();
|
this.profileFilters = initializeProfileFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,13 +68,13 @@ public class ShieldServerTransportService extends TransportService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <Request extends TransportRequest> void registerRequestHandler(String action, Supplier<Request> requestFactory, String executor, TransportRequestHandler<Request> handler) {
|
public <Request extends TransportRequest> void registerRequestHandler(String action, Supplier<Request> requestFactory, String executor, TransportRequestHandler<Request> handler) {
|
||||||
TransportRequestHandler<Request> wrappedHandler = new ProfileSecuredRequestHandler<>(action, handler, profileFilters);
|
TransportRequestHandler<Request> wrappedHandler = new ProfileSecuredRequestHandler<>(action, handler, profileFilters, licenseState);
|
||||||
super.registerRequestHandler(action, requestFactory, executor, wrappedHandler);
|
super.registerRequestHandler(action, requestFactory, executor, wrappedHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <Request extends TransportRequest> void registerRequestHandler(String action, Supplier<Request> request, String executor, boolean forceExecution, TransportRequestHandler<Request> handler) {
|
public <Request extends TransportRequest> void registerRequestHandler(String action, Supplier<Request> request, String executor, boolean forceExecution, TransportRequestHandler<Request> handler) {
|
||||||
TransportRequestHandler<Request> wrappedHandler = new ProfileSecuredRequestHandler<>(action, handler, profileFilters);
|
TransportRequestHandler<Request> wrappedHandler = new ProfileSecuredRequestHandler<>(action, handler, profileFilters, licenseState);
|
||||||
super.registerRequestHandler(action, request, executor, forceExecution, wrappedHandler);
|
super.registerRequestHandler(action, request, executor, forceExecution, wrappedHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +108,7 @@ public class ShieldServerTransportService extends TransportService {
|
||||||
profileFilters.put(NettyTransport.DEFAULT_PROFILE, new ServerTransportFilter.NodeProfile(authcService, authzService, actionMapper, extractClientCert));
|
profileFilters.put(NettyTransport.DEFAULT_PROFILE, new ServerTransportFilter.NodeProfile(authcService, authzService, actionMapper, extractClientCert));
|
||||||
}
|
}
|
||||||
|
|
||||||
return profileFilters;
|
return Collections.unmodifiableMap(profileFilters);
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerTransportFilter transportFilter(String profile) {
|
ServerTransportFilter transportFilter(String profile) {
|
||||||
|
@ -116,17 +120,20 @@ public class ShieldServerTransportService extends TransportService {
|
||||||
protected final String action;
|
protected final String action;
|
||||||
protected final TransportRequestHandler<T> handler;
|
protected final TransportRequestHandler<T> handler;
|
||||||
private final Map<String, ServerTransportFilter> profileFilters;
|
private final Map<String, ServerTransportFilter> profileFilters;
|
||||||
|
private final ShieldLicenseState licenseState;
|
||||||
|
|
||||||
public ProfileSecuredRequestHandler(String action, TransportRequestHandler<T> handler, Map<String, ServerTransportFilter> profileFilters) {
|
public ProfileSecuredRequestHandler(String action, TransportRequestHandler<T> handler, Map<String, ServerTransportFilter> profileFilters, ShieldLicenseState licenseState) {
|
||||||
this.action = action;
|
this.action = action;
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.profileFilters = profileFilters;
|
this.profileFilters = profileFilters;
|
||||||
|
this.licenseState = licenseState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void messageReceived(T request, TransportChannel channel) throws Exception {
|
public void messageReceived(T request, TransportChannel channel) throws Exception {
|
||||||
try {
|
try {
|
||||||
|
if (licenseState.securityEnabled()) {
|
||||||
String profile = channel.getProfileName();
|
String profile = channel.getProfileName();
|
||||||
ServerTransportFilter filter = profileFilters.get(profile);
|
ServerTransportFilter filter = profileFilters.get(profile);
|
||||||
|
|
||||||
|
@ -140,6 +147,7 @@ public class ShieldServerTransportService extends TransportService {
|
||||||
}
|
}
|
||||||
assert filter != null;
|
assert filter != null;
|
||||||
filter.inbound(action, request, channel);
|
filter.inbound(action, request, channel);
|
||||||
|
}
|
||||||
RequestContext context = new RequestContext(request);
|
RequestContext context = new RequestContext(request);
|
||||||
RequestContext.setCurrent(context);
|
RequestContext.setCurrent(context);
|
||||||
handler.messageReceived(request, channel);
|
handler.messageReceived(request, channel);
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.elasticsearch.common.transport.TransportAddress;
|
||||||
import org.elasticsearch.http.HttpServerTransport;
|
import org.elasticsearch.http.HttpServerTransport;
|
||||||
import org.elasticsearch.node.settings.NodeSettingsService;
|
import org.elasticsearch.node.settings.NodeSettingsService;
|
||||||
import org.elasticsearch.shield.audit.AuditTrail;
|
import org.elasticsearch.shield.audit.AuditTrail;
|
||||||
|
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||||
import org.elasticsearch.transport.Transport;
|
import org.elasticsearch.transport.Transport;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
@ -73,16 +74,19 @@ public class IPFilter extends AbstractLifecycleComponent<IPFilter> {
|
||||||
private NodeSettingsService nodeSettingsService;
|
private NodeSettingsService nodeSettingsService;
|
||||||
private final AuditTrail auditTrail;
|
private final AuditTrail auditTrail;
|
||||||
private final Transport transport;
|
private final Transport transport;
|
||||||
|
private final ShieldLicenseState licenseState;
|
||||||
private final boolean alwaysAllowBoundAddresses;
|
private final boolean alwaysAllowBoundAddresses;
|
||||||
private Map<String, ShieldIpFilterRule[]> rules = Collections.EMPTY_MAP;
|
private Map<String, ShieldIpFilterRule[]> rules = Collections.EMPTY_MAP;
|
||||||
private HttpServerTransport httpServerTransport = null;
|
private HttpServerTransport httpServerTransport = null;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public IPFilter(final Settings settings, AuditTrail auditTrail, NodeSettingsService nodeSettingsService, Transport transport) {
|
public IPFilter(final Settings settings, AuditTrail auditTrail, NodeSettingsService nodeSettingsService,
|
||||||
|
Transport transport, ShieldLicenseState licenseState) {
|
||||||
super(settings);
|
super(settings);
|
||||||
this.nodeSettingsService = nodeSettingsService;
|
this.nodeSettingsService = nodeSettingsService;
|
||||||
this.auditTrail = auditTrail;
|
this.auditTrail = auditTrail;
|
||||||
this.transport = transport;
|
this.transport = transport;
|
||||||
|
this.licenseState = licenseState;
|
||||||
this.alwaysAllowBoundAddresses = settings.getAsBoolean("shield.filter.always_allow_bound_address", true);
|
this.alwaysAllowBoundAddresses = settings.getAsBoolean("shield.filter.always_allow_bound_address", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +126,12 @@ public class IPFilter extends AbstractLifecycleComponent<IPFilter> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean accept(String profile, InetAddress peerAddress) {
|
public boolean accept(String profile, InetAddress peerAddress) {
|
||||||
|
if (licenseState.securityEnabled() == false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!rules.containsKey(profile)) {
|
if (!rules.containsKey(profile)) {
|
||||||
|
// FIXME we need to audit here
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,26 +13,31 @@ import org.elasticsearch.action.admin.cluster.stats.ClusterStatsResponse;
|
||||||
import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
|
import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
|
||||||
import org.elasticsearch.action.index.IndexResponse;
|
import org.elasticsearch.action.index.IndexResponse;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
|
import org.elasticsearch.client.support.Headers;
|
||||||
|
import org.elasticsearch.client.transport.NoNodeAvailableException;
|
||||||
|
import org.elasticsearch.client.transport.TransportClient;
|
||||||
import org.elasticsearch.common.component.AbstractComponent;
|
import org.elasticsearch.common.component.AbstractComponent;
|
||||||
import org.elasticsearch.common.inject.AbstractModule;
|
import org.elasticsearch.common.inject.AbstractModule;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.inject.Module;
|
import org.elasticsearch.common.inject.Module;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.license.core.License;
|
import org.elasticsearch.license.core.License;
|
||||||
|
import org.elasticsearch.license.core.License.OperationMode;
|
||||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||||
import org.elasticsearch.license.plugin.core.Licensee;
|
import org.elasticsearch.license.plugin.core.Licensee;
|
||||||
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
|
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
|
||||||
|
import org.elasticsearch.node.Node;
|
||||||
import org.elasticsearch.plugins.Plugin;
|
import org.elasticsearch.plugins.Plugin;
|
||||||
import org.elasticsearch.rest.RestStatus;
|
import org.elasticsearch.rest.RestStatus;
|
||||||
import org.elasticsearch.shield.license.LicenseService;
|
import org.elasticsearch.shield.ShieldPlugin;
|
||||||
|
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
|
||||||
import org.elasticsearch.test.ShieldIntegTestCase;
|
import org.elasticsearch.test.ShieldIntegTestCase;
|
||||||
import org.elasticsearch.test.ShieldSettingsSource;
|
import org.elasticsearch.test.ShieldSettingsSource;
|
||||||
|
import org.elasticsearch.transport.Transport;
|
||||||
|
import org.junit.After;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
|
||||||
|
@ -95,6 +100,18 @@ public class LicensingTests extends ShieldIntegTestCase {
|
||||||
return InternalLicensePlugin.NAME;
|
return InternalLicensePlugin.NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Settings nodeSettings(int nodeOrdinal) {
|
||||||
|
return Settings.builder().put(super.nodeSettings(nodeOrdinal))
|
||||||
|
.put(Node.HTTP_ENABLED, true)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void resetLicensing() {
|
||||||
|
enableLicensing();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEnableDisableBehaviour() throws Exception {
|
public void testEnableDisableBehaviour() throws Exception {
|
||||||
IndexResponse indexResponse = index("test", "type", jsonBuilder()
|
IndexResponse indexResponse = index("test", "type", jsonBuilder()
|
||||||
|
@ -121,7 +138,7 @@ public class LicensingTests extends ShieldIntegTestCase {
|
||||||
fail("expected an license expired exception when executing an index stats action");
|
fail("expected an license expired exception when executing an index stats action");
|
||||||
} catch (ElasticsearchSecurityException ee) {
|
} catch (ElasticsearchSecurityException ee) {
|
||||||
// expected
|
// expected
|
||||||
assertThat(ee.getHeader("es.license.expired.feature"), hasItem(LicenseService.FEATURE_NAME));
|
assertThat(ee.getHeader("es.license.expired.feature"), hasItem(ShieldPlugin.NAME));
|
||||||
assertThat(ee.status(), is(RestStatus.UNAUTHORIZED));
|
assertThat(ee.status(), is(RestStatus.UNAUTHORIZED));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +147,7 @@ public class LicensingTests extends ShieldIntegTestCase {
|
||||||
fail("expected an license expired exception when executing cluster stats action");
|
fail("expected an license expired exception when executing cluster stats action");
|
||||||
} catch (ElasticsearchSecurityException ee) {
|
} catch (ElasticsearchSecurityException ee) {
|
||||||
// expected
|
// expected
|
||||||
assertThat(ee.getHeader("es.license.expired.feature"), hasItem(LicenseService.FEATURE_NAME));
|
assertThat(ee.getHeader("es.license.expired.feature"), hasItem(ShieldPlugin.NAME));
|
||||||
assertThat(ee.status(), is(RestStatus.UNAUTHORIZED));
|
assertThat(ee.status(), is(RestStatus.UNAUTHORIZED));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +156,7 @@ public class LicensingTests extends ShieldIntegTestCase {
|
||||||
fail("expected an license expired exception when executing cluster health action");
|
fail("expected an license expired exception when executing cluster health action");
|
||||||
} catch (ElasticsearchSecurityException ee) {
|
} catch (ElasticsearchSecurityException ee) {
|
||||||
// expected
|
// expected
|
||||||
assertThat(ee.getHeader("es.license.expired.feature"), hasItem(LicenseService.FEATURE_NAME));
|
assertThat(ee.getHeader("es.license.expired.feature"), hasItem(ShieldPlugin.NAME));
|
||||||
assertThat(ee.status(), is(RestStatus.UNAUTHORIZED));
|
assertThat(ee.status(), is(RestStatus.UNAUTHORIZED));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,11 +165,11 @@ public class LicensingTests extends ShieldIntegTestCase {
|
||||||
fail("expected an license expired exception when executing cluster health action");
|
fail("expected an license expired exception when executing cluster health action");
|
||||||
} catch (ElasticsearchSecurityException ee) {
|
} catch (ElasticsearchSecurityException ee) {
|
||||||
// expected
|
// expected
|
||||||
assertThat(ee.getHeader("es.license.expired.feature"), hasItem(LicenseService.FEATURE_NAME));
|
assertThat(ee.getHeader("es.license.expired.feature"), hasItem(ShieldPlugin.NAME));
|
||||||
assertThat(ee.status(), is(RestStatus.UNAUTHORIZED));
|
assertThat(ee.status(), is(RestStatus.UNAUTHORIZED));
|
||||||
}
|
}
|
||||||
|
|
||||||
enableLicensing();
|
enableLicensing(LicensingTests.generateLicense(randomFrom(OperationMode.values())));
|
||||||
|
|
||||||
IndicesStatsResponse indicesStatsResponse = client.admin().indices().prepareStats().get();
|
IndicesStatsResponse indicesStatsResponse = client.admin().indices().prepareStats().get();
|
||||||
assertNoFailures(indicesStatsResponse);
|
assertNoFailures(indicesStatsResponse);
|
||||||
|
@ -170,16 +187,75 @@ public class LicensingTests extends ShieldIntegTestCase {
|
||||||
assertThat(nodeStats, notNullValue());
|
assertThat(nodeStats, notNullValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestAuthenticationByLicenseType() throws Exception {
|
||||||
|
// the default of the licensing tests is basic
|
||||||
|
assertThat(httpClient().path("/").execute().getStatusCode(), is(200));
|
||||||
|
|
||||||
|
// generate a new license with a mode that enables auth
|
||||||
|
OperationMode mode = randomFrom(OperationMode.GOLD, OperationMode.TRIAL, OperationMode.PLATINUM);
|
||||||
|
enableLicensing(generateLicense(mode));
|
||||||
|
assertThat(httpClient().path("/").execute().getStatusCode(), is(401));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransportClientAuthenticationByLicenseType() throws Exception {
|
||||||
|
Settings.Builder builder = Settings.builder()
|
||||||
|
.put(internalCluster().transportClient().settings());
|
||||||
|
// remove user info
|
||||||
|
builder.remove("shield.user");
|
||||||
|
builder.remove(Headers.PREFIX + "." + UsernamePasswordToken.BASIC_AUTH_HEADER);
|
||||||
|
|
||||||
|
// basic has no auth
|
||||||
|
try (TransportClient client = TransportClient.builder().settings(builder).addPlugin(ShieldPlugin.class).build()) {
|
||||||
|
client.addTransportAddress(internalCluster().getDataNodeInstance(Transport.class).boundAddress().publishAddress());
|
||||||
|
assertGreenClusterState(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
// enable a license that enables security
|
||||||
|
OperationMode mode = randomFrom(OperationMode.GOLD, OperationMode.PLATINUM, OperationMode.TRIAL);
|
||||||
|
enableLicensing(generateLicense(mode));
|
||||||
|
|
||||||
|
try (TransportClient client = TransportClient.builder().settings(builder).addPlugin(ShieldPlugin.class).build()) {
|
||||||
|
client.addTransportAddress(internalCluster().getDataNodeInstance(Transport.class).boundAddress().publishAddress());
|
||||||
|
client.admin().cluster().prepareHealth().get();
|
||||||
|
fail("should not have been able to connect to a node!");
|
||||||
|
} catch (NoNodeAvailableException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void disableLicensing() {
|
public static void disableLicensing() {
|
||||||
|
disableLicensing(InternalLicenseeRegistry.DUMMY_LICENSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void disableLicensing(License license) {
|
||||||
for (InternalLicenseeRegistry service : internalCluster().getInstances(InternalLicenseeRegistry.class)) {
|
for (InternalLicenseeRegistry service : internalCluster().getInstances(InternalLicenseeRegistry.class)) {
|
||||||
service.disable();
|
service.disable(license);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void enableLicensing() {
|
public static void enableLicensing() {
|
||||||
for (InternalLicenseeRegistry service : internalCluster().getInstances(InternalLicenseeRegistry.class)) {
|
enableLicensing(InternalLicenseeRegistry.DUMMY_LICENSE);
|
||||||
service.enable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void enableLicensing(License license) {
|
||||||
|
for (InternalLicenseeRegistry service : internalCluster().getInstances(InternalLicenseeRegistry.class)) {
|
||||||
|
service.enable(license);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static License generateLicense(OperationMode operationMode) {
|
||||||
|
return License.builder()
|
||||||
|
.expiryDate(System.currentTimeMillis())
|
||||||
|
.issueDate(System.currentTimeMillis())
|
||||||
|
.issuedTo("LicensingTests")
|
||||||
|
.issuer("test")
|
||||||
|
.maxNodes(Integer.MAX_VALUE)
|
||||||
|
.signature("_signature")
|
||||||
|
.type(operationMode.toString().toLowerCase(Locale.ROOT))
|
||||||
|
.uid(String.valueOf(randomLong()) + System.identityHashCode(LicensingTests.class))
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InternalLicensePlugin extends Plugin {
|
public static class InternalLicensePlugin extends Plugin {
|
||||||
|
@ -228,24 +304,24 @@ public class LicensingTests extends ShieldIntegTestCase {
|
||||||
@Inject
|
@Inject
|
||||||
public InternalLicenseeRegistry(Settings settings) {
|
public InternalLicenseeRegistry(Settings settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
enable();
|
enable(DUMMY_LICENSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void register(Licensee licensee) {
|
public void register(Licensee licensee) {
|
||||||
licensees.add(licensee);
|
licensees.add(licensee);
|
||||||
enable();
|
enable(DUMMY_LICENSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void enable() {
|
void enable(License license) {
|
||||||
for (Licensee licensee : licensees) {
|
for (Licensee licensee : licensees) {
|
||||||
licensee.onChange(DUMMY_LICENSE, LicenseState.ENABLED);
|
licensee.onChange(license, LicenseState.ENABLED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void disable() {
|
void disable(License license) {
|
||||||
for (Licensee licensee : licensees) {
|
for (Licensee licensee : licensees) {
|
||||||
licensee.onChange(DUMMY_LICENSE, LicenseState.DISABLED);
|
licensee.onChange(license, LicenseState.DISABLED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import org.apache.http.impl.client.HttpClients;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.http.HttpServerTransport;
|
import org.elasticsearch.http.HttpServerTransport;
|
||||||
import org.elasticsearch.integration.LicensingTests;
|
import org.elasticsearch.integration.LicensingTests;
|
||||||
|
import org.elasticsearch.license.core.License.OperationMode;
|
||||||
import org.elasticsearch.node.Node;
|
import org.elasticsearch.node.Node;
|
||||||
import org.elasticsearch.plugins.Plugin;
|
import org.elasticsearch.plugins.Plugin;
|
||||||
import org.elasticsearch.shield.authc.support.SecuredString;
|
import org.elasticsearch.shield.authc.support.SecuredString;
|
||||||
|
@ -104,11 +105,31 @@ public class ShieldPluginEnabledDisabledTests extends ShieldIntegTestCase {
|
||||||
@Test
|
@Test
|
||||||
public void testShieldInfoStatus() throws IOException {
|
public void testShieldInfoStatus() throws IOException {
|
||||||
HttpServerTransport httpServerTransport = internalCluster().getDataNodeInstance(HttpServerTransport.class);
|
HttpServerTransport httpServerTransport = internalCluster().getDataNodeInstance(HttpServerTransport.class);
|
||||||
|
OperationMode mode;
|
||||||
|
if (enabled) {
|
||||||
|
mode = randomFrom(OperationMode.values());
|
||||||
|
LicensingTests.enableLicensing(LicensingTests.generateLicense(mode));
|
||||||
|
} else {
|
||||||
|
// this is the default right now
|
||||||
|
mode = OperationMode.BASIC;
|
||||||
|
}
|
||||||
|
|
||||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||||
HttpResponse response = new HttpRequestBuilder(httpClient).httpTransport(httpServerTransport).method("GET").path("/_shield").addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
HttpResponse response = new HttpRequestBuilder(httpClient).httpTransport(httpServerTransport).method("GET").path("/_shield").addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||||
basicAuthHeaderValue(ShieldSettingsSource.DEFAULT_USER_NAME, new SecuredString(ShieldSettingsSource.DEFAULT_PASSWORD.toCharArray()))).execute();
|
basicAuthHeaderValue(ShieldSettingsSource.DEFAULT_USER_NAME, new SecuredString(ShieldSettingsSource.DEFAULT_PASSWORD.toCharArray()))).execute();
|
||||||
assertThat(response.getStatusCode(), is(OK.getStatus()));
|
assertThat(response.getStatusCode(), is(OK.getStatus()));
|
||||||
assertThat(new JsonPath(response.getBody()).evaluate("status").toString(), equalTo(enabled ? "enabled" : "disabled"));
|
|
||||||
|
String expectedValue;
|
||||||
|
if (enabled) {
|
||||||
|
if (mode == OperationMode.BASIC) {
|
||||||
|
expectedValue = "unlicensed";
|
||||||
|
} else {
|
||||||
|
expectedValue = "enabled";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
expectedValue = "disabled";
|
||||||
|
}
|
||||||
|
assertThat(new JsonPath(response.getBody()).evaluate("status").toString(), equalTo(expectedValue));
|
||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
LicensingTests.disableLicensing();
|
LicensingTests.disableLicensing();
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
package org.elasticsearch.shield;
|
package org.elasticsearch.shield;
|
||||||
|
|
||||||
import org.elasticsearch.Version;
|
import org.elasticsearch.Version;
|
||||||
|
import org.elasticsearch.shield.license.ShieldLicensee;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -28,7 +29,7 @@ public class VersionCompatibilityTests extends ESTestCase {
|
||||||
@Test
|
@Test
|
||||||
public void testCompatibility() {
|
public void testCompatibility() {
|
||||||
/**
|
/**
|
||||||
* see https://github.com/elasticsearch/elasticsearch/issues/9372 {@link org.elasticsearch.shield.license.LicenseService}
|
* see https://github.com/elasticsearch/elasticsearch/issues/9372 {@link ShieldLicensee}
|
||||||
* Once es core supports merging cluster level custom metadata (licenses in our case), the tribe node will see some license coming from the tribe and everything will be ok.
|
* Once es core supports merging cluster level custom metadata (licenses in our case), the tribe node will see some license coming from the tribe and everything will be ok.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -11,14 +11,13 @@ import org.elasticsearch.action.ActionRequest;
|
||||||
import org.elasticsearch.action.search.SearchScrollRequest;
|
import org.elasticsearch.action.search.SearchScrollRequest;
|
||||||
import org.elasticsearch.action.support.ActionFilterChain;
|
import org.elasticsearch.action.support.ActionFilterChain;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
|
||||||
import org.elasticsearch.shield.User;
|
import org.elasticsearch.shield.User;
|
||||||
import org.elasticsearch.shield.action.interceptor.RequestInterceptor;
|
import org.elasticsearch.shield.action.interceptor.RequestInterceptor;
|
||||||
import org.elasticsearch.shield.audit.AuditTrail;
|
import org.elasticsearch.shield.audit.AuditTrail;
|
||||||
import org.elasticsearch.shield.authc.AuthenticationService;
|
import org.elasticsearch.shield.authc.AuthenticationService;
|
||||||
import org.elasticsearch.shield.authz.AuthorizationService;
|
import org.elasticsearch.shield.authz.AuthorizationService;
|
||||||
import org.elasticsearch.shield.crypto.CryptoService;
|
import org.elasticsearch.shield.crypto.CryptoService;
|
||||||
import org.elasticsearch.shield.license.LicenseEventsNotifier;
|
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -39,7 +38,7 @@ public class ShieldActionFilterTests extends ESTestCase {
|
||||||
private AuthorizationService authzService;
|
private AuthorizationService authzService;
|
||||||
private CryptoService cryptoService;
|
private CryptoService cryptoService;
|
||||||
private AuditTrail auditTrail;
|
private AuditTrail auditTrail;
|
||||||
private LicenseEventsNotifier licenseEventsNotifier;
|
private ShieldLicenseState shieldLicenseState;
|
||||||
private ShieldActionFilter filter;
|
private ShieldActionFilter filter;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@ -48,8 +47,10 @@ public class ShieldActionFilterTests extends ESTestCase {
|
||||||
authzService = mock(AuthorizationService.class);
|
authzService = mock(AuthorizationService.class);
|
||||||
cryptoService = mock(CryptoService.class);
|
cryptoService = mock(CryptoService.class);
|
||||||
auditTrail = mock(AuditTrail.class);
|
auditTrail = mock(AuditTrail.class);
|
||||||
licenseEventsNotifier = new MockLicenseEventsNotifier();
|
shieldLicenseState = mock(ShieldLicenseState.class);
|
||||||
filter = new ShieldActionFilter(Settings.EMPTY, authcService, authzService, cryptoService, auditTrail, licenseEventsNotifier, new ShieldActionMapper(), new HashSet<RequestInterceptor>());
|
when(shieldLicenseState.securityEnabled()).thenReturn(true);
|
||||||
|
when(shieldLicenseState.statsAndHealthEnabled()).thenReturn(true);
|
||||||
|
filter = new ShieldActionFilter(Settings.EMPTY, authcService, authzService, cryptoService, auditTrail, shieldLicenseState, new ShieldActionMapper(), new HashSet<RequestInterceptor>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -110,10 +111,16 @@ public class ShieldActionFilterTests extends ESTestCase {
|
||||||
verifyNoMoreInteractions(chain);
|
verifyNoMoreInteractions(chain);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MockLicenseEventsNotifier extends LicenseEventsNotifier {
|
@Test
|
||||||
@Override
|
public void testApplyUnlicensed() throws Exception {
|
||||||
public void register(MockLicenseEventsNotifier.Listener listener) {
|
ActionRequest request = mock(ActionRequest.class);
|
||||||
listener.notify(LicenseState.ENABLED);
|
ActionListener listener = mock(ActionListener.class);
|
||||||
}
|
ActionFilterChain chain = mock(ActionFilterChain.class);
|
||||||
|
when(shieldLicenseState.securityEnabled()).thenReturn(false);
|
||||||
|
filter.apply("_action", request, listener, chain);
|
||||||
|
verifyZeroInteractions(authcService);
|
||||||
|
verifyZeroInteractions(authzService);
|
||||||
|
verify(chain).proceed(eq("_action"), eq(request), eq(listener));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,8 +37,8 @@ import org.elasticsearch.index.mapper.MapperService;
|
||||||
import org.elasticsearch.index.query.IndexQueryParserService;
|
import org.elasticsearch.index.query.IndexQueryParserService;
|
||||||
import org.elasticsearch.index.query.ParsedQuery;
|
import org.elasticsearch.index.query.ParsedQuery;
|
||||||
import org.elasticsearch.index.shard.ShardId;
|
import org.elasticsearch.index.shard.ShardId;
|
||||||
import org.elasticsearch.indices.IndicesLifecycle;
|
|
||||||
import org.elasticsearch.shield.authz.InternalAuthorizationService;
|
import org.elasticsearch.shield.authz.InternalAuthorizationService;
|
||||||
|
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.transport.TransportRequest;
|
import org.elasticsearch.transport.TransportRequest;
|
||||||
import org.mockito.Matchers;
|
import org.mockito.Matchers;
|
||||||
|
@ -78,7 +78,6 @@ public class ShieldIndexSearcherWrapperIntegrationTests extends ESTestCase {
|
||||||
request.putInContext(InternalAuthorizationService.INDICES_PERMISSIONS_KEY, new IndicesAccessControl(true, singletonMap("_index", indexAccessControl)));
|
request.putInContext(InternalAuthorizationService.INDICES_PERMISSIONS_KEY, new IndicesAccessControl(true, singletonMap("_index", indexAccessControl)));
|
||||||
IndexQueryParserService parserService = mock(IndexQueryParserService.class);
|
IndexQueryParserService parserService = mock(IndexQueryParserService.class);
|
||||||
|
|
||||||
IndicesLifecycle indicesLifecycle = mock(IndicesLifecycle.class);
|
|
||||||
BitsetFilterCache bitsetFilterCache = mock(BitsetFilterCache.class);
|
BitsetFilterCache bitsetFilterCache = mock(BitsetFilterCache.class);
|
||||||
when(bitsetFilterCache.getBitSetProducer(Matchers.any(Query.class))).then(new Answer<BitSetProducer>() {
|
when(bitsetFilterCache.getBitSetProducer(Matchers.any(Query.class))).then(new Answer<BitSetProducer>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -100,8 +99,10 @@ public class ShieldIndexSearcherWrapperIntegrationTests extends ESTestCase {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
ShieldLicenseState licenseState = mock(ShieldLicenseState.class);
|
||||||
|
when(licenseState.documentAndFieldLevelSecurityEnabled()).thenReturn(true);
|
||||||
ShieldIndexSearcherWrapper wrapper = new ShieldIndexSearcherWrapper(
|
ShieldIndexSearcherWrapper wrapper = new ShieldIndexSearcherWrapper(
|
||||||
Settings.EMPTY, parserService, mapperService, bitsetFilterCache
|
Settings.EMPTY, parserService, mapperService, bitsetFilterCache, licenseState
|
||||||
);
|
);
|
||||||
|
|
||||||
Directory directory = newDirectory();
|
Directory directory = newDirectory();
|
||||||
|
|
|
@ -47,6 +47,7 @@ import org.elasticsearch.index.similarity.SimilarityService;
|
||||||
import org.elasticsearch.script.ScriptService;
|
import org.elasticsearch.script.ScriptService;
|
||||||
import org.elasticsearch.search.aggregations.LeafBucketCollector;
|
import org.elasticsearch.search.aggregations.LeafBucketCollector;
|
||||||
import org.elasticsearch.shield.authz.InternalAuthorizationService;
|
import org.elasticsearch.shield.authz.InternalAuthorizationService;
|
||||||
|
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.transport.TransportRequest;
|
import org.elasticsearch.transport.TransportRequest;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
@ -73,6 +74,7 @@ public class ShieldIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
private MapperService mapperService;
|
private MapperService mapperService;
|
||||||
private ShieldIndexSearcherWrapper shieldIndexSearcherWrapper;
|
private ShieldIndexSearcherWrapper shieldIndexSearcherWrapper;
|
||||||
private ElasticsearchDirectoryReader esIn;
|
private ElasticsearchDirectoryReader esIn;
|
||||||
|
private ShieldLicenseState licenseState;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void before() throws Exception {
|
public void before() throws Exception {
|
||||||
|
@ -84,7 +86,9 @@ public class ShieldIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
mapperService = new MapperService(index, settings, analysisService, similarityService, scriptService);
|
mapperService = new MapperService(index, settings, analysisService, similarityService, scriptService);
|
||||||
|
|
||||||
shardId = new ShardId(index, 0);
|
shardId = new ShardId(index, 0);
|
||||||
shieldIndexSearcherWrapper = new ShieldIndexSearcherWrapper(settings, null, mapperService, null);
|
licenseState = mock(ShieldLicenseState.class);
|
||||||
|
when(licenseState.documentAndFieldLevelSecurityEnabled()).thenReturn(true);
|
||||||
|
shieldIndexSearcherWrapper = new ShieldIndexSearcherWrapper(settings, null, mapperService, null, licenseState);
|
||||||
IndexShard indexShard = mock(IndexShard.class);
|
IndexShard indexShard = mock(IndexShard.class);
|
||||||
when(indexShard.shardId()).thenReturn(shardId);
|
when(indexShard.shardId()).thenReturn(shardId);
|
||||||
|
|
||||||
|
@ -130,6 +134,21 @@ public class ShieldIndexSearcherWrapperUnitTests extends ESTestCase {
|
||||||
assertThat(result.getFieldNames().contains("_all"), is(false)); // _all contains actual user data and therefor can't be included by default
|
assertThat(result.getFieldNames().contains("_all"), is(false)); // _all contains actual user data and therefor can't be included by default
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testWrapReaderWhenFeatureDisabled() throws Exception {
|
||||||
|
when(licenseState.documentAndFieldLevelSecurityEnabled()).thenReturn(false);
|
||||||
|
DirectoryReader reader = shieldIndexSearcherWrapper.wrap(esIn);
|
||||||
|
assertThat(reader, sameInstance(esIn));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWrapSearcherWhenFeatureDisabled() throws Exception {
|
||||||
|
ShardId shardId = new ShardId("_index", 0);
|
||||||
|
EngineConfig engineConfig = new EngineConfig(shardId, null, null, Settings.EMPTY, null, null, null, null, null, null, new BM25Similarity(), null, null, null, new NoneQueryCache(shardId.index(), Settings.EMPTY), QueryCachingPolicy.ALWAYS_CACHE, null); // can't mock...
|
||||||
|
|
||||||
|
IndexSearcher indexSearcher = new IndexSearcher(esIn);
|
||||||
|
IndexSearcher result = shieldIndexSearcherWrapper.wrap(engineConfig, indexSearcher);
|
||||||
|
assertThat(result, sameInstance(indexSearcher));
|
||||||
|
}
|
||||||
|
|
||||||
public void testWildcards() throws Exception {
|
public void testWildcards() throws Exception {
|
||||||
XContentBuilder mappingSource = jsonBuilder().startObject().startObject("type").startObject("properties")
|
XContentBuilder mappingSource = jsonBuilder().startObject().startObject("type").startObject("properties")
|
||||||
.startObject("field1_a").field("type", "string").endObject()
|
.startObject("field1_a").field("type", "string").endObject()
|
||||||
|
|
|
@ -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.license;
|
||||||
|
|
||||||
|
import org.elasticsearch.license.core.License;
|
||||||
|
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||||
|
import org.elasticsearch.license.plugin.core.Licensee;
|
||||||
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for the {@link ShieldLicenseState}
|
||||||
|
*/
|
||||||
|
public class ShieldLicenseStateTests extends ESTestCase {
|
||||||
|
|
||||||
|
public void testDefaults() {
|
||||||
|
ShieldLicenseState licenseState = new ShieldLicenseState();
|
||||||
|
assertThat(licenseState.securityEnabled(), is(true));
|
||||||
|
assertThat(licenseState.statsAndHealthEnabled(), is(true));
|
||||||
|
assertThat(licenseState.documentAndFieldLevelSecurityEnabled(), is(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBasic() {
|
||||||
|
ShieldLicenseState licenseState = new ShieldLicenseState();
|
||||||
|
licenseState.updateStatus(new Licensee.Status(License.OperationMode.BASIC, randomBoolean() ? LicenseState.ENABLED : LicenseState.GRACE_PERIOD));
|
||||||
|
|
||||||
|
assertThat(licenseState.securityEnabled(), is(false));
|
||||||
|
assertThat(licenseState.statsAndHealthEnabled(), is(true));
|
||||||
|
assertThat(licenseState.documentAndFieldLevelSecurityEnabled(), is(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testBasicExpired() {
|
||||||
|
ShieldLicenseState licenseState = new ShieldLicenseState();
|
||||||
|
licenseState.updateStatus(new Licensee.Status(License.OperationMode.BASIC, LicenseState.DISABLED));
|
||||||
|
|
||||||
|
assertThat(licenseState.securityEnabled(), is(false));
|
||||||
|
assertThat(licenseState.statsAndHealthEnabled(), is(false));
|
||||||
|
assertThat(licenseState.documentAndFieldLevelSecurityEnabled(), is(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGold() {
|
||||||
|
ShieldLicenseState licenseState = new ShieldLicenseState();
|
||||||
|
licenseState.updateStatus(new Licensee.Status(License.OperationMode.GOLD, randomBoolean() ? LicenseState.ENABLED : LicenseState.GRACE_PERIOD));
|
||||||
|
|
||||||
|
assertThat(licenseState.securityEnabled(), is(true));
|
||||||
|
assertThat(licenseState.statsAndHealthEnabled(), is(true));
|
||||||
|
assertThat(licenseState.documentAndFieldLevelSecurityEnabled(), is(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGoldExpired() {
|
||||||
|
ShieldLicenseState licenseState = new ShieldLicenseState();
|
||||||
|
licenseState.updateStatus(new Licensee.Status(License.OperationMode.GOLD, LicenseState.DISABLED));
|
||||||
|
|
||||||
|
assertThat(licenseState.securityEnabled(), is(true));
|
||||||
|
assertThat(licenseState.statsAndHealthEnabled(), is(false));
|
||||||
|
assertThat(licenseState.documentAndFieldLevelSecurityEnabled(), is(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPlatinum() {
|
||||||
|
ShieldLicenseState licenseState = new ShieldLicenseState();
|
||||||
|
licenseState.updateStatus(new Licensee.Status(License.OperationMode.PLATINUM, randomBoolean() ? LicenseState.ENABLED : LicenseState.GRACE_PERIOD));
|
||||||
|
|
||||||
|
assertThat(licenseState.securityEnabled(), is(true));
|
||||||
|
assertThat(licenseState.statsAndHealthEnabled(), is(true));
|
||||||
|
assertThat(licenseState.documentAndFieldLevelSecurityEnabled(), is(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPlatinumExpired() {
|
||||||
|
ShieldLicenseState licenseState = new ShieldLicenseState();
|
||||||
|
licenseState.updateStatus(new Licensee.Status(License.OperationMode.PLATINUM, LicenseState.DISABLED));
|
||||||
|
|
||||||
|
assertThat(licenseState.securityEnabled(), is(true));
|
||||||
|
assertThat(licenseState.statsAndHealthEnabled(), is(false));
|
||||||
|
assertThat(licenseState.documentAndFieldLevelSecurityEnabled(), is(true));
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import org.elasticsearch.rest.RestFilterChain;
|
||||||
import org.elasticsearch.rest.RestRequest;
|
import org.elasticsearch.rest.RestRequest;
|
||||||
import org.elasticsearch.shield.User;
|
import org.elasticsearch.shield.User;
|
||||||
import org.elasticsearch.shield.authc.AuthenticationService;
|
import org.elasticsearch.shield.authc.AuthenticationService;
|
||||||
|
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -30,6 +31,7 @@ public class ShieldRestFilterTests extends ESTestCase {
|
||||||
private RestChannel channel;
|
private RestChannel channel;
|
||||||
private RestFilterChain chain;
|
private RestFilterChain chain;
|
||||||
private ShieldRestFilter filter;
|
private ShieldRestFilter filter;
|
||||||
|
private ShieldLicenseState licenseState;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() throws Exception {
|
public void init() throws Exception {
|
||||||
|
@ -37,7 +39,9 @@ public class ShieldRestFilterTests extends ESTestCase {
|
||||||
RestController restController = mock(RestController.class);
|
RestController restController = mock(RestController.class);
|
||||||
channel = mock(RestChannel.class);
|
channel = mock(RestChannel.class);
|
||||||
chain = mock(RestFilterChain.class);
|
chain = mock(RestFilterChain.class);
|
||||||
filter = new ShieldRestFilter(authcService, restController, Settings.EMPTY);
|
licenseState = mock(ShieldLicenseState.class);
|
||||||
|
when(licenseState.securityEnabled()).thenReturn(true);
|
||||||
|
filter = new ShieldRestFilter(authcService, restController, Settings.EMPTY, licenseState);
|
||||||
verify(restController).registerFilter(filter);
|
verify(restController).registerFilter(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +55,15 @@ public class ShieldRestFilterTests extends ESTestCase {
|
||||||
verifyZeroInteractions(channel);
|
verifyZeroInteractions(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProcessBasicLicense() throws Exception {
|
||||||
|
RestRequest request = mock(RestRequest.class);
|
||||||
|
when(licenseState.securityEnabled()).thenReturn(false);
|
||||||
|
filter.process(request, channel, chain);
|
||||||
|
verify(chain).continueProcessing(request, channel);
|
||||||
|
verifyZeroInteractions(channel, authcService);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProcess_AuthenticationError() throws Exception {
|
public void testProcess_AuthenticationError() throws Exception {
|
||||||
RestRequest request = mock(RestRequest.class);
|
RestRequest request = mock(RestRequest.class);
|
||||||
|
|
|
@ -18,6 +18,7 @@ import org.elasticsearch.plugins.Plugin;
|
||||||
import org.elasticsearch.shield.action.ShieldActionMapper;
|
import org.elasticsearch.shield.action.ShieldActionMapper;
|
||||||
import org.elasticsearch.shield.authc.AuthenticationService;
|
import org.elasticsearch.shield.authc.AuthenticationService;
|
||||||
import org.elasticsearch.shield.authz.AuthorizationService;
|
import org.elasticsearch.shield.authz.AuthorizationService;
|
||||||
|
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||||
import org.elasticsearch.test.ESIntegTestCase;
|
import org.elasticsearch.test.ESIntegTestCase;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
import org.elasticsearch.transport.*;
|
import org.elasticsearch.transport.*;
|
||||||
|
@ -298,7 +299,8 @@ public class TransportFilterTests extends ESIntegTestCase {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public InternalPluginServerTransportService(Settings settings, Transport transport, ThreadPool threadPool, AuthenticationService authcService, AuthorizationService authzService, ShieldActionMapper actionMapper, ClientTransportFilter clientTransportFilter) {
|
public InternalPluginServerTransportService(Settings settings, Transport transport, ThreadPool threadPool, AuthenticationService authcService, AuthorizationService authzService, ShieldActionMapper actionMapper, ClientTransportFilter clientTransportFilter) {
|
||||||
super(settings, transport, threadPool, authcService, authzService, actionMapper, clientTransportFilter);
|
super(settings, transport, threadPool, authcService, authzService, actionMapper, clientTransportFilter, mock(ShieldLicenseState.class));
|
||||||
|
when(licenseState.securityEnabled()).thenReturn(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Map<String, ServerTransportFilter> initializeProfileFilters() {
|
protected Map<String, ServerTransportFilter> initializeProfileFilters() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import org.elasticsearch.common.transport.TransportAddress;
|
||||||
import org.elasticsearch.http.HttpServerTransport;
|
import org.elasticsearch.http.HttpServerTransport;
|
||||||
import org.elasticsearch.node.settings.NodeSettingsService;
|
import org.elasticsearch.node.settings.NodeSettingsService;
|
||||||
import org.elasticsearch.shield.audit.AuditTrail;
|
import org.elasticsearch.shield.audit.AuditTrail;
|
||||||
|
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.test.junit.annotations.Network;
|
import org.elasticsearch.test.junit.annotations.Network;
|
||||||
import org.elasticsearch.transport.Transport;
|
import org.elasticsearch.transport.Transport;
|
||||||
|
@ -35,6 +36,7 @@ import static org.mockito.Mockito.*;
|
||||||
public class IPFilterTests extends ESTestCase {
|
public class IPFilterTests extends ESTestCase {
|
||||||
|
|
||||||
private IPFilter ipFilter;
|
private IPFilter ipFilter;
|
||||||
|
private ShieldLicenseState licenseState;
|
||||||
private AuditTrail auditTrail;
|
private AuditTrail auditTrail;
|
||||||
private Transport transport;
|
private Transport transport;
|
||||||
private HttpServerTransport httpTransport;
|
private HttpServerTransport httpTransport;
|
||||||
|
@ -42,6 +44,8 @@ public class IPFilterTests extends ESTestCase {
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() {
|
public void init() {
|
||||||
|
licenseState = mock(ShieldLicenseState.class);
|
||||||
|
when(licenseState.securityEnabled()).thenReturn(true);
|
||||||
auditTrail = mock(AuditTrail.class);
|
auditTrail = mock(AuditTrail.class);
|
||||||
nodeSettingsService = mock(NodeSettingsService.class);
|
nodeSettingsService = mock(NodeSettingsService.class);
|
||||||
|
|
||||||
|
@ -66,7 +70,7 @@ public class IPFilterTests extends ESTestCase {
|
||||||
.put("shield.transport.filter.allow", "127.0.0.1")
|
.put("shield.transport.filter.allow", "127.0.0.1")
|
||||||
.put("shield.transport.filter.deny", "10.0.0.0/8")
|
.put("shield.transport.filter.deny", "10.0.0.0/8")
|
||||||
.build();
|
.build();
|
||||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||||
|
|
||||||
assertAddressIsAllowed("127.0.0.1");
|
assertAddressIsAllowed("127.0.0.1");
|
||||||
assertAddressIsDenied("10.2.3.4");
|
assertAddressIsDenied("10.2.3.4");
|
||||||
|
@ -80,7 +84,7 @@ public class IPFilterTests extends ESTestCase {
|
||||||
.put("shield.transport.filter.allow", "2001:0db8:1234::/48")
|
.put("shield.transport.filter.allow", "2001:0db8:1234::/48")
|
||||||
.putArray("shield.transport.filter.deny", "1234:db8:85a3:0:0:8a2e:370:7334", "4321:db8:1234::/48")
|
.putArray("shield.transport.filter.deny", "1234:db8:85a3:0:0:8a2e:370:7334", "4321:db8:1234::/48")
|
||||||
.build();
|
.build();
|
||||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||||
|
|
||||||
assertAddressIsAllowed("2001:0db8:1234:0000:0000:8a2e:0370:7334");
|
assertAddressIsAllowed("2001:0db8:1234:0000:0000:8a2e:0370:7334");
|
||||||
assertAddressIsDenied("1234:0db8:85a3:0000:0000:8a2e:0370:7334");
|
assertAddressIsDenied("1234:0db8:85a3:0000:0000:8a2e:0370:7334");
|
||||||
|
@ -94,7 +98,7 @@ public class IPFilterTests extends ESTestCase {
|
||||||
.put("shield.transport.filter.allow", "127.0.0.1")
|
.put("shield.transport.filter.allow", "127.0.0.1")
|
||||||
.put("shield.transport.filter.deny", "*.google.com")
|
.put("shield.transport.filter.deny", "*.google.com")
|
||||||
.build();
|
.build();
|
||||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||||
|
|
||||||
assertAddressIsAllowed("127.0.0.1");
|
assertAddressIsAllowed("127.0.0.1");
|
||||||
assertAddressIsDenied("8.8.8.8");
|
assertAddressIsDenied("8.8.8.8");
|
||||||
|
@ -105,7 +109,7 @@ public class IPFilterTests extends ESTestCase {
|
||||||
Settings settings = settingsBuilder()
|
Settings settings = settingsBuilder()
|
||||||
.put("shield.transport.filter.allow", "_all")
|
.put("shield.transport.filter.allow", "_all")
|
||||||
.build();
|
.build();
|
||||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||||
|
|
||||||
assertAddressIsAllowed("127.0.0.1");
|
assertAddressIsAllowed("127.0.0.1");
|
||||||
assertAddressIsAllowed("173.194.70.100");
|
assertAddressIsAllowed("173.194.70.100");
|
||||||
|
@ -119,7 +123,7 @@ public class IPFilterTests extends ESTestCase {
|
||||||
.put("transport.profiles.client.shield.filter.allow", "192.168.0.1")
|
.put("transport.profiles.client.shield.filter.allow", "192.168.0.1")
|
||||||
.put("transport.profiles.client.shield.filter.deny", "_all")
|
.put("transport.profiles.client.shield.filter.deny", "_all")
|
||||||
.build();
|
.build();
|
||||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||||
|
|
||||||
assertAddressIsAllowed("127.0.0.1");
|
assertAddressIsAllowed("127.0.0.1");
|
||||||
assertAddressIsDenied("192.168.0.1");
|
assertAddressIsDenied("192.168.0.1");
|
||||||
|
@ -133,7 +137,7 @@ public class IPFilterTests extends ESTestCase {
|
||||||
.put("shield.transport.filter.allow", "10.0.0.1")
|
.put("shield.transport.filter.allow", "10.0.0.1")
|
||||||
.put("shield.transport.filter.deny", "10.0.0.0/8")
|
.put("shield.transport.filter.deny", "10.0.0.0/8")
|
||||||
.build();
|
.build();
|
||||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||||
|
|
||||||
assertAddressIsAllowed("10.0.0.1");
|
assertAddressIsAllowed("10.0.0.1");
|
||||||
assertAddressIsDenied("10.0.0.2");
|
assertAddressIsDenied("10.0.0.2");
|
||||||
|
@ -142,7 +146,7 @@ public class IPFilterTests extends ESTestCase {
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultAllow() throws Exception {
|
public void testDefaultAllow() throws Exception {
|
||||||
Settings settings = settingsBuilder().build();
|
Settings settings = settingsBuilder().build();
|
||||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||||
|
|
||||||
assertAddressIsAllowed("10.0.0.1");
|
assertAddressIsAllowed("10.0.0.1");
|
||||||
assertAddressIsAllowed("10.0.0.2");
|
assertAddressIsAllowed("10.0.0.2");
|
||||||
|
@ -156,7 +160,7 @@ public class IPFilterTests extends ESTestCase {
|
||||||
.put("shield.http.filter.allow", "10.0.0.0/8")
|
.put("shield.http.filter.allow", "10.0.0.0/8")
|
||||||
.put("shield.http.filter.deny", "192.168.0.1")
|
.put("shield.http.filter.deny", "192.168.0.1")
|
||||||
.build();
|
.build();
|
||||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||||
ipFilter.setHttpServerTransport(httpTransport);
|
ipFilter.setHttpServerTransport(httpTransport);
|
||||||
|
|
||||||
assertAddressIsAllowedForProfile(IPFilter.HTTP_PROFILE_NAME, "10.2.3.4");
|
assertAddressIsAllowedForProfile(IPFilter.HTTP_PROFILE_NAME, "10.2.3.4");
|
||||||
|
@ -169,7 +173,7 @@ public class IPFilterTests extends ESTestCase {
|
||||||
.put("shield.transport.filter.allow", "127.0.0.1")
|
.put("shield.transport.filter.allow", "127.0.0.1")
|
||||||
.put("shield.transport.filter.deny", "10.0.0.0/8")
|
.put("shield.transport.filter.deny", "10.0.0.0/8")
|
||||||
.build();
|
.build();
|
||||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||||
ipFilter.setHttpServerTransport(httpTransport);
|
ipFilter.setHttpServerTransport(httpTransport);
|
||||||
|
|
||||||
assertAddressIsAllowedForProfile(IPFilter.HTTP_PROFILE_NAME, "127.0.0.1");
|
assertAddressIsAllowedForProfile(IPFilter.HTTP_PROFILE_NAME, "127.0.0.1");
|
||||||
|
@ -189,7 +193,7 @@ public class IPFilterTests extends ESTestCase {
|
||||||
} else {
|
} else {
|
||||||
settings = settingsBuilder().put("shield.transport.filter.deny", "_all").build();
|
settings = settingsBuilder().put("shield.transport.filter.deny", "_all").build();
|
||||||
}
|
}
|
||||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||||
ipFilter.setHttpServerTransport(httpTransport);
|
ipFilter.setHttpServerTransport(httpTransport);
|
||||||
|
|
||||||
for (String addressString : addressStrings) {
|
for (String addressString : addressStrings) {
|
||||||
|
@ -198,6 +202,26 @@ public class IPFilterTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testThatAllAddressesAreAllowedWhenLicenseDisablesSecurity() {
|
||||||
|
Settings settings = settingsBuilder()
|
||||||
|
.put("shield.transport.filter.deny", "_all")
|
||||||
|
.build();
|
||||||
|
when(licenseState.securityEnabled()).thenReturn(false);
|
||||||
|
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||||
|
|
||||||
|
// don't use the assert helper because we don't want the audit trail to be invoked here
|
||||||
|
String message = String.format(Locale.ROOT, "Expected address %s to be allowed", "8.8.8.8");
|
||||||
|
InetAddress address = InetAddresses.forString("8.8.8.8");
|
||||||
|
assertThat(message, ipFilter.accept("default", address), is(true));
|
||||||
|
verifyZeroInteractions(auditTrail);
|
||||||
|
|
||||||
|
// for sanity enable license and check that it is denied
|
||||||
|
when(licenseState.securityEnabled()).thenReturn(true);
|
||||||
|
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||||
|
assertAddressIsDeniedForProfile("default", "8.8.8.8");
|
||||||
|
}
|
||||||
|
|
||||||
private void assertAddressIsAllowedForProfile(String profile, String ... inetAddresses) {
|
private void assertAddressIsAllowedForProfile(String profile, String ... inetAddresses) {
|
||||||
for (String inetAddress : inetAddresses) {
|
for (String inetAddress : inetAddresses) {
|
||||||
String message = String.format(Locale.ROOT, "Expected address %s to be allowed", inetAddress);
|
String message = String.format(Locale.ROOT, "Expected address %s to be allowed", inetAddress);
|
||||||
|
|
|
@ -14,6 +14,7 @@ import org.elasticsearch.common.transport.TransportAddress;
|
||||||
import org.elasticsearch.http.HttpServerTransport;
|
import org.elasticsearch.http.HttpServerTransport;
|
||||||
import org.elasticsearch.node.settings.NodeSettingsService;
|
import org.elasticsearch.node.settings.NodeSettingsService;
|
||||||
import org.elasticsearch.shield.audit.AuditTrail;
|
import org.elasticsearch.shield.audit.AuditTrail;
|
||||||
|
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||||
import org.elasticsearch.shield.transport.filter.IPFilter;
|
import org.elasticsearch.shield.transport.filter.IPFilter;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.transport.Transport;
|
import org.elasticsearch.transport.Transport;
|
||||||
|
@ -52,7 +53,9 @@ public class IPFilterNettyUpstreamHandlerTests extends ESTestCase {
|
||||||
when(transport.lifecycleState()).thenReturn(Lifecycle.State.STARTED);
|
when(transport.lifecycleState()).thenReturn(Lifecycle.State.STARTED);
|
||||||
|
|
||||||
NodeSettingsService nodeSettingsService = mock(NodeSettingsService.class);
|
NodeSettingsService nodeSettingsService = mock(NodeSettingsService.class);
|
||||||
IPFilter ipFilter = new IPFilter(settings, AuditTrail.NOOP, nodeSettingsService, transport).start();
|
ShieldLicenseState licenseState = mock(ShieldLicenseState.class);
|
||||||
|
when(licenseState.securityEnabled()).thenReturn(true);
|
||||||
|
IPFilter ipFilter = new IPFilter(settings, AuditTrail.NOOP, nodeSettingsService, transport, licenseState).start();
|
||||||
|
|
||||||
if (isHttpEnabled) {
|
if (isHttpEnabled) {
|
||||||
HttpServerTransport httpTransport = mock(HttpServerTransport.class);
|
HttpServerTransport httpTransport = mock(HttpServerTransport.class);
|
||||||
|
|
|
@ -12,7 +12,6 @@ import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.plugins.Plugin;
|
import org.elasticsearch.plugins.Plugin;
|
||||||
import org.elasticsearch.plugins.PluginInfo;
|
|
||||||
import org.elasticsearch.shield.ShieldPlugin;
|
import org.elasticsearch.shield.ShieldPlugin;
|
||||||
import org.elasticsearch.shield.authc.support.SecuredString;
|
import org.elasticsearch.shield.authc.support.SecuredString;
|
||||||
import org.elasticsearch.test.ESIntegTestCase.SuppressLocalMode;
|
import org.elasticsearch.test.ESIntegTestCase.SuppressLocalMode;
|
||||||
|
@ -22,8 +21,6 @@ import org.junit.BeforeClass;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.rules.ExternalResource;
|
import org.junit.rules.ExternalResource;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
Loading…
Reference in New Issue