diff --git a/core/pom.xml b/core/pom.xml
index 58118a2d032..16f3c7fec26 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -251,6 +251,7 @@
org/elasticsearch/test/**/*
org/elasticsearch/bootstrap/BootstrapForTesting.class
+ org/elasticsearch/bootstrap/MockPluginPolicy.class
org/elasticsearch/common/cli/CliToolTestCase.class
org/elasticsearch/common/cli/CliToolTestCase$*.class
@@ -279,7 +280,7 @@
rest-api-spec/**/*
org/elasticsearch/test/**/*
org/elasticsearch/bootstrap/BootstrapForTesting.class
- org/elasticsearch/bootstrap/XTestSecurityManager*.class
+ org/elasticsearch/bootstrap/MockPluginPolicy.class
org/elasticsearch/common/cli/CliToolTestCase.class
org/elasticsearch/common/cli/CliToolTestCase$*.class
org/elasticsearch/cluster/MockInternalClusterInfoService.class
diff --git a/core/src/test/java/org/elasticsearch/bootstrap/BootstrapForTesting.java b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapForTesting.java
index 191f9324eb2..41fad1607e3 100644
--- a/core/src/test/java/org/elasticsearch/bootstrap/BootstrapForTesting.java
+++ b/core/src/test/java/org/elasticsearch/bootstrap/BootstrapForTesting.java
@@ -30,13 +30,8 @@ import org.elasticsearch.common.logging.Loggers;
import java.io.FilePermission;
import java.net.URL;
import java.nio.file.Path;
-import java.security.CodeSource;
-import java.security.Permission;
-import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.Policy;
-import java.security.cert.Certificate;
-import java.util.Collections;
import java.util.Objects;
import static com.carrotsearch.randomizedtesting.RandomizedTest.systemPropertyAsBoolean;
@@ -119,14 +114,17 @@ public class BootstrapForTesting {
perms.add(new FilePermission(coverageDir.resolve("jacoco-it.exec").toString(), "read,write"));
}
- // if its an insecure plugin, its not easy to simulate here, since we don't have a real plugin install.
- // we just do our best so unit testing can work. integration tests for such plugins are essential.
+ final Policy policy;
+ // if its an insecure plugin, we use a wrapper policy impl to try
+ // to simulate what happens with a real distribution
String artifact = System.getProperty("tests.artifact");
String insecurePluginProp = Security.INSECURE_PLUGINS.get(artifact);
if (insecurePluginProp != null) {
- setInsecurePluginPermissions(perms, insecurePluginProp);
+ policy = new MockPluginPolicy(perms, insecurePluginProp);
+ } else {
+ policy = new ESPolicy(perms);
}
- Policy.setPolicy(new ESPolicy(perms));
+ Policy.setPolicy(policy);
System.setSecurityManager(new TestSecurityManager());
Security.selfTest();
@@ -144,44 +142,6 @@ public class BootstrapForTesting {
}
}
- /**
- * with a real plugin install, we just set a property to plugin/foo*, which matches
- * plugin code and all dependencies. when running unit tests, things are disorganized,
- * and might even be on different filesystem roots (windows), so we can't even make
- * a URL that will match everything. instead, add the extra permissions globally.
- */
- // TODO: maybe wrap with a policy so the extra permissions aren't applied to test classes/framework,
- // so that stacks are always polluted and tests fail for missing AccessController blocks...
- static void setInsecurePluginPermissions(Permissions permissions, String insecurePluginProp) throws Exception {
- // the hack begins!
-
- // parse whole policy file, with and without the substitution, compute the delta, then add globally.
- URL bogus = new URL("file:/bogus");
- ESPolicy policy = new ESPolicy(new Permissions());
- PermissionCollection small = policy.template.getPermissions(new CodeSource(bogus, (Certificate[])null));
- System.setProperty(insecurePluginProp, bogus.toString());
- policy = new ESPolicy(new Permissions());
- System.clearProperty(insecurePluginProp);
- PermissionCollection big = policy.template.getPermissions(new CodeSource(bogus, (Certificate[])null));
-
- PermissionCollection delta = delta(small, big);
- for (Permission p : Collections.list(delta.elements())) {
- permissions.add(p);
- }
- }
-
- // computes delta of small and big, the slow way
- static PermissionCollection delta(PermissionCollection small, PermissionCollection big) {
- Permissions extra = new Permissions();
- for (Permission p : Collections.list(big.elements())) {
- // check big too, to remove UnresolvedPermissions (acts like NaN)
- if (big.implies(p) && small.implies(p) == false) {
- extra.add(p);
- }
- }
- return extra;
- }
-
// does nothing, just easy way to make sure the class is loaded.
public static void ensureInitialized() {}
}
diff --git a/core/src/test/java/org/elasticsearch/bootstrap/MockPluginPolicy.java b/core/src/test/java/org/elasticsearch/bootstrap/MockPluginPolicy.java
new file mode 100644
index 00000000000..104aad59291
--- /dev/null
+++ b/core/src/test/java/org/elasticsearch/bootstrap/MockPluginPolicy.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.bootstrap;
+
+import com.carrotsearch.randomizedtesting.RandomizedRunner;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.elasticsearch.common.io.PathUtils;
+import org.elasticsearch.common.logging.Loggers;
+import org.junit.Assert;
+
+import java.net.URL;
+import java.nio.file.Path;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.security.cert.Certificate;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Simulates in unit tests per-plugin permissions.
+ * Unit tests for plugins do not have a proper plugin structure,
+ * so we don't know which codebases to apply the permission to.
+ *
+ * As an approximation, we just exclude es/test/framework classes,
+ * because they will be present in stacks and fail tests for the
+ * simple case where an AccessController block is missing, because
+ * java security checks every codebase in the stacktrace, and we
+ * are sure to pollute it.
+ */
+final class MockPluginPolicy extends Policy {
+ final ESPolicy standardPolicy;
+ final PermissionCollection extraPermissions;
+ final Set extraSources;
+
+ /**
+ * Create a new MockPluginPolicy with dynamic {@code permissions} and
+ * adding the extra plugin permissions from {@code insecurePluginProp} to
+ * all code except test classes.
+ */
+ MockPluginPolicy(Permissions permissions, String insecurePluginProp) throws Exception {
+ // the hack begins!
+
+ // parse whole policy file, with and without the substitution, compute the delta
+ standardPolicy = new ESPolicy(permissions);
+
+ URL bogus = new URL("file:/bogus"); // its "any old codebase" this time: generic permissions
+ PermissionCollection smallPermissions = standardPolicy.template.getPermissions(new CodeSource(bogus, (Certificate[])null));
+ Set small = new HashSet<>(Collections.list(smallPermissions.elements()));
+
+ // set the URL for the property substitution, this time it will also have special permissions
+ System.setProperty(insecurePluginProp, bogus.toString());
+ ESPolicy biggerPolicy = new ESPolicy(permissions);
+ System.clearProperty(insecurePluginProp);
+ PermissionCollection bigPermissions = biggerPolicy.template.getPermissions(new CodeSource(bogus, (Certificate[])null));
+ Set big = new HashSet<>(Collections.list(bigPermissions.elements()));
+
+ // compute delta to remove all the generic permissions
+ // we want equals() vs implies() for this check, in case we need
+ // to pass along any UnresolvedPermission to the plugin
+ big.removeAll(small);
+
+ // build collection of the special permissions for easy checking
+ extraPermissions = new Permissions();
+ for (Permission p : big) {
+ extraPermissions.add(p);
+ }
+
+ // every element in classpath except test-classes/
+ extraSources = new HashSet();
+ for (URL location : JarHell.parseClassPath()) {
+ Path path = PathUtils.get(location.toURI());
+ String baseName = path.getFileName().toString();
+ if (baseName.contains("test-classes") == false) {
+ extraSources.add(new CodeSource(location, (Certificate[])null));
+ }
+ }
+ // exclude some obvious places
+ // es core
+ extraSources.remove(Bootstrap.class.getProtectionDomain().getCodeSource());
+ // es test framework
+ extraSources.remove(getClass().getProtectionDomain().getCodeSource());
+ // lucene test framework
+ extraSources.remove(LuceneTestCase.class.getProtectionDomain().getCodeSource());
+ // test runner
+ extraSources.remove(RandomizedRunner.class.getProtectionDomain().getCodeSource());
+ // junit library
+ extraSources.remove(Assert.class.getProtectionDomain().getCodeSource());
+
+ Loggers.getLogger(getClass()).debug("Apply permissions [{}] to codebases [{}]", extraPermissions, extraSources);
+ }
+
+ @Override
+ public boolean implies(ProtectionDomain domain, Permission permission) {
+ if (standardPolicy.implies(domain, permission)) {
+ return true;
+ } else if (extraSources.contains(domain.getCodeSource())) {
+ return extraPermissions.implies(permission);
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/plugins/cloud-gce/src/main/java/org/elasticsearch/cloud/gce/GceComputeServiceImpl.java b/plugins/cloud-gce/src/main/java/org/elasticsearch/cloud/gce/GceComputeServiceImpl.java
index f89f6bc19f2..1a8a7dd7895 100644
--- a/plugins/cloud-gce/src/main/java/org/elasticsearch/cloud/gce/GceComputeServiceImpl.java
+++ b/plugins/cloud-gce/src/main/java/org/elasticsearch/cloud/gce/GceComputeServiceImpl.java
@@ -37,6 +37,7 @@ import org.elasticsearch.common.unit.TimeValue;
import java.io.IOException;
import java.security.AccessController;
import java.security.GeneralSecurityException;
+import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.*;
@@ -59,13 +60,20 @@ public class GceComputeServiceImpl extends AbstractLifecycleComponent instances = zones.stream().map((zoneId) -> {
try {
- Compute.Instances.List list = client().instances().list(project, zoneId);
- InstanceList instanceList = list.execute();
+ // hack around code messiness in GCE code
+ // TODO: get this fixed
+ InstanceList instanceList = AccessController.doPrivileged(new PrivilegedExceptionAction() {
+ @Override
+ public InstanceList run() throws Exception {
+ Compute.Instances.List list = client().instances().list(project, zoneId);
+ return list.execute();
+ }
+ });
if (instanceList.isEmpty()) {
return Collections.EMPTY_LIST;
}
return instanceList.getItems();
- } catch (IOException e) {
+ } catch (PrivilegedActionException e) {
logger.warn("Problem fetching instance list for zone {}", zoneId);
logger.debug("Full exception:", e);
return Collections.EMPTY_LIST;
diff --git a/plugins/cloud-gce/src/main/java/org/elasticsearch/discovery/gce/GceUnicastHostsProvider.java b/plugins/cloud-gce/src/main/java/org/elasticsearch/discovery/gce/GceUnicastHostsProvider.java
index 5415f583607..79cd11666ad 100644
--- a/plugins/cloud-gce/src/main/java/org/elasticsearch/discovery/gce/GceUnicastHostsProvider.java
+++ b/plugins/cloud-gce/src/main/java/org/elasticsearch/discovery/gce/GceUnicastHostsProvider.java
@@ -242,8 +242,7 @@ public class GceUnicastHostsProvider extends AbstractComponent implements Unicas
}
} catch (Throwable e) {
- logger.warn("Exception caught during discovery {} : {}", e.getClass().getName(), e.getMessage());
- logger.trace("Exception caught during discovery", e);
+ logger.warn("Exception caught during discovery: {}", e, e.getMessage());
}
logger.debug("{} node(s) added", cachedDiscoNodes.size());