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() {
|
||||
// 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 &&
|
||||
status.getLicenseState() != LicenseState.DISABLED;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ package org.elasticsearch.shield;
|
|||
|
||||
import org.elasticsearch.common.inject.util.Providers;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.shield.license.LicenseService;
|
||||
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||
import org.elasticsearch.shield.support.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";
|
||||
if (!clientMode) {
|
||||
// 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.index.SearcherWrapperInstaller;
|
||||
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.crypto.CryptoModule;
|
||||
import org.elasticsearch.shield.crypto.InternalCryptoService;
|
||||
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.action.RestShieldInfoAction;
|
||||
import org.elasticsearch.shield.rest.action.authc.cache.RestClearRealmCacheAction;
|
||||
|
@ -125,7 +124,7 @@ public class ShieldPlugin extends Plugin {
|
|||
@Override
|
||||
public Collection<Class<? extends LifecycleComponent>> nodeServices() {
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ import org.elasticsearch.action.support.ActionFilterChain;
|
|||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||
import org.elasticsearch.license.plugin.core.LicenseUtils;
|
||||
import org.elasticsearch.shield.ShieldPlugin;
|
||||
import org.elasticsearch.shield.User;
|
||||
import org.elasticsearch.shield.action.interceptor.RequestInterceptor;
|
||||
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.Privilege;
|
||||
import org.elasticsearch.shield.crypto.CryptoService;
|
||||
import org.elasticsearch.shield.license.LicenseEventsNotifier;
|
||||
import org.elasticsearch.shield.license.LicenseService;
|
||||
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
@ -49,24 +48,18 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte
|
|||
private final AuditTrail auditTrail;
|
||||
private final ShieldActionMapper actionMapper;
|
||||
private final Set<RequestInterceptor> requestInterceptors;
|
||||
|
||||
private volatile boolean licenseEnabled = true;
|
||||
private final ShieldLicenseState licenseState;
|
||||
|
||||
@Inject
|
||||
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);
|
||||
this.authcService = authcService;
|
||||
this.authzService = authzService;
|
||||
this.cryptoService = cryptoService;
|
||||
this.auditTrail = auditTrail;
|
||||
this.actionMapper = actionMapper;
|
||||
licenseEventsNotifier.register(new LicenseEventsNotifier.Listener() {
|
||||
@Override
|
||||
public void notify(LicenseState state) {
|
||||
licenseEnabled = state != LicenseState.DISABLED;
|
||||
}
|
||||
});
|
||||
this.licenseState = licenseState;
|
||||
this.requestInterceptors = requestInterceptors;
|
||||
}
|
||||
|
||||
|
@ -77,36 +70,40 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte
|
|||
A functional requirement - when the license of shield is disabled (invalid/expires), shield will continue
|
||||
to operate normally, except all read operations will be blocked.
|
||||
*/
|
||||
if (!licenseEnabled && 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" +
|
||||
"operations are blocked on shield license expiration. All data operations (read and write) continue to work. \n" +
|
||||
"If you have a new license, please update it. Otherwise, please reach out to your support contact.", action);
|
||||
throw LicenseUtils.newExpirationException(LicenseService.FEATURE_NAME);
|
||||
throw LicenseUtils.newExpirationException(ShieldPlugin.NAME);
|
||||
}
|
||||
|
||||
try {
|
||||
/**
|
||||
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
|
||||
by user interaction. Since these requests are triggered by es core modules, they are security
|
||||
agnostic and therefore not associated with any user. When these requests execute locally, they
|
||||
are executed directly on their relevant action. Since there is no other way a request can make
|
||||
it to the action without an associated user (not via REST or transport - this is taken care of by
|
||||
the {@link Rest} filter and the {@link ServerTransport} filter respectively), it's safe to assume a system user
|
||||
here if a request is not associated with any other user.
|
||||
*/
|
||||
if (licenseState.securityEnabled()) {
|
||||
/**
|
||||
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
|
||||
by user interaction. Since these requests are triggered by es core modules, they are security
|
||||
agnostic and therefore not associated with any user. When these requests execute locally, they
|
||||
are executed directly on their relevant action. Since there is no other way a request can make
|
||||
it to the action without an associated user (not via REST or transport - this is taken care of by
|
||||
the {@link Rest} filter and the {@link ServerTransport} filter respectively), it's safe to assume a system user
|
||||
here if a request is not associated with any other user.
|
||||
*/
|
||||
|
||||
String shieldAction = actionMapper.action(action, request);
|
||||
User user = authcService.authenticate(shieldAction, request, User.SYSTEM);
|
||||
authzService.authorize(user, shieldAction, request);
|
||||
request = unsign(user, shieldAction, request);
|
||||
String shieldAction = actionMapper.action(action, request);
|
||||
User user = authcService.authenticate(shieldAction, request, User.SYSTEM);
|
||||
authzService.authorize(user, shieldAction, request);
|
||||
request = unsign(user, shieldAction, request);
|
||||
|
||||
for (RequestInterceptor interceptor : requestInterceptors) {
|
||||
if (interceptor.supports(request)) {
|
||||
interceptor.intercept(request, user);
|
||||
for (RequestInterceptor interceptor : requestInterceptors) {
|
||||
if (interceptor.supports(request)) {
|
||||
interceptor.intercept(request, user);
|
||||
}
|
||||
}
|
||||
chain.proceed(action, request, new SigningListener(this, listener));
|
||||
} else {
|
||||
chain.proceed(action, request, listener);
|
||||
}
|
||||
chain.proceed(action, request, new SigningListener(this, listener));
|
||||
} catch (Throwable t) {
|
||||
listener.onFailure(t);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.elasticsearch.index.shard.ShardId;
|
|||
import org.elasticsearch.index.shard.ShardUtils;
|
||||
import org.elasticsearch.shield.authz.InternalAuthorizationService;
|
||||
import org.elasticsearch.shield.authz.accesscontrol.DocumentSubsetReader.DocumentSubsetDirectoryReader;
|
||||
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||
import org.elasticsearch.shield.support.Exceptions;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -54,13 +55,16 @@ public final class ShieldIndexSearcherWrapper extends AbstractComponent implemen
|
|||
private final Set<String> allowedMetaFields;
|
||||
private final IndexQueryParserService parserService;
|
||||
private final BitsetFilterCache bitsetFilterCache;
|
||||
private final ShieldLicenseState shieldLicenseState;
|
||||
|
||||
@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);
|
||||
this.mapperService = mapperService;
|
||||
this.parserService = parserService;
|
||||
this.bitsetFilterCache = bitsetFilterCache;
|
||||
this.shieldLicenseState = shieldLicenseState;
|
||||
|
||||
Set<String> allowedMetaFields = new HashSet<>();
|
||||
allowedMetaFields.addAll(Arrays.asList(MapperService.getAllMetaFields()));
|
||||
|
@ -73,6 +77,10 @@ public final class ShieldIndexSearcherWrapper extends AbstractComponent implemen
|
|||
|
||||
@Override
|
||||
public DirectoryReader wrap(DirectoryReader reader) {
|
||||
if (shieldLicenseState.documentAndFieldLevelSecurityEnabled() == false) {
|
||||
return reader;
|
||||
}
|
||||
|
||||
final Set<String> allowedMetaFields = this.allowedMetaFields;
|
||||
try {
|
||||
RequestContext context = RequestContext.current();
|
||||
|
@ -124,6 +132,10 @@ public final class ShieldIndexSearcherWrapper extends AbstractComponent implemen
|
|||
|
||||
@Override
|
||||
public IndexSearcher wrap(EngineConfig engineConfig, IndexSearcher searcher) throws EngineException {
|
||||
if (shieldLicenseState.documentAndFieldLevelSecurityEnabled() == false) {
|
||||
return searcher;
|
||||
}
|
||||
|
||||
final DirectoryReader directoryReader = (DirectoryReader) searcher.getIndexReader();
|
||||
if (directoryReader instanceof DocumentSubsetDirectoryReader) {
|
||||
// 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
|
||||
protected void configureNode() {
|
||||
bind(LicenseService.class).asEagerSingleton();
|
||||
bind(LicenseEventsNotifier.class).asEagerSingleton();
|
||||
bind(ShieldLicensee.class).asEagerSingleton();
|
||||
bind(ShieldLicenseState.class).asEagerSingleton();
|
||||
}
|
||||
|
||||
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.common.Strings;
|
||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||
import org.elasticsearch.license.plugin.core.Licensee;
|
||||
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
|
||||
import org.elasticsearch.license.core.License.OperationMode;
|
||||
import org.elasticsearch.license.plugin.core.*;
|
||||
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 LicenseeRegistry licenseeRegistry;
|
||||
private final LicenseEventsNotifier notifier;
|
||||
|
||||
private volatile LicenseState state = LicenseState.DISABLED;
|
||||
private final boolean isTribeNode;
|
||||
private final ShieldLicenseState shieldLicenseState;
|
||||
|
||||
@Inject
|
||||
public LicenseService(Settings settings, LicenseeRegistry licenseeRegistry, LicenseEventsNotifier notifier) {
|
||||
super(settings);
|
||||
this.licenseeRegistry = licenseeRegistry;
|
||||
this.notifier = notifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return FEATURE_NAME;
|
||||
public ShieldLicensee(Settings settings, LicenseeRegistry clientService,
|
||||
LicensesManagerService managerService, ShieldLicenseState shieldLicenseState) {
|
||||
super(settings, ShieldPlugin.NAME, clientService, managerService);
|
||||
add(new Listener() {
|
||||
@Override
|
||||
public void onChange(License license, Status status) {
|
||||
shieldLicenseState.updateStatus(status);
|
||||
}
|
||||
});
|
||||
this.shieldLicenseState = shieldLicenseState;
|
||||
this.isTribeNode = settings.getGroups("tribe", true).isEmpty() == false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -78,31 +74,13 @@ public class LicenseService extends AbstractLifecycleComponent<LicenseService> i
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onChange(License license, LicenseState state) {
|
||||
synchronized (this) {
|
||||
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 {
|
||||
protected void doStart() throws ElasticsearchException {;
|
||||
if (isTribeNode) {
|
||||
//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.shield.authc.AuthenticationService;
|
||||
import org.elasticsearch.shield.authc.pki.PkiRealm;
|
||||
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||
import org.elasticsearch.shield.transport.SSLClientAuth;
|
||||
import org.elasticsearch.shield.transport.netty.ShieldNettyHttpServerTransport;
|
||||
import org.jboss.netty.handler.ssl.SslHandler;
|
||||
|
@ -28,11 +29,13 @@ public class ShieldRestFilter extends RestFilter {
|
|||
|
||||
private final AuthenticationService service;
|
||||
private final ESLogger logger;
|
||||
private final ShieldLicenseState licenseState;
|
||||
private final boolean extractClientCertificate;
|
||||
|
||||
@Inject
|
||||
public ShieldRestFilter(AuthenticationService service, RestController controller, Settings settings) {
|
||||
public ShieldRestFilter(AuthenticationService service, RestController controller, Settings settings, ShieldLicenseState licenseState) {
|
||||
this.service = service;
|
||||
this.licenseState = licenseState;
|
||||
controller.registerFilter(this);
|
||||
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();
|
||||
|
@ -47,15 +50,17 @@ public class ShieldRestFilter extends RestFilter {
|
|||
@Override
|
||||
public void process(RestRequest request, RestChannel channel, RestFilterChain filterChain) throws Exception {
|
||||
|
||||
// CORS - allow for preflight unauthenticated OPTIONS request
|
||||
if (request.method() != RestRequest.Method.OPTIONS) {
|
||||
if (extractClientCertificate) {
|
||||
putClientCertificateInContext(request, logger);
|
||||
if (licenseState.securityEnabled()) {
|
||||
// CORS - allow for preflight unauthenticated OPTIONS request
|
||||
if (request.method() != RestRequest.Method.OPTIONS) {
|
||||
if (extractClientCertificate) {
|
||||
putClientCertificateInContext(request, logger);
|
||||
}
|
||||
service.authenticate(request);
|
||||
}
|
||||
service.authenticate(request);
|
||||
}
|
||||
|
||||
RemoteHostHeader.process(request);
|
||||
RemoteHostHeader.process(request);
|
||||
}
|
||||
|
||||
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.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||
import org.elasticsearch.rest.*;
|
||||
import org.elasticsearch.shield.ShieldBuild;
|
||||
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.HEAD;
|
||||
|
@ -24,14 +23,14 @@ import static org.elasticsearch.rest.RestRequest.Method.HEAD;
|
|||
public class RestShieldInfoAction extends BaseRestHandler {
|
||||
|
||||
private final ClusterName clusterName;
|
||||
private final LicenseService licenseService;
|
||||
private final ShieldLicenseState shieldLicenseState;
|
||||
private final boolean shieldEnabled;
|
||||
|
||||
@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);
|
||||
this.clusterName = clusterName;
|
||||
this.licenseService = licenseService;
|
||||
this.shieldLicenseState = licenseState;
|
||||
this.shieldEnabled = ShieldPlugin.shieldEnabled(settings);
|
||||
controller.registerHandler(GET, "/_shield", this);
|
||||
controller.registerHandler(HEAD, "/_shield", this);
|
||||
|
@ -72,7 +71,10 @@ public class RestShieldInfoAction extends BaseRestHandler {
|
|||
|
||||
private Status resolveStatus() {
|
||||
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.UNLICENSED;
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.elasticsearch.shield.action.ShieldActionMapper;
|
|||
import org.elasticsearch.shield.authc.AuthenticationService;
|
||||
import org.elasticsearch.shield.authz.AuthorizationService;
|
||||
import org.elasticsearch.shield.authz.accesscontrol.RequestContext;
|
||||
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||
import org.elasticsearch.shield.transport.netty.ShieldNettyTransport;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.*;
|
||||
|
@ -35,6 +36,7 @@ public class ShieldServerTransportService extends TransportService {
|
|||
protected final AuthorizationService authzService;
|
||||
protected final ShieldActionMapper actionMapper;
|
||||
protected final ClientTransportFilter clientFilter;
|
||||
protected final ShieldLicenseState licenseState;
|
||||
|
||||
protected final Map<String, ServerTransportFilter> profileFilters;
|
||||
|
||||
|
@ -43,12 +45,14 @@ public class ShieldServerTransportService extends TransportService {
|
|||
AuthenticationService authcService,
|
||||
AuthorizationService authzService,
|
||||
ShieldActionMapper actionMapper,
|
||||
ClientTransportFilter clientTransportFilter) {
|
||||
ClientTransportFilter clientTransportFilter,
|
||||
ShieldLicenseState licenseState) {
|
||||
super(settings, transport, threadPool);
|
||||
this.authcService = authcService;
|
||||
this.authzService = authzService;
|
||||
this.actionMapper = actionMapper;
|
||||
this.clientFilter = clientTransportFilter;
|
||||
this.licenseState = licenseState;
|
||||
this.profileFilters = initializeProfileFilters();
|
||||
}
|
||||
|
||||
|
@ -64,13 +68,13 @@ public class ShieldServerTransportService extends TransportService {
|
|||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -104,7 +108,7 @@ public class ShieldServerTransportService extends TransportService {
|
|||
profileFilters.put(NettyTransport.DEFAULT_PROFILE, new ServerTransportFilter.NodeProfile(authcService, authzService, actionMapper, extractClientCert));
|
||||
}
|
||||
|
||||
return profileFilters;
|
||||
return Collections.unmodifiableMap(profileFilters);
|
||||
}
|
||||
|
||||
ServerTransportFilter transportFilter(String profile) {
|
||||
|
@ -116,30 +120,34 @@ public class ShieldServerTransportService extends TransportService {
|
|||
protected final String action;
|
||||
protected final TransportRequestHandler<T> handler;
|
||||
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.handler = handler;
|
||||
this.profileFilters = profileFilters;
|
||||
this.licenseState = licenseState;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void messageReceived(T request, TransportChannel channel) throws Exception {
|
||||
try {
|
||||
String profile = channel.getProfileName();
|
||||
ServerTransportFilter filter = profileFilters.get(profile);
|
||||
if (licenseState.securityEnabled()) {
|
||||
String profile = channel.getProfileName();
|
||||
ServerTransportFilter filter = profileFilters.get(profile);
|
||||
|
||||
if (filter == null) {
|
||||
if (TransportService.DIRECT_RESPONSE_PROFILE.equals(profile)) {
|
||||
// apply the default filter to local requests. We never know what the request is or who sent it...
|
||||
filter = profileFilters.get("default");
|
||||
} else {
|
||||
throw new IllegalStateException("transport profile [" + profile + "] is not associated with a transport filter");
|
||||
if (filter == null) {
|
||||
if (TransportService.DIRECT_RESPONSE_PROFILE.equals(profile)) {
|
||||
// apply the default filter to local requests. We never know what the request is or who sent it...
|
||||
filter = profileFilters.get("default");
|
||||
} else {
|
||||
throw new IllegalStateException("transport profile [" + profile + "] is not associated with a transport filter");
|
||||
}
|
||||
}
|
||||
assert filter != null;
|
||||
filter.inbound(action, request, channel);
|
||||
}
|
||||
assert filter != null;
|
||||
filter.inbound(action, request, channel);
|
||||
RequestContext context = new RequestContext(request);
|
||||
RequestContext.setCurrent(context);
|
||||
handler.messageReceived(request, channel);
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.elasticsearch.common.transport.TransportAddress;
|
|||
import org.elasticsearch.http.HttpServerTransport;
|
||||
import org.elasticsearch.node.settings.NodeSettingsService;
|
||||
import org.elasticsearch.shield.audit.AuditTrail;
|
||||
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||
import org.elasticsearch.transport.Transport;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
@ -73,16 +74,19 @@ public class IPFilter extends AbstractLifecycleComponent<IPFilter> {
|
|||
private NodeSettingsService nodeSettingsService;
|
||||
private final AuditTrail auditTrail;
|
||||
private final Transport transport;
|
||||
private final ShieldLicenseState licenseState;
|
||||
private final boolean alwaysAllowBoundAddresses;
|
||||
private Map<String, ShieldIpFilterRule[]> rules = Collections.EMPTY_MAP;
|
||||
private HttpServerTransport httpServerTransport = null;
|
||||
|
||||
@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);
|
||||
this.nodeSettingsService = nodeSettingsService;
|
||||
this.auditTrail = auditTrail;
|
||||
this.transport = transport;
|
||||
this.licenseState = licenseState;
|
||||
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) {
|
||||
if (licenseState.securityEnabled() == false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!rules.containsKey(profile)) {
|
||||
// FIXME we need to audit here
|
||||
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.index.IndexResponse;
|
||||
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.inject.AbstractModule;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.inject.Module;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.core.License;
|
||||
import org.elasticsearch.license.core.License.OperationMode;
|
||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||
import org.elasticsearch.license.plugin.core.Licensee;
|
||||
import org.elasticsearch.license.plugin.core.LicenseeRegistry;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
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.ShieldSettingsSource;
|
||||
import org.elasticsearch.transport.Transport;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
|
||||
|
@ -95,6 +100,18 @@ public class LicensingTests extends ShieldIntegTestCase {
|
|||
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
|
||||
public void testEnableDisableBehaviour() throws Exception {
|
||||
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");
|
||||
} catch (ElasticsearchSecurityException ee) {
|
||||
// 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));
|
||||
}
|
||||
|
||||
|
@ -130,7 +147,7 @@ public class LicensingTests extends ShieldIntegTestCase {
|
|||
fail("expected an license expired exception when executing cluster stats action");
|
||||
} catch (ElasticsearchSecurityException ee) {
|
||||
// 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));
|
||||
}
|
||||
|
||||
|
@ -139,7 +156,7 @@ public class LicensingTests extends ShieldIntegTestCase {
|
|||
fail("expected an license expired exception when executing cluster health action");
|
||||
} catch (ElasticsearchSecurityException ee) {
|
||||
// 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));
|
||||
}
|
||||
|
||||
|
@ -148,11 +165,11 @@ public class LicensingTests extends ShieldIntegTestCase {
|
|||
fail("expected an license expired exception when executing cluster health action");
|
||||
} catch (ElasticsearchSecurityException ee) {
|
||||
// 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));
|
||||
}
|
||||
|
||||
enableLicensing();
|
||||
enableLicensing(LicensingTests.generateLicense(randomFrom(OperationMode.values())));
|
||||
|
||||
IndicesStatsResponse indicesStatsResponse = client.admin().indices().prepareStats().get();
|
||||
assertNoFailures(indicesStatsResponse);
|
||||
|
@ -170,18 +187,77 @@ public class LicensingTests extends ShieldIntegTestCase {
|
|||
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() {
|
||||
disableLicensing(InternalLicenseeRegistry.DUMMY_LICENSE);
|
||||
}
|
||||
|
||||
public static void disableLicensing(License license) {
|
||||
for (InternalLicenseeRegistry service : internalCluster().getInstances(InternalLicenseeRegistry.class)) {
|
||||
service.disable();
|
||||
service.disable(license);
|
||||
}
|
||||
}
|
||||
|
||||
public static void enableLicensing() {
|
||||
enableLicensing(InternalLicenseeRegistry.DUMMY_LICENSE);
|
||||
}
|
||||
|
||||
public static void enableLicensing(License license) {
|
||||
for (InternalLicenseeRegistry service : internalCluster().getInstances(InternalLicenseeRegistry.class)) {
|
||||
service.enable();
|
||||
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 final String NAME = "internal-licensing";
|
||||
|
@ -228,24 +304,24 @@ public class LicensingTests extends ShieldIntegTestCase {
|
|||
@Inject
|
||||
public InternalLicenseeRegistry(Settings settings) {
|
||||
super(settings);
|
||||
enable();
|
||||
enable(DUMMY_LICENSE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(Licensee licensee) {
|
||||
licensees.add(licensee);
|
||||
enable();
|
||||
enable(DUMMY_LICENSE);
|
||||
}
|
||||
|
||||
void enable() {
|
||||
void enable(License license) {
|
||||
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) {
|
||||
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.http.HttpServerTransport;
|
||||
import org.elasticsearch.integration.LicensingTests;
|
||||
import org.elasticsearch.license.core.License.OperationMode;
|
||||
import org.elasticsearch.node.Node;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.shield.authc.support.SecuredString;
|
||||
|
@ -104,11 +105,31 @@ public class ShieldPluginEnabledDisabledTests extends ShieldIntegTestCase {
|
|||
@Test
|
||||
public void testShieldInfoStatus() throws IOException {
|
||||
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()) {
|
||||
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();
|
||||
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) {
|
||||
LicensingTests.disableLicensing();
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.elasticsearch.shield;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.shield.license.ShieldLicensee;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -28,7 +29,7 @@ public class VersionCompatibilityTests extends ESTestCase {
|
|||
@Test
|
||||
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.
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -11,14 +11,13 @@ import org.elasticsearch.action.ActionRequest;
|
|||
import org.elasticsearch.action.search.SearchScrollRequest;
|
||||
import org.elasticsearch.action.support.ActionFilterChain;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.license.plugin.core.LicenseState;
|
||||
import org.elasticsearch.shield.User;
|
||||
import org.elasticsearch.shield.action.interceptor.RequestInterceptor;
|
||||
import org.elasticsearch.shield.audit.AuditTrail;
|
||||
import org.elasticsearch.shield.authc.AuthenticationService;
|
||||
import org.elasticsearch.shield.authz.AuthorizationService;
|
||||
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.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -39,7 +38,7 @@ public class ShieldActionFilterTests extends ESTestCase {
|
|||
private AuthorizationService authzService;
|
||||
private CryptoService cryptoService;
|
||||
private AuditTrail auditTrail;
|
||||
private LicenseEventsNotifier licenseEventsNotifier;
|
||||
private ShieldLicenseState shieldLicenseState;
|
||||
private ShieldActionFilter filter;
|
||||
|
||||
@Before
|
||||
|
@ -48,8 +47,10 @@ public class ShieldActionFilterTests extends ESTestCase {
|
|||
authzService = mock(AuthorizationService.class);
|
||||
cryptoService = mock(CryptoService.class);
|
||||
auditTrail = mock(AuditTrail.class);
|
||||
licenseEventsNotifier = new MockLicenseEventsNotifier();
|
||||
filter = new ShieldActionFilter(Settings.EMPTY, authcService, authzService, cryptoService, auditTrail, licenseEventsNotifier, new ShieldActionMapper(), new HashSet<RequestInterceptor>());
|
||||
shieldLicenseState = mock(ShieldLicenseState.class);
|
||||
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
|
||||
|
@ -110,10 +111,16 @@ public class ShieldActionFilterTests extends ESTestCase {
|
|||
verifyNoMoreInteractions(chain);
|
||||
}
|
||||
|
||||
private class MockLicenseEventsNotifier extends LicenseEventsNotifier {
|
||||
@Override
|
||||
public void register(MockLicenseEventsNotifier.Listener listener) {
|
||||
listener.notify(LicenseState.ENABLED);
|
||||
}
|
||||
@Test
|
||||
public void testApplyUnlicensed() throws Exception {
|
||||
ActionRequest request = mock(ActionRequest.class);
|
||||
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.ParsedQuery;
|
||||
import org.elasticsearch.index.shard.ShardId;
|
||||
import org.elasticsearch.indices.IndicesLifecycle;
|
||||
import org.elasticsearch.shield.authz.InternalAuthorizationService;
|
||||
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.transport.TransportRequest;
|
||||
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)));
|
||||
IndexQueryParserService parserService = mock(IndexQueryParserService.class);
|
||||
|
||||
IndicesLifecycle indicesLifecycle = mock(IndicesLifecycle.class);
|
||||
BitsetFilterCache bitsetFilterCache = mock(BitsetFilterCache.class);
|
||||
when(bitsetFilterCache.getBitSetProducer(Matchers.any(Query.class))).then(new Answer<BitSetProducer>() {
|
||||
@Override
|
||||
|
@ -100,8 +99,10 @@ public class ShieldIndexSearcherWrapperIntegrationTests extends ESTestCase {
|
|||
};
|
||||
}
|
||||
});
|
||||
ShieldLicenseState licenseState = mock(ShieldLicenseState.class);
|
||||
when(licenseState.documentAndFieldLevelSecurityEnabled()).thenReturn(true);
|
||||
ShieldIndexSearcherWrapper wrapper = new ShieldIndexSearcherWrapper(
|
||||
Settings.EMPTY, parserService, mapperService, bitsetFilterCache
|
||||
Settings.EMPTY, parserService, mapperService, bitsetFilterCache, licenseState
|
||||
);
|
||||
|
||||
Directory directory = newDirectory();
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.elasticsearch.index.similarity.SimilarityService;
|
|||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.search.aggregations.LeafBucketCollector;
|
||||
import org.elasticsearch.shield.authz.InternalAuthorizationService;
|
||||
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.transport.TransportRequest;
|
||||
import org.junit.After;
|
||||
|
@ -73,6 +74,7 @@ public class ShieldIndexSearcherWrapperUnitTests extends ESTestCase {
|
|||
private MapperService mapperService;
|
||||
private ShieldIndexSearcherWrapper shieldIndexSearcherWrapper;
|
||||
private ElasticsearchDirectoryReader esIn;
|
||||
private ShieldLicenseState licenseState;
|
||||
|
||||
@Before
|
||||
public void before() throws Exception {
|
||||
|
@ -84,7 +86,9 @@ public class ShieldIndexSearcherWrapperUnitTests extends ESTestCase {
|
|||
mapperService = new MapperService(index, settings, analysisService, similarityService, scriptService);
|
||||
|
||||
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);
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
XContentBuilder mappingSource = jsonBuilder().startObject().startObject("type").startObject("properties")
|
||||
.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.shield.User;
|
||||
import org.elasticsearch.shield.authc.AuthenticationService;
|
||||
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -30,6 +31,7 @@ public class ShieldRestFilterTests extends ESTestCase {
|
|||
private RestChannel channel;
|
||||
private RestFilterChain chain;
|
||||
private ShieldRestFilter filter;
|
||||
private ShieldLicenseState licenseState;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
|
@ -37,7 +39,9 @@ public class ShieldRestFilterTests extends ESTestCase {
|
|||
RestController restController = mock(RestController.class);
|
||||
channel = mock(RestChannel.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);
|
||||
}
|
||||
|
||||
|
@ -51,6 +55,15 @@ public class ShieldRestFilterTests extends ESTestCase {
|
|||
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
|
||||
public void testProcess_AuthenticationError() throws Exception {
|
||||
RestRequest request = mock(RestRequest.class);
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.elasticsearch.plugins.Plugin;
|
|||
import org.elasticsearch.shield.action.ShieldActionMapper;
|
||||
import org.elasticsearch.shield.authc.AuthenticationService;
|
||||
import org.elasticsearch.shield.authz.AuthorizationService;
|
||||
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||
import org.elasticsearch.test.ESIntegTestCase;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.*;
|
||||
|
@ -298,7 +299,8 @@ public class TransportFilterTests extends ESIntegTestCase {
|
|||
|
||||
@Inject
|
||||
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() {
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.elasticsearch.common.transport.TransportAddress;
|
|||
import org.elasticsearch.http.HttpServerTransport;
|
||||
import org.elasticsearch.node.settings.NodeSettingsService;
|
||||
import org.elasticsearch.shield.audit.AuditTrail;
|
||||
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
import org.elasticsearch.transport.Transport;
|
||||
|
@ -35,6 +36,7 @@ import static org.mockito.Mockito.*;
|
|||
public class IPFilterTests extends ESTestCase {
|
||||
|
||||
private IPFilter ipFilter;
|
||||
private ShieldLicenseState licenseState;
|
||||
private AuditTrail auditTrail;
|
||||
private Transport transport;
|
||||
private HttpServerTransport httpTransport;
|
||||
|
@ -42,6 +44,8 @@ public class IPFilterTests extends ESTestCase {
|
|||
|
||||
@Before
|
||||
public void init() {
|
||||
licenseState = mock(ShieldLicenseState.class);
|
||||
when(licenseState.securityEnabled()).thenReturn(true);
|
||||
auditTrail = mock(AuditTrail.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.deny", "10.0.0.0/8")
|
||||
.build();
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||
|
||||
assertAddressIsAllowed("127.0.0.1");
|
||||
assertAddressIsDenied("10.2.3.4");
|
||||
|
@ -80,7 +84,7 @@ public class IPFilterTests extends ESTestCase {
|
|||
.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")
|
||||
.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");
|
||||
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.deny", "*.google.com")
|
||||
.build();
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||
|
||||
assertAddressIsAllowed("127.0.0.1");
|
||||
assertAddressIsDenied("8.8.8.8");
|
||||
|
@ -105,7 +109,7 @@ public class IPFilterTests extends ESTestCase {
|
|||
Settings settings = settingsBuilder()
|
||||
.put("shield.transport.filter.allow", "_all")
|
||||
.build();
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||
|
||||
assertAddressIsAllowed("127.0.0.1");
|
||||
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.deny", "_all")
|
||||
.build();
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||
|
||||
assertAddressIsAllowed("127.0.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.deny", "10.0.0.0/8")
|
||||
.build();
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||
|
||||
assertAddressIsAllowed("10.0.0.1");
|
||||
assertAddressIsDenied("10.0.0.2");
|
||||
|
@ -142,7 +146,7 @@ public class IPFilterTests extends ESTestCase {
|
|||
@Test
|
||||
public void testDefaultAllow() throws Exception {
|
||||
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.2");
|
||||
|
@ -156,7 +160,7 @@ public class IPFilterTests extends ESTestCase {
|
|||
.put("shield.http.filter.allow", "10.0.0.0/8")
|
||||
.put("shield.http.filter.deny", "192.168.0.1")
|
||||
.build();
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||
ipFilter.setHttpServerTransport(httpTransport);
|
||||
|
||||
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.deny", "10.0.0.0/8")
|
||||
.build();
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport, licenseState).start();
|
||||
ipFilter.setHttpServerTransport(httpTransport);
|
||||
|
||||
assertAddressIsAllowedForProfile(IPFilter.HTTP_PROFILE_NAME, "127.0.0.1");
|
||||
|
@ -189,7 +193,7 @@ public class IPFilterTests extends ESTestCase {
|
|||
} else {
|
||||
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);
|
||||
|
||||
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) {
|
||||
for (String inetAddress : inetAddresses) {
|
||||
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.node.settings.NodeSettingsService;
|
||||
import org.elasticsearch.shield.audit.AuditTrail;
|
||||
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||
import org.elasticsearch.shield.transport.filter.IPFilter;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.transport.Transport;
|
||||
|
@ -52,7 +53,9 @@ public class IPFilterNettyUpstreamHandlerTests extends ESTestCase {
|
|||
when(transport.lifecycleState()).thenReturn(Lifecycle.State.STARTED);
|
||||
|
||||
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) {
|
||||
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.common.settings.Settings;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.plugins.PluginInfo;
|
||||
import org.elasticsearch.shield.ShieldPlugin;
|
||||
import org.elasticsearch.shield.authc.support.SecuredString;
|
||||
import org.elasticsearch.test.ESIntegTestCase.SuppressLocalMode;
|
||||
|
@ -22,8 +21,6 @@ import org.junit.BeforeClass;
|
|||
import org.junit.Rule;
|
||||
import org.junit.rules.ExternalResource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
|
Loading…
Reference in New Issue