From a931e4e0b808190b143e6a3523ccc79be8a83e19 Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Wed, 16 Dec 2015 12:09:15 +0100 Subject: [PATCH] Marvel: Add tribe nodes support This commit enable tribe nodes support for Marvel. It avoid ElasticsearchSecurityException when a tribe node is connected to a cluster that has been configured for both Shield and Marvel by loading the MarvelShieldIntegration support on tribe node even if marvel.enabled is set to false. It also allows tribe nodes to be monitored using Marvel with their own marvel settings. closes elastic/elasticsearch#1088 Original commit: elastic/x-pack-elasticsearch@e0401c12885821c23656a1a3b4f627add2451a23 --- .../elasticsearch/marvel/MarvelPlugin.java | 57 +++++++++++++------ .../marvel/shield/MarvelShieldModule.java | 13 +++-- .../marvel/MarvelPluginTests.java | 11 ++++ 3 files changed, 59 insertions(+), 22 deletions(-) diff --git a/elasticsearch/x-pack/marvel/src/main/java/org/elasticsearch/marvel/MarvelPlugin.java b/elasticsearch/x-pack/marvel/src/main/java/org/elasticsearch/marvel/MarvelPlugin.java index 9969777d11f..473bb49005b 100644 --- a/elasticsearch/x-pack/marvel/src/main/java/org/elasticsearch/marvel/MarvelPlugin.java +++ b/elasticsearch/x-pack/marvel/src/main/java/org/elasticsearch/marvel/MarvelPlugin.java @@ -29,9 +29,11 @@ import org.elasticsearch.shield.authz.AuthorizationModule; import org.elasticsearch.tribe.TribeService; import org.elasticsearch.xpack.XPackPlugin; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.List; public class MarvelPlugin extends Plugin { @@ -58,22 +60,29 @@ public class MarvelPlugin extends Plugin { return "Elasticsearch Marvel"; } - public boolean isEnabled() { + boolean isEnabled() { return enabled; } @Override public Collection nodeModules() { - if (!enabled) { - return Collections.emptyList(); + List modules = new ArrayList<>(); + + // Always load the security integration for tribe nodes. + // This is useful if the tribe node is connected to a + // protected monitored cluster: __marvel_user operations must be allowed. + if (enabled || isTribeNode(settings) || isTribeClientNode(settings)) { + modules.add(new MarvelShieldModule(settings)); } - return Arrays.asList( - new MarvelModule(), - new LicenseModule(), - new CollectorModule(), - new ExporterModule(settings), - new MarvelShieldModule(settings), - new RendererModule()); + + if (enabled) { + modules.add(new MarvelModule()); + modules.add(new LicenseModule()); + modules.add(new CollectorModule()); + modules.add(new ExporterModule(settings)); + modules.add(new RendererModule()); + } + return Collections.unmodifiableList(modules); } @Override @@ -85,17 +94,29 @@ public class MarvelPlugin extends Plugin { } public static boolean marvelEnabled(Settings settings) { - String tribe = settings.get(TribeService.TRIBE_NAME); - if (tribe != null) { - logger.trace("marvel cannot be started on tribe node [{}]", tribe); - return false; - } - if (!"node".equals(settings.get(Client.CLIENT_TYPE_SETTING))) { logger.trace("marvel cannot be started on a transport client"); return false; } - return settings.getAsBoolean(ENABLED, true); + // By default, marvel is disabled on tribe nodes + return settings.getAsBoolean(ENABLED, !isTribeNode(settings) && !isTribeClientNode(settings)); + } + + static boolean isTribeNode(Settings settings) { + if (settings.getGroups("tribe", true).isEmpty() == false) { + logger.trace("detecting tribe node"); + return true; + } + return false; + } + + static boolean isTribeClientNode(Settings settings) { + String tribeName = settings.get(TribeService.TRIBE_NAME); + if (tribeName != null) { + logger.trace("detecting tribe client node [{}]", tribeName); + return true; + } + return false; } // NOTE: The fact this signature takes a module is a hack, and effectively like the previous @@ -103,7 +124,7 @@ public class MarvelPlugin extends Plugin { // We need to avoid trying to load the AuthorizationModule class unless we know shield integration // is enabled. This is a temporary solution until inter-plugin-communication can be worked out. public void onModule(Module module) { - if (enabled && MarvelShieldIntegration.enabled(settings) && module instanceof AuthorizationModule) { + if (MarvelShieldIntegration.enabled(settings) && module instanceof AuthorizationModule) { ((AuthorizationModule)module).registerReservedRole(InternalMarvelUser.ROLE); } } diff --git a/elasticsearch/x-pack/marvel/src/main/java/org/elasticsearch/marvel/shield/MarvelShieldModule.java b/elasticsearch/x-pack/marvel/src/main/java/org/elasticsearch/marvel/shield/MarvelShieldModule.java index 3d043e3f8b1..e4794ee1c23 100644 --- a/elasticsearch/x-pack/marvel/src/main/java/org/elasticsearch/marvel/shield/MarvelShieldModule.java +++ b/elasticsearch/x-pack/marvel/src/main/java/org/elasticsearch/marvel/shield/MarvelShieldModule.java @@ -7,23 +7,28 @@ package org.elasticsearch.marvel.shield; import org.elasticsearch.common.inject.AbstractModule; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.marvel.MarvelPlugin; /** * */ public class MarvelShieldModule extends AbstractModule { - private final boolean enabled; + private final boolean shieldEnabled; + private final boolean marvelEnabled; public MarvelShieldModule(Settings settings) { - this.enabled = MarvelShieldIntegration.enabled(settings); + this.shieldEnabled = MarvelShieldIntegration.enabled(settings); + this.marvelEnabled = MarvelPlugin.marvelEnabled(settings);; } @Override protected void configure() { bind(MarvelShieldIntegration.class).asEagerSingleton(); - bind(SecuredClient.class).asEagerSingleton(); - if (enabled) { + if (marvelEnabled) { + bind(SecuredClient.class).asEagerSingleton(); + } + if (shieldEnabled) { bind(MarvelSettingsFilter.Shield.class).asEagerSingleton(); bind(MarvelSettingsFilter.class).to(MarvelSettingsFilter.Shield.class); } else { diff --git a/elasticsearch/x-pack/marvel/src/test/java/org/elasticsearch/marvel/MarvelPluginTests.java b/elasticsearch/x-pack/marvel/src/test/java/org/elasticsearch/marvel/MarvelPluginTests.java index 9de1859b940..89292290d67 100644 --- a/elasticsearch/x-pack/marvel/src/test/java/org/elasticsearch/marvel/MarvelPluginTests.java +++ b/elasticsearch/x-pack/marvel/src/test/java/org/elasticsearch/marvel/MarvelPluginTests.java @@ -9,6 +9,8 @@ import org.elasticsearch.action.admin.cluster.node.info.NodeInfo; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.marvel.agent.AgentService; +import org.elasticsearch.marvel.agent.settings.MarvelSettings; +import org.elasticsearch.marvel.shield.MarvelShieldIntegration; import org.elasticsearch.marvel.test.MarvelIntegTestCase; import org.elasticsearch.plugins.PluginInfo; import org.elasticsearch.test.ESIntegTestCase.ClusterScope; @@ -24,6 +26,7 @@ public class MarvelPluginTests extends MarvelIntegTestCase { protected Settings nodeSettings(int nodeOrdinal) { return Settings.builder() .put(super.nodeSettings(nodeOrdinal)) + .put(MarvelSettings.INTERVAL_SETTING.getKey(), "-1") .build(); } @@ -39,10 +42,18 @@ public class MarvelPluginTests extends MarvelIntegTestCase { assertServiceIsNotBound(AgentService.class); } + public void testMarvelEnabledOnTribeNode() { + internalCluster().startNode(Settings.builder().put(MarvelPlugin.ENABLED, true).put(TribeService.TRIBE_NAME, "t1").build()); + assertPluginIsLoaded(); + assertServiceIsBound(AgentService.class); + assertServiceIsBound(MarvelShieldIntegration.class); + } + public void testMarvelDisabledOnTribeNode() { internalCluster().startNode(Settings.builder().put(TribeService.TRIBE_NAME, "t1").build()); assertPluginIsLoaded(); assertServiceIsNotBound(AgentService.class); + assertServiceIsBound(MarvelShieldIntegration.class); } private void assertPluginIsLoaded() {