IP Filtering: Make IP filtering a dynamic setting
In order to be able to configure ip filtering in a dynamic way, all the ip filter related settings have been made dynamic. This commit also fixed a bug, as the setting shield.http.filter.enabled was not working, but mentioned in the documentation. Documentation has been updated along the way. Closes elastic/elasticsearch#697 Original commit: elastic/x-pack-elasticsearch@2760c47b5b
This commit is contained in:
parent
c8a169e3be
commit
6a6e44545c
|
@ -7,6 +7,7 @@ package org.elasticsearch.shield;
|
|||
|
||||
import org.elasticsearch.client.Client;
|
||||
import org.elasticsearch.client.support.Headers;
|
||||
import org.elasticsearch.cluster.settings.ClusterDynamicSettingsModule;
|
||||
import org.elasticsearch.common.collect.ImmutableList;
|
||||
import org.elasticsearch.common.component.LifecycleComponent;
|
||||
import org.elasticsearch.common.inject.Module;
|
||||
|
@ -20,6 +21,7 @@ import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
|
|||
import org.elasticsearch.shield.authz.store.FileRolesStore;
|
||||
import org.elasticsearch.shield.license.LicenseService;
|
||||
import org.elasticsearch.shield.signature.InternalSignatureService;
|
||||
import org.elasticsearch.shield.transport.filter.IPFilter;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
@ -65,7 +67,7 @@ public class ShieldPlugin extends AbstractPlugin {
|
|||
@Override
|
||||
public Collection<Class<? extends LifecycleComponent>> services() {
|
||||
return enabled && !clientMode ?
|
||||
ImmutableList.<Class<? extends LifecycleComponent>>of(LicenseService.class, FileRolesStore.class, Realms.class, InternalSignatureService.class) :
|
||||
ImmutableList.<Class<? extends LifecycleComponent>>of(LicenseService.class, FileRolesStore.class, Realms.class, InternalSignatureService.class, IPFilter.class) :
|
||||
ImmutableList.<Class<? extends LifecycleComponent>>of();
|
||||
}
|
||||
|
||||
|
@ -81,6 +83,10 @@ public class ShieldPlugin extends AbstractPlugin {
|
|||
return settingsBuilder.build();
|
||||
}
|
||||
|
||||
public void onModule(ClusterDynamicSettingsModule clusterDynamicSettingsModule) {
|
||||
clusterDynamicSettingsModule.addDynamicSettings("shield.transport.filter.*", "shield.http.filter.*", "transport.profiles.*", IPFilter.IP_FILTER_ENABLED_SETTING, IPFilter.IP_FILTER_ENABLED_HTTP_SETTING);
|
||||
}
|
||||
|
||||
private void addUserSettings(ImmutableSettings.Builder settingsBuilder) {
|
||||
String authHeaderSettingName = Headers.PREFIX + "." + UsernamePasswordToken.BASIC_AUTH_HEADER;
|
||||
if (settings.get(authHeaderSettingName) != null) {
|
||||
|
|
|
@ -5,24 +5,29 @@
|
|||
*/
|
||||
package org.elasticsearch.shield.transport.filter;
|
||||
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.common.collect.HppcMaps;
|
||||
import org.elasticsearch.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.collect.Maps;
|
||||
import org.elasticsearch.common.collect.ObjectArrays;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||
import org.elasticsearch.common.component.Lifecycle;
|
||||
import org.elasticsearch.common.component.LifecycleListener;
|
||||
import org.elasticsearch.common.hppc.ObjectObjectOpenHashMap;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.jackson.dataformat.yaml.snakeyaml.error.YAMLException;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.inject.internal.Nullable;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.transport.InetSocketTransportAddress;
|
||||
import org.elasticsearch.http.HttpServerTransport;
|
||||
import org.elasticsearch.node.settings.NodeSettingsService;
|
||||
import org.elasticsearch.shield.audit.AuditTrail;
|
||||
import org.elasticsearch.transport.Transport;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class IPFilter extends AbstractComponent {
|
||||
public class IPFilter extends AbstractLifecycleComponent<IPFilter> {
|
||||
|
||||
/**
|
||||
* .http has been chosen for handling HTTP filters, which are not part of the profiles
|
||||
|
@ -32,6 +37,9 @@ public class IPFilter extends AbstractComponent {
|
|||
*/
|
||||
public static final String HTTP_PROFILE_NAME = ".http";
|
||||
|
||||
public static final String IP_FILTER_ENABLED_SETTING = "shield.transport.filter.enabled";
|
||||
public static final String IP_FILTER_ENABLED_HTTP_SETTING = "shield.http.filter.enabled";
|
||||
|
||||
public static final ShieldIpFilterRule DEFAULT_PROFILE_ACCEPT_ALL = new ShieldIpFilterRule(true, "default:accept_all") {
|
||||
@Override
|
||||
public boolean contains(InetAddress inetAddress) {
|
||||
|
@ -49,15 +57,61 @@ public class IPFilter extends AbstractComponent {
|
|||
}
|
||||
};
|
||||
|
||||
private final LifecycleListener parseSettingsListener = new LifecycleListener() {
|
||||
@Override
|
||||
public void afterStart() {
|
||||
IPFilter.this.rules = IPFilter.this.parseSettings(settings);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private NodeSettingsService nodeSettingsService;
|
||||
private final AuditTrail auditTrail;
|
||||
private final Map<String, ShieldIpFilterRule[]> rules;
|
||||
private final Transport transport;
|
||||
private Map<String, ShieldIpFilterRule[]> rules = Collections.EMPTY_MAP;
|
||||
private HttpServerTransport httpServerTransport = null;
|
||||
|
||||
@Inject
|
||||
public IPFilter(Settings settings, AuditTrail auditTrail) {
|
||||
public IPFilter(final Settings settings, AuditTrail auditTrail, NodeSettingsService nodeSettingsService, Transport transport) {
|
||||
super(settings);
|
||||
this.nodeSettingsService = nodeSettingsService;
|
||||
this.auditTrail = auditTrail;
|
||||
rules = parseSettings(settings, logger);
|
||||
this.transport = transport;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws ElasticsearchException {
|
||||
nodeSettingsService.addListener(new ApplySettings(settings));
|
||||
|
||||
if (transport.lifecycleState() == Lifecycle.State.STARTED) {
|
||||
rules = parseSettings(settings);
|
||||
} else {
|
||||
transport.addLifecycleListener(parseSettingsListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws ElasticsearchException {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doClose() throws ElasticsearchException {
|
||||
}
|
||||
|
||||
// this cannot be put into the constructor as HTTP might be disabled
|
||||
@Inject(optional = true)
|
||||
public void setHttpServerTransport(@Nullable HttpServerTransport httpServerTransport) {
|
||||
if (httpServerTransport == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.httpServerTransport = httpServerTransport;
|
||||
|
||||
if (httpServerTransport.lifecycleState() == Lifecycle.State.STARTED) {
|
||||
IPFilter.this.rules = IPFilter.this.parseSettings(settings);
|
||||
} else {
|
||||
httpServerTransport.addLifecycleListener(parseSettingsListener);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean accept(String profile, InetAddress peerAddress) {
|
||||
|
@ -81,44 +135,154 @@ public class IPFilter extends AbstractComponent {
|
|||
return true;
|
||||
}
|
||||
|
||||
private static Map<String, ShieldIpFilterRule[]> parseSettings(Settings settings, ESLogger logger) {
|
||||
if (!settings.getAsBoolean("shield.transport.filter.enabled", true)) {
|
||||
private Map<String, ShieldIpFilterRule[]> parseSettings(Settings settings) {
|
||||
boolean isIpFilterEnabled = settings.getAsBoolean(IP_FILTER_ENABLED_SETTING, true);
|
||||
boolean isHttpFilterEnabled = settings.getAsBoolean(IP_FILTER_ENABLED_HTTP_SETTING, isIpFilterEnabled);
|
||||
|
||||
if (!isIpFilterEnabled && !isHttpFilterEnabled) {
|
||||
return Collections.EMPTY_MAP;
|
||||
}
|
||||
|
||||
Map<String, ShieldIpFilterRule[]> profileRules = Maps.newHashMap();
|
||||
String[] allowed = settings.getAsArray("shield.transport.filter.allow");
|
||||
String[] denied = settings.getAsArray("shield.transport.filter.deny");
|
||||
String[] httpAllowed = settings.getAsArray("shield.http.filter.allow", settings.getAsArray("transport.profiles.default.shield.filter.allow", settings.getAsArray("shield.transport.filter.allow")));
|
||||
String[] httpDdenied = settings.getAsArray("shield.http.filter.deny", settings.getAsArray("transport.profiles.default.shield.filter.deny", settings.getAsArray("shield.transport.filter.deny")));
|
||||
|
||||
try {
|
||||
profileRules.put("default", ObjectArrays.concat(parseValue(allowed, true), parseValue(denied, false), ShieldIpFilterRule.class));
|
||||
profileRules.put(HTTP_PROFILE_NAME, ObjectArrays.concat(parseValue(httpAllowed, true), parseValue(httpDdenied, false), ShieldIpFilterRule.class));
|
||||
if (isHttpFilterEnabled && httpServerTransport != null && httpServerTransport.lifecycleState() == Lifecycle.State.STARTED) {
|
||||
InetAddress localAddress = ((InetSocketTransportAddress) this.httpServerTransport.boundAddress().boundAddress()).address().getAddress();
|
||||
String[] httpAllowed = settings.getAsArray("shield.http.filter.allow", settings.getAsArray("transport.profiles.default.shield.filter.allow", settings.getAsArray("shield.transport.filter.allow")));
|
||||
String[] httpDdenied = settings.getAsArray("shield.http.filter.deny", settings.getAsArray("transport.profiles.default.shield.filter.deny", settings.getAsArray("shield.transport.filter.deny")));
|
||||
profileRules.put(HTTP_PROFILE_NAME, ObjectArrays.concat(parseValue(httpAllowed, true, localAddress), parseValue(httpDdenied, false, localAddress), ShieldIpFilterRule.class));
|
||||
}
|
||||
|
||||
if (isIpFilterEnabled && this.transport.lifecycleState() == Lifecycle.State.STARTED) {
|
||||
InetAddress localAddress = ((InetSocketTransportAddress) this.transport.boundAddress().boundAddress()).address().getAddress();
|
||||
|
||||
String[] allowed = settings.getAsArray("shield.transport.filter.allow");
|
||||
String[] denied = settings.getAsArray("shield.transport.filter.deny");
|
||||
profileRules.put("default", ObjectArrays.concat(parseValue(allowed, true, localAddress), parseValue(denied, false, localAddress), ShieldIpFilterRule.class));
|
||||
|
||||
Map<String, Settings> groupedSettings = settings.getGroups("transport.profiles.");
|
||||
for (Map.Entry<String, Settings> entry : groupedSettings.entrySet()) {
|
||||
String profile = entry.getKey();
|
||||
Settings profileSettings = entry.getValue().getByPrefix("shield.filter.");
|
||||
profileRules.put(profile, ObjectArrays.concat(
|
||||
parseValue(profileSettings.getAsArray("allow"), true),
|
||||
parseValue(profileSettings.getAsArray("deny"), false),
|
||||
parseValue(profileSettings.getAsArray("allow"), true, localAddress),
|
||||
parseValue(profileSettings.getAsArray("deny"), false, localAddress),
|
||||
ShieldIpFilterRule.class));
|
||||
}
|
||||
|
||||
} catch (IOException | YAMLException e) {
|
||||
throw new ElasticsearchParseException("failed to read & parse rules from settings", e);
|
||||
}
|
||||
|
||||
logger.debug("loaded ip filtering profiles: {}", profileRules.keySet());
|
||||
return ImmutableMap.copyOf(profileRules);
|
||||
}
|
||||
|
||||
private static ShieldIpFilterRule[] parseValue(String[] values, boolean isAllowRule) throws UnknownHostException {
|
||||
ShieldIpFilterRule[] rules = new ShieldIpFilterRule[values.length];
|
||||
private ShieldIpFilterRule[] parseValue(String[] values, boolean isAllowRule, InetAddress localAddress) {
|
||||
List<ShieldIpFilterRule> rules = new ArrayList<>();
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
rules[i] = new ShieldIpFilterRule(isAllowRule, values[i]);
|
||||
|
||||
// never ever deny on localhost, do not even add this rule
|
||||
if (!isAllowRule && isLocalAddress(localAddress, values[i])) {
|
||||
logger.warn("Configuration setting not applied to reject connections on [{}]. local connections are always allowed!", values[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
rules.add(new ShieldIpFilterRule(isAllowRule, values[i]));
|
||||
}
|
||||
return rules.toArray(new ShieldIpFilterRule[]{});
|
||||
}
|
||||
|
||||
private boolean isLocalAddress(InetAddress localAddress, String address) {
|
||||
return address.equals("127.0.0.1") || address.equals("localhost") || address.equals("::1") || address.startsWith("fe80::1") ||
|
||||
address.equals(localAddress.getHostAddress()) || address.equals(localAddress.getHostName());
|
||||
}
|
||||
|
||||
private class ApplySettings implements NodeSettingsService.Listener {
|
||||
|
||||
String[] allowed;
|
||||
String[] denied;
|
||||
String[] httpAllowed;
|
||||
String[] httpDenied;
|
||||
ObjectObjectOpenHashMap<String, String[]> profileAllowed;
|
||||
ObjectObjectOpenHashMap<String, String[]> profileDenied;
|
||||
private boolean enabled;
|
||||
private boolean httpEnabled;
|
||||
|
||||
public ApplySettings(Settings settings) {
|
||||
loadValuesFromSettings(settings);
|
||||
}
|
||||
|
||||
private void loadValuesFromSettings(Settings settings) {
|
||||
this.enabled = settings.getAsBoolean(IP_FILTER_ENABLED_SETTING, this.enabled);
|
||||
this.httpEnabled = settings.getAsBoolean(IP_FILTER_ENABLED_HTTP_SETTING, this.httpEnabled);
|
||||
this.allowed = settings.getAsArray("shield.transport.filter.allow");
|
||||
this.denied = settings.getAsArray("shield.transport.filter.deny");
|
||||
this.httpAllowed = settings.getAsArray("shield.http.filter.allow");
|
||||
this.httpDenied = settings.getAsArray("shield.http.filter.deny");
|
||||
|
||||
if (settings.getGroups("transport.profiles.").size() == 0) {
|
||||
profileAllowed = HppcMaps.newMap(0);
|
||||
profileDenied = HppcMaps.newMap(0);
|
||||
}
|
||||
|
||||
profileAllowed = HppcMaps.newNoNullKeysMap(settings.getGroups("transport.profiles.").size());
|
||||
profileDenied = HppcMaps.newNoNullKeysMap(settings.getGroups("transport.profiles.").size());
|
||||
for (Map.Entry<String, Settings> entry : settings.getGroups("transport.profiles.").entrySet()) {
|
||||
profileAllowed.put(entry.getKey(), entry.getValue().getAsArray("shield.filter.allow"));
|
||||
profileDenied.put(entry.getKey(), entry.getValue().getAsArray("shield.filter.deny"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRefreshSettings(Settings settings) {
|
||||
if (ipFilterSettingsInvolved(settings) && settingsChanged(settings)) {
|
||||
IPFilter.this.rules = parseSettings(settings);
|
||||
loadValuesFromSettings(settings);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean settingsChanged(Settings settings) {
|
||||
// simple checks first
|
||||
if (this.enabled != settings.getAsBoolean(IP_FILTER_ENABLED_SETTING, this.enabled) ||
|
||||
this.httpEnabled != settings.getAsBoolean(IP_FILTER_ENABLED_HTTP_SETTING, this.httpEnabled) ||
|
||||
!Arrays.equals(allowed, settings.getAsArray("shield.transport.filter.allow")) ||
|
||||
!Arrays.equals(denied, settings.getAsArray("shield.transport.filter.deny")) ||
|
||||
!Arrays.equals(httpAllowed, settings.getAsArray("shield.http.filter.allow")) ||
|
||||
!Arrays.equals(httpDenied, settings.getAsArray("shield.http.filter.deny"))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// profile checks now
|
||||
ObjectObjectOpenHashMap<Object, Object> newProfileAllowed = HppcMaps.newNoNullKeysMap(settings.getGroups("transport.profiles.").size());
|
||||
ObjectObjectOpenHashMap<Object, Object> newProfileDenied = HppcMaps.newNoNullKeysMap(settings.getGroups("transport.profiles.").size());
|
||||
for (Map.Entry<String, Settings> entry : settings.getGroups("transport.profiles.").entrySet()) {
|
||||
newProfileAllowed.put(entry.getKey(), entry.getValue().getAsArray("shield.filter.allow"));
|
||||
newProfileDenied.put(entry.getKey(), entry.getValue().getAsArray("shield.filter.deny"));
|
||||
}
|
||||
|
||||
boolean allowedProfileChanged = !newProfileAllowed.equals(profileAllowed);
|
||||
boolean deniedProfileChanged = !newProfileDenied.equals(profileDenied);
|
||||
return allowedProfileChanged || deniedProfileChanged;
|
||||
}
|
||||
|
||||
private boolean ipFilterSettingsInvolved(Settings settings) {
|
||||
boolean containsStaticIpFilterSettings = settings.get("shield.transport.filter.allow") != null ||
|
||||
settings.get("shield.transport.filter.deny") != null ||
|
||||
settings.get("shield.http.filter.allow") != null ||
|
||||
settings.get("shield.http.filter.deny") != null ||
|
||||
settings.get(IP_FILTER_ENABLED_SETTING) != null ||
|
||||
settings.get(IP_FILTER_ENABLED_HTTP_SETTING) != null;
|
||||
|
||||
if (containsStaticIpFilterSettings) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// now if any profile has a filter setting configured
|
||||
for (Map.Entry<String, Settings> entry : settings.getGroups("transport.profiles.").entrySet()) {
|
||||
if (entry.getValue().get("shield.filter.allow") != null || entry.getValue().get("shield.filter.deny") != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,18 @@
|
|||
*/
|
||||
package org.elasticsearch.shield.transport.filter;
|
||||
|
||||
import org.elasticsearch.common.component.Lifecycle;
|
||||
import org.elasticsearch.common.net.InetAddresses;
|
||||
import org.elasticsearch.common.network.NetworkUtils;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.transport.BoundTransportAddress;
|
||||
import org.elasticsearch.common.transport.InetSocketTransportAddress;
|
||||
import org.elasticsearch.http.HttpServerTransport;
|
||||
import org.elasticsearch.node.settings.NodeSettingsService;
|
||||
import org.elasticsearch.shield.audit.AuditTrail;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.test.junit.annotations.Network;
|
||||
import org.elasticsearch.transport.Transport;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
@ -28,10 +35,24 @@ public class IPFilterTests extends ElasticsearchTestCase {
|
|||
|
||||
private IPFilter ipFilter;
|
||||
private AuditTrail auditTrail;
|
||||
private Transport transport;
|
||||
private HttpServerTransport httpTransport;
|
||||
private NodeSettingsService nodeSettingsService;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
auditTrail = mock(AuditTrail.class);
|
||||
nodeSettingsService = mock(NodeSettingsService.class);
|
||||
|
||||
httpTransport = mock(HttpServerTransport.class);
|
||||
InetSocketTransportAddress httpAddress = new InetSocketTransportAddress(NetworkUtils.getLocalAddress(), 9200);
|
||||
when(httpTransport.boundAddress()).thenReturn(new BoundTransportAddress(httpAddress, httpAddress));
|
||||
when(httpTransport.lifecycleState()).thenReturn(Lifecycle.State.STARTED);
|
||||
|
||||
transport = mock(Transport.class);
|
||||
InetSocketTransportAddress address = new InetSocketTransportAddress(NetworkUtils.getLocalAddress(), 9300);
|
||||
when(transport.boundAddress()).thenReturn(new BoundTransportAddress(address, address));
|
||||
when(transport.lifecycleState()).thenReturn(Lifecycle.State.STARTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -40,7 +61,7 @@ public class IPFilterTests extends ElasticsearchTestCase {
|
|||
.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);
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
|
||||
assertAddressIsAllowed("127.0.0.1");
|
||||
assertAddressIsDenied("10.2.3.4");
|
||||
|
@ -54,7 +75,7 @@ public class IPFilterTests extends ElasticsearchTestCase {
|
|||
.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);
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
|
||||
assertAddressIsAllowed("2001:0db8:1234:0000:0000:8a2e:0370:7334");
|
||||
assertAddressIsDenied("1234:0db8:85a3:0000:0000:8a2e:0370:7334");
|
||||
|
@ -68,7 +89,7 @@ public class IPFilterTests extends ElasticsearchTestCase {
|
|||
.put("shield.transport.filter.allow", "127.0.0.1")
|
||||
.put("shield.transport.filter.deny", "*.google.com")
|
||||
.build();
|
||||
ipFilter = new IPFilter(settings, auditTrail);
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
|
||||
assertAddressIsAllowed("127.0.0.1");
|
||||
assertAddressIsDenied("8.8.8.8");
|
||||
|
@ -79,7 +100,7 @@ public class IPFilterTests extends ElasticsearchTestCase {
|
|||
Settings settings = settingsBuilder()
|
||||
.put("shield.transport.filter.allow", "_all")
|
||||
.build();
|
||||
ipFilter = new IPFilter(settings, auditTrail);
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
|
||||
assertAddressIsAllowed("127.0.0.1");
|
||||
assertAddressIsAllowed("173.194.70.100");
|
||||
|
@ -93,7 +114,7 @@ public class IPFilterTests extends ElasticsearchTestCase {
|
|||
.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);
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
|
||||
assertAddressIsAllowed("127.0.0.1");
|
||||
assertAddressIsDenied("192.168.0.1");
|
||||
|
@ -107,7 +128,7 @@ public class IPFilterTests extends ElasticsearchTestCase {
|
|||
.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);
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
|
||||
assertAddressIsAllowed("10.0.0.1");
|
||||
assertAddressIsDenied("10.0.0.2");
|
||||
|
@ -116,7 +137,7 @@ public class IPFilterTests extends ElasticsearchTestCase {
|
|||
@Test
|
||||
public void testDefaultAllow() throws Exception {
|
||||
Settings settings = settingsBuilder().build();
|
||||
ipFilter = new IPFilter(settings, auditTrail);
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
|
||||
assertAddressIsAllowed("10.0.0.1");
|
||||
assertAddressIsAllowed("10.0.0.2");
|
||||
|
@ -128,12 +149,13 @@ public class IPFilterTests extends ElasticsearchTestCase {
|
|||
.put("shield.transport.filter.allow", "127.0.0.1")
|
||||
.put("shield.transport.filter.deny", "10.0.0.0/8")
|
||||
.put("shield.http.filter.allow", "10.0.0.0/8")
|
||||
.put("shield.http.filter.deny", "127.0.0.1")
|
||||
.put("shield.http.filter.deny", "192.168.0.1")
|
||||
.build();
|
||||
ipFilter = new IPFilter(settings, auditTrail);
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
ipFilter.setHttpServerTransport(httpTransport);
|
||||
|
||||
assertAddressIsAllowedForProfile(IPFilter.HTTP_PROFILE_NAME, "10.2.3.4");
|
||||
assertAddressIsDeniedForProfile(IPFilter.HTTP_PROFILE_NAME, "127.0.0.1");
|
||||
assertAddressIsDeniedForProfile(IPFilter.HTTP_PROFILE_NAME, "192.168.0.1");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -142,12 +164,24 @@ public class IPFilterTests extends ElasticsearchTestCase {
|
|||
.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);
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
ipFilter.setHttpServerTransport(httpTransport);
|
||||
|
||||
assertAddressIsAllowedForProfile(IPFilter.HTTP_PROFILE_NAME, "127.0.0.1");
|
||||
assertAddressIsDeniedForProfile(IPFilter.HTTP_PROFILE_NAME, "10.2.3.4");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatLocalhostIsNeverRejected() throws Exception {
|
||||
Settings settings = settingsBuilder()
|
||||
.put("shield.transport.filter.deny", "127.0.0.1")
|
||||
.build();
|
||||
ipFilter = new IPFilter(settings, auditTrail, nodeSettingsService, transport).start();
|
||||
ipFilter.setHttpServerTransport(httpTransport);
|
||||
|
||||
assertAddressIsAllowedForProfile(IPFilter.HTTP_PROFILE_NAME, "127.0.0.1");
|
||||
}
|
||||
|
||||
private void assertAddressIsAllowedForProfile(String profile, String ... inetAddresses) {
|
||||
for (String inetAddress : inetAddresses) {
|
||||
String message = String.format(Locale.ROOT, "Expected address %s to be allowed", inetAddress);
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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.transport.filter;
|
||||
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.node.internal.InternalNode;
|
||||
import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
|
||||
import org.elasticsearch.test.ShieldIntegrationTest;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
|
||||
import static org.elasticsearch.test.ElasticsearchIntegrationTest.Scope.TEST;
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
@ClusterScope(scope = TEST, numDataNodes = 1)
|
||||
public class IpFilteringUpdateTests extends ShieldIntegrationTest {
|
||||
|
||||
private boolean httpEnabled = false;
|
||||
|
||||
@Override
|
||||
protected Settings nodeSettings(int nodeOrdinal) {
|
||||
httpEnabled = randomBoolean();
|
||||
|
||||
return settingsBuilder()
|
||||
.put(super.nodeSettings(nodeOrdinal))
|
||||
.put(InternalNode.HTTP_ENABLED, httpEnabled)
|
||||
.put("shield.transport.filter.deny", "127.0.0.200")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatIpFilterConfigurationCanBeChangedDynamically() throws Exception {
|
||||
// ensure this did not get overwritten by the listener during startup
|
||||
assertConnectionRejected("default", "127.0.0.200");
|
||||
|
||||
// allow all by default
|
||||
assertConnectionAccepted("default", "127.0.0.8");
|
||||
assertConnectionAccepted(".http", "127.0.0.8");
|
||||
assertConnectionAccepted("client", "127.0.0.8");
|
||||
|
||||
Settings settings = settingsBuilder()
|
||||
.put("shield.transport.filter.allow", "127.0.0.1")
|
||||
.put("shield.transport.filter.deny", "127.0.0.8")
|
||||
.build();
|
||||
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings));
|
||||
assertConnectionRejected("default", "127.0.0.8");
|
||||
|
||||
settings = settingsBuilder()
|
||||
.putArray("shield.http.filter.allow", "127.0.0.1")
|
||||
.putArray("shield.http.filter.deny", "127.0.0.8")
|
||||
.build();
|
||||
assertAcked(client().admin().cluster().prepareUpdateSettings().setPersistentSettings(settings));
|
||||
assertConnectionRejected("default", "127.0.0.8");
|
||||
assertConnectionRejected(".http", "127.0.0.8");
|
||||
|
||||
settings = settingsBuilder()
|
||||
.put("transport.profiles.client.shield.filter.allow", "127.0.0.1")
|
||||
.put("transport.profiles.client.shield.filter.deny", "127.0.0.8")
|
||||
.build();
|
||||
assertAcked(client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings));
|
||||
assertConnectionRejected("default", "127.0.0.8");
|
||||
assertConnectionRejected(".http", "127.0.0.8");
|
||||
assertConnectionRejected("client", "127.0.0.8");
|
||||
|
||||
// check that all is in cluster state
|
||||
ClusterState clusterState = client().admin().cluster().prepareState().get().getState();
|
||||
assertThat(clusterState.metaData().settings().get("shield.transport.filter.allow"), is("127.0.0.1"));
|
||||
assertThat(clusterState.metaData().settings().get("shield.transport.filter.deny"), is("127.0.0.8"));
|
||||
assertThat(clusterState.metaData().persistentSettings().get("shield.http.filter.allow.0"), is("127.0.0.1"));
|
||||
assertThat(clusterState.metaData().persistentSettings().get("shield.http.filter.deny.0"), is("127.0.0.8"));
|
||||
assertThat(clusterState.metaData().settings().get("transport.profiles.client.shield.filter.allow"), is("127.0.0.1"));
|
||||
assertThat(clusterState.metaData().settings().get("transport.profiles.client.shield.filter.deny"), is("127.0.0.8"));
|
||||
|
||||
// now disable ip filtering dynamically and make sure nothing is rejected
|
||||
settings = settingsBuilder()
|
||||
.put(IPFilter.IP_FILTER_ENABLED_SETTING, false)
|
||||
.put(IPFilter.IP_FILTER_ENABLED_HTTP_SETTING, true)
|
||||
.build();
|
||||
assertAcked(client().admin().cluster().prepareUpdateSettings().setPersistentSettings(settings));
|
||||
assertConnectionAccepted("default", "127.0.0.8");
|
||||
assertConnectionAccepted("client", "127.0.0.8");
|
||||
|
||||
// now also disable for HTTP
|
||||
assertConnectionRejected(".http", "127.0.0.8");
|
||||
settings = settingsBuilder()
|
||||
.put(IPFilter.IP_FILTER_ENABLED_HTTP_SETTING, false)
|
||||
.build();
|
||||
assertAcked(client().admin().cluster().prepareUpdateSettings().setPersistentSettings(settings));
|
||||
assertConnectionAccepted(".http", "127.0.0.8");
|
||||
}
|
||||
|
||||
private void assertConnectionAccepted(String profile, String host) throws UnknownHostException {
|
||||
// HTTP is not applied if disabled
|
||||
if (!httpEnabled && IPFilter.HTTP_PROFILE_NAME.equals(profile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IPFilter ipFilter = internalCluster().getInstance(IPFilter.class);
|
||||
String message = String.format(Locale.ROOT, "Expected allowed connection for profile %s against host %s", profile, host);
|
||||
assertThat(message, ipFilter.accept(profile, InetAddress.getByName(host)), is(true));
|
||||
}
|
||||
|
||||
private void assertConnectionRejected(String profile, String host) throws UnknownHostException {
|
||||
// HTTP is not applied if disabled
|
||||
if (!httpEnabled && IPFilter.HTTP_PROFILE_NAME.equals(profile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IPFilter ipFilter = internalCluster().getInstance(IPFilter.class);
|
||||
String message = String.format(Locale.ROOT, "Expected rejection for profile %s against host %s", profile, host);
|
||||
assertThat(message, ipFilter.accept(profile, InetAddress.getByName(host)), is(false));
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
+c:255.255.0.0/16
|
||||
-i:192.168.100.2
|
||||
-c:2001:db8::/48
|
||||
-n:hostname
|
|
@ -6,11 +6,18 @@
|
|||
package org.elasticsearch.shield.transport.netty;
|
||||
|
||||
import com.google.common.net.InetAddresses;
|
||||
import org.elasticsearch.common.component.Lifecycle;
|
||||
import org.elasticsearch.common.netty.channel.*;
|
||||
import org.elasticsearch.common.network.NetworkUtils;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.transport.BoundTransportAddress;
|
||||
import org.elasticsearch.common.transport.InetSocketTransportAddress;
|
||||
import org.elasticsearch.http.HttpServerTransport;
|
||||
import org.elasticsearch.node.settings.NodeSettingsService;
|
||||
import org.elasticsearch.shield.audit.AuditTrail;
|
||||
import org.elasticsearch.shield.transport.filter.IPFilter;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.transport.Transport;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -19,6 +26,8 @@ import java.net.SocketAddress;
|
|||
|
||||
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -34,9 +43,29 @@ public class IPFilterNettyUpstreamHandlerTests extends ElasticsearchTestCase {
|
|||
.put("shield.transport.filter.deny", "10.0.0.0/8")
|
||||
.build();
|
||||
|
||||
IPFilter ipFilter = new IPFilter(settings, AuditTrail.NOOP);
|
||||
boolean isHttpEnabled = randomBoolean();
|
||||
|
||||
nettyUpstreamHandler = new IPFilterNettyUpstreamHandler(ipFilter, IPFilter.HTTP_PROFILE_NAME);
|
||||
Transport transport = mock(Transport.class);
|
||||
InetSocketTransportAddress address = new InetSocketTransportAddress(NetworkUtils.getLocalAddress(), 9300);
|
||||
when(transport.boundAddress()).thenReturn(new BoundTransportAddress(address, address));
|
||||
when(transport.lifecycleState()).thenReturn(Lifecycle.State.STARTED);
|
||||
|
||||
NodeSettingsService nodeSettingsService = mock(NodeSettingsService.class);
|
||||
IPFilter ipFilter = new IPFilter(settings, AuditTrail.NOOP, nodeSettingsService, transport).start();
|
||||
|
||||
if (isHttpEnabled) {
|
||||
HttpServerTransport httpTransport = mock(HttpServerTransport.class);
|
||||
InetSocketTransportAddress httpAddress = new InetSocketTransportAddress(NetworkUtils.getLocalAddress(), 9200);
|
||||
when(httpTransport.boundAddress()).thenReturn(new BoundTransportAddress(httpAddress, httpAddress));
|
||||
when(httpTransport.lifecycleState()).thenReturn(Lifecycle.State.STARTED);
|
||||
ipFilter.setHttpServerTransport(httpTransport);
|
||||
}
|
||||
|
||||
if (isHttpEnabled) {
|
||||
nettyUpstreamHandler = new IPFilterNettyUpstreamHandler(ipFilter, IPFilter.HTTP_PROFILE_NAME);
|
||||
} else {
|
||||
nettyUpstreamHandler = new IPFilterNettyUpstreamHandler(ipFilter, "default");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue