Ip filtering: Check correct path for default file

Also added logging, so that on start up a message is logged, if all
connections are rejected or the config file is not found.

Closes elastic/elasticsearch#48

Original commit: elastic/x-pack-elasticsearch@51f16d75ba
This commit is contained in:
Alexander Reelsen 2014-08-29 08:59:05 +02:00
parent c17c140cd2
commit 25d2480e78
3 changed files with 40 additions and 28 deletions

View File

@ -5,13 +5,13 @@
*/ */
package org.elasticsearch.shield.n2n; package org.elasticsearch.shield.n2n;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings; import org.elasticsearch.common.Strings;
import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.jackson.dataformat.yaml.snakeyaml.error.YAMLException; import org.elasticsearch.common.jackson.dataformat.yaml.snakeyaml.error.YAMLException;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.net.InetAddresses; import org.elasticsearch.common.net.InetAddresses;
import org.elasticsearch.common.netty.handler.ipfilter.IpFilterRule; import org.elasticsearch.common.netty.handler.ipfilter.IpFilterRule;
import org.elasticsearch.common.netty.handler.ipfilter.IpSubnetFilterRule; import org.elasticsearch.common.netty.handler.ipfilter.IpSubnetFilterRule;
@ -20,6 +20,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.yaml.YamlXContent; import org.elasticsearch.common.xcontent.yaml.YamlXContent;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.plugin.SecurityPlugin;
import org.elasticsearch.watcher.FileChangesListener; import org.elasticsearch.watcher.FileChangesListener;
import org.elasticsearch.watcher.FileWatcher; import org.elasticsearch.watcher.FileWatcher;
import org.elasticsearch.watcher.ResourceWatcherService; import org.elasticsearch.watcher.ResourceWatcherService;
@ -51,7 +52,7 @@ public class IPFilteringN2NAuthenticator extends AbstractComponent implements N2
public IPFilteringN2NAuthenticator(Settings settings, Environment env, ResourceWatcherService watcherService) { public IPFilteringN2NAuthenticator(Settings settings, Environment env, ResourceWatcherService watcherService) {
super(settings); super(settings);
file = resolveFile(componentSettings, env); file = resolveFile(componentSettings, env);
rules = parseFile(file); rules = parseFile(file, logger);
watcher = new FileWatcher(file.getParent().toFile()); watcher = new FileWatcher(file.getParent().toFile());
watcher.addListener(new FileListener()); watcher.addListener(new FileListener());
watcherService.add(watcher); watcherService.add(watcher);
@ -60,13 +61,15 @@ public class IPFilteringN2NAuthenticator extends AbstractComponent implements N2
private Path resolveFile(Settings settings, Environment env) { private Path resolveFile(Settings settings, Environment env) {
String location = settings.get("file"); String location = settings.get("file");
if (location == null) { if (location == null) {
return env.configFile().toPath().resolve(DEFAULT_FILE); File shieldDirectory = new File(env.configFile(), SecurityPlugin.NAME);
return shieldDirectory.toPath().resolve(DEFAULT_FILE);
} }
return Paths.get(location); return Paths.get(location);
} }
public static IpFilterRule[] parseFile(Path path) { public static IpFilterRule[] parseFile(Path path, ESLogger logger) {
if (!Files.exists(path)) { if (!Files.exists(path)) {
logger.info("No IP filtering rules loaded, as file {} does not exist. Rejecting all incoming connections!", path);
return NO_RULES; return NO_RULES;
} }
@ -109,8 +112,11 @@ public class IPFilteringN2NAuthenticator extends AbstractComponent implements N2
} }
if (rules.size() == 0) { if (rules.size() == 0) {
logger.info("No IP filtering rules loaded. Rejecting all incoming connections!");
return NO_RULES; return NO_RULES;
} }
logger.debug("Loaded {} ip filtering rules", rules.size());
return rules.toArray(new IpFilterRule[rules.size()]); return rules.toArray(new IpFilterRule[rules.size()]);
} }
@ -130,9 +136,13 @@ public class IPFilteringN2NAuthenticator extends AbstractComponent implements N2
public boolean authenticate(@Nullable Principal peerPrincipal, InetAddress peerAddress, int peerPort) { public boolean authenticate(@Nullable Principal peerPrincipal, InetAddress peerAddress, int peerPort) {
for (int i = 0; i < rules.length; i++) { for (int i = 0; i < rules.length; i++) {
if (rules[i].contains(peerAddress)) { if (rules[i].contains(peerAddress)) {
return rules[i].isAllowRule(); boolean isAllowed = rules[i].isAllowRule();
logger.trace("Authentication rule matched for host [{}]: {}", peerAddress, isAllowed);
return isAllowed;
} }
} }
logger.trace("Rejecting host {}", peerAddress);
return false; return false;
} }
@ -140,7 +150,7 @@ public class IPFilteringN2NAuthenticator extends AbstractComponent implements N2
@Override @Override
public void onFileCreated(File file) { public void onFileCreated(File file) {
if (file.equals(IPFilteringN2NAuthenticator.this.file.toFile())) { if (file.equals(IPFilteringN2NAuthenticator.this.file.toFile())) {
rules = parseFile(file.toPath()); rules = parseFile(file.toPath(), logger);
} }
} }
@ -154,9 +164,7 @@ public class IPFilteringN2NAuthenticator extends AbstractComponent implements N2
@Override @Override
public void onFileChanged(File file) { public void onFileChanged(File file) {
if (file.equals(IPFilteringN2NAuthenticator.this.file.toFile())) { if (file.equals(IPFilteringN2NAuthenticator.this.file.toFile())) {
if (file.equals(IPFilteringN2NAuthenticator.this.file.toFile())) { rules = parseFile(file.toPath(), logger);
rules = parseFile(file.toPath());
}
} }
} }
} }

View File

@ -44,6 +44,8 @@ public class IPFilteringN2NAuthenticatorTests extends ElasticsearchTestCase {
@Rule @Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder(); public TemporaryFolder temporaryFolder = new TemporaryFolder();
private final Settings resourceWatcherServiceSettings = settingsBuilder().put("watcher.interval.medium", TimeValue.timeValueMillis(200)).build();
private ResourceWatcherService resourceWatcherService; private ResourceWatcherService resourceWatcherService;
private File configFile; private File configFile;
private Settings settings; private Settings settings;
@ -137,12 +139,11 @@ public class IPFilteringN2NAuthenticatorTests extends ElasticsearchTestCase {
@Test(expected = ElasticsearchParseException.class) @Test(expected = ElasticsearchParseException.class)
public void testThatInvalidFileThrowsCorrectException() throws Exception { public void testThatInvalidFileThrowsCorrectException() throws Exception {
writeConfigFile("deny: all allow: all \n\n"); writeConfigFile("deny: all allow: all \n\n");
IPFilteringN2NAuthenticator.parseFile(configFile.toPath()); IPFilteringN2NAuthenticator.parseFile(configFile.toPath(), logger);
} }
private void writeConfigFile(String data) throws IOException { private void writeConfigFile(String data) throws IOException {
Files.write(data.getBytes(Charsets.UTF_8), configFile); Files.write(data.getBytes(Charsets.UTF_8), configFile);
Settings resourceWatcherServiceSettings = settingsBuilder().put("watcher.interval.medium", TimeValue.timeValueMillis(200)).build();
resourceWatcherService = new ResourceWatcherService(resourceWatcherServiceSettings, new ThreadPool("resourceWatcher")).start(); resourceWatcherService = new ResourceWatcherService(resourceWatcherServiceSettings, new ThreadPool("resourceWatcher")).start();
settings = settingsBuilder().put("shield.n2n.file", configFile.getPath()).build(); settings = settingsBuilder().put("shield.n2n.file", configFile.getPath()).build();
ipFilteringN2NAuthenticator = new IPFilteringN2NAuthenticator(settings, new Environment(), resourceWatcherService); ipFilteringN2NAuthenticator = new IPFilteringN2NAuthenticator(settings, new Environment(), resourceWatcherService);

View File

@ -6,7 +6,7 @@
package org.elasticsearch.shield.n2n; package org.elasticsearch.shield.n2n;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.net.InetAddresses; import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.transport.TransportAddress;
@ -18,11 +18,9 @@ import org.junit.Test;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.HttpURLConnection; import java.net.InetSocketAddress;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
import java.net.URL;
import java.util.Locale;
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder; import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope; import static org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
@ -38,12 +36,16 @@ public class IpFilteringIntegrationTests extends ShieldIntegrationTest {
@Override @Override
protected Settings nodeSettings(int nodeOrdinal) { protected Settings nodeSettings(int nodeOrdinal) {
File folder = newFolder(); ImmutableSettings.Builder builder = settingsBuilder().put(super.nodeSettings(nodeOrdinal));
// either deny all or do not have a configuration file, as this denies by default
if (getRandom().nextBoolean()) {
File folder = newFolder();
builder.put("shield.n2n.file", writeFile(folder, "ip_filter.yml", CONFIG_IPFILTER_DENY_ALL));
} else {
builder.remove("shield.n2n.file");
}
return settingsBuilder() return builder.build();
.put(super.nodeSettings(nodeOrdinal))
.put("shield.n2n.file", writeFile(folder, "ip_filter.yml", CONFIG_IPFILTER_DENY_ALL))
.build();
} }
@Test(expected = SocketException.class) @Test(expected = SocketException.class)
@ -51,21 +53,22 @@ public class IpFilteringIntegrationTests extends ShieldIntegrationTest {
TransportAddress transportAddress = internalCluster().getDataNodeInstance(HttpServerTransport.class).boundAddress().boundAddress(); TransportAddress transportAddress = internalCluster().getDataNodeInstance(HttpServerTransport.class).boundAddress().boundAddress();
assertThat(transportAddress, is(instanceOf(InetSocketTransportAddress.class))); assertThat(transportAddress, is(instanceOf(InetSocketTransportAddress.class)));
InetSocketTransportAddress inetSocketTransportAddress = (InetSocketTransportAddress) transportAddress; InetSocketTransportAddress inetSocketTransportAddress = (InetSocketTransportAddress) transportAddress;
String url = String.format(Locale.ROOT, "http://%s:%s/", InetAddresses.toUriString(inetSocketTransportAddress.address().getAddress()), inetSocketTransportAddress.address().getPort());
logger.info("Opening connection to {}", url); trySocketConnection(inetSocketTransportAddress.address());
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.connect();
logger.info("HTTP connection response code [{}]", connection.getResponseCode());
} }
@Test(expected = SocketException.class) @Test(expected = SocketException.class)
public void testThatIpFilteringIsIntegratedIntoNettyPipelineViaTransportClient() throws Exception { public void testThatIpFilteringIsIntegratedIntoNettyPipelineViaTransportClient() throws Exception {
InetSocketTransportAddress transportAddress = (InetSocketTransportAddress) internalCluster().getDataNodeInstance(Transport.class).boundAddress().boundAddress(); TransportAddress transportAddress = (InetSocketTransportAddress) internalCluster().getDataNodeInstance(Transport.class).boundAddress().boundAddress();
assertThat(transportAddress, is(instanceOf(InetSocketTransportAddress.class)));
InetSocketTransportAddress inetSocketTransportAddress = (InetSocketTransportAddress) transportAddress;
trySocketConnection(inetSocketTransportAddress.address());
}
private void trySocketConnection(InetSocketAddress address) throws Exception {
try (Socket socket = new Socket()) { try (Socket socket = new Socket()) {
logger.info("Connecting to {}", transportAddress.address()); logger.info("Connecting to {}", address);
socket.connect(transportAddress.address(), 500); socket.connect(address, 500);
assertThat(socket.isConnected(), is(true)); assertThat(socket.isConnected(), is(true));
try (OutputStream os = socket.getOutputStream()) { try (OutputStream os = socket.getOutputStream()) {