Cleanup plugin security
* plugin authors can use full policy syntax, including codebase substitution properties like core syntax. * simplify test logic. * move out test-framework permissions to separate file. Closes #14311
This commit is contained in:
parent
ad1e3ab925
commit
eec3c2a97c
|
@ -236,7 +236,7 @@
|
|||
<includes>
|
||||
<include>org/elasticsearch/test/**/*</include>
|
||||
<include>org/elasticsearch/bootstrap/BootstrapForTesting.class</include>
|
||||
<include>org/elasticsearch/bootstrap/MockPluginPolicy.class</include>
|
||||
<include>org/elasticsearch/bootstrap/BootstrapForTesting$*.class</include>
|
||||
<include>org/elasticsearch/common/cli/CliToolTestCase.class</include>
|
||||
<include>org/elasticsearch/common/cli/CliToolTestCase$*.class</include>
|
||||
</includes>
|
||||
|
@ -265,7 +265,7 @@
|
|||
<include>rest-api-spec/**/*</include>
|
||||
<include>org/elasticsearch/test/**/*</include>
|
||||
<include>org/elasticsearch/bootstrap/BootstrapForTesting.class</include>
|
||||
<include>org/elasticsearch/bootstrap/MockPluginPolicy.class</include>
|
||||
<include>org/elasticsearch/bootstrap/BootstrapForTesting$*.class</include>
|
||||
<include>org/elasticsearch/common/cli/CliToolTestCase.class</include>
|
||||
<include>org/elasticsearch/common/cli/CliToolTestCase$*.class</include>
|
||||
<include>org/elasticsearch/cluster/MockInternalClusterInfoService.class</include>
|
||||
|
|
|
@ -21,14 +21,12 @@ package org.elasticsearch.bootstrap;
|
|||
|
||||
import org.elasticsearch.common.SuppressForbidden;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.security.CodeSource;
|
||||
import java.security.Permission;
|
||||
import java.security.PermissionCollection;
|
||||
import java.security.Policy;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.security.URIParameter;
|
||||
import java.util.Map;
|
||||
|
||||
/** custom policy for union of static and dynamic permissions */
|
||||
|
@ -42,13 +40,11 @@ final class ESPolicy extends Policy {
|
|||
final Policy template;
|
||||
final Policy untrusted;
|
||||
final PermissionCollection dynamic;
|
||||
final Map<String,PermissionCollection> plugins;
|
||||
final Map<String,Policy> plugins;
|
||||
|
||||
public ESPolicy(PermissionCollection dynamic, Map<String,PermissionCollection> plugins) throws Exception {
|
||||
URI policyUri = getClass().getResource(POLICY_RESOURCE).toURI();
|
||||
URI untrustedUri = getClass().getResource(UNTRUSTED_RESOURCE).toURI();
|
||||
this.template = Policy.getInstance("JavaPolicy", new URIParameter(policyUri));
|
||||
this.untrusted = Policy.getInstance("JavaPolicy", new URIParameter(untrustedUri));
|
||||
public ESPolicy(PermissionCollection dynamic, Map<String,Policy> plugins) {
|
||||
this.template = Security.readPolicy(getClass().getResource(POLICY_RESOURCE), JarHell.parseClassPath());
|
||||
this.untrusted = Security.readPolicy(getClass().getResource(UNTRUSTED_RESOURCE), new URL[0]);
|
||||
this.dynamic = dynamic;
|
||||
this.plugins = plugins;
|
||||
}
|
||||
|
@ -69,9 +65,10 @@ final class ESPolicy extends Policy {
|
|||
if (BootstrapInfo.UNTRUSTED_CODEBASE.equals(location.getFile())) {
|
||||
return untrusted.implies(domain, permission);
|
||||
}
|
||||
// check for an additional plugin permission
|
||||
PermissionCollection plugin = plugins.get(location.getFile());
|
||||
if (plugin != null && plugin.implies(permission)) {
|
||||
// check for an additional plugin permission: plugin policy is
|
||||
// only consulted for its codesources.
|
||||
Policy plugin = plugins.get(location.getFile());
|
||||
if (plugin != null && plugin.implies(domain, permission)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,10 +20,12 @@
|
|||
package org.elasticsearch.bootstrap;
|
||||
|
||||
import org.elasticsearch.common.SuppressForbidden;
|
||||
import org.elasticsearch.common.io.PathUtils;
|
||||
import org.elasticsearch.env.Environment;
|
||||
import org.elasticsearch.plugins.PluginInfo;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.AccessMode;
|
||||
import java.nio.file.DirectoryStream;
|
||||
|
@ -32,15 +34,14 @@ import java.nio.file.Files;
|
|||
import java.nio.file.NotDirectoryException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PermissionCollection;
|
||||
import java.security.Permissions;
|
||||
import java.security.Policy;
|
||||
import java.security.URIParameter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Initializes SecurityManager with necessary permissions.
|
||||
|
@ -99,8 +100,6 @@ final class Security {
|
|||
* Can only happen once!
|
||||
*/
|
||||
static void configure(Environment environment) throws Exception {
|
||||
// set properties for jar locations
|
||||
setCodebaseProperties();
|
||||
|
||||
// enable security policy: union of template and environment-based paths, and possibly plugin permissions
|
||||
Policy.setPolicy(new ESPolicy(createPermissions(environment), getPluginPermissions(environment)));
|
||||
|
@ -121,70 +120,34 @@ final class Security {
|
|||
selfTest();
|
||||
}
|
||||
|
||||
// mapping of jars to codebase properties
|
||||
// note that this is only read once, when policy is parsed.
|
||||
private static final Map<Pattern,String> SPECIAL_JARS;
|
||||
static {
|
||||
Map<Pattern,String> m = new IdentityHashMap<>();
|
||||
m.put(Pattern.compile(".*lucene-core-.*\\.jar$"), "es.security.jar.lucene.core");
|
||||
m.put(Pattern.compile(".*lucene-test-framework-.*\\.jar$"), "es.security.jar.lucene.testframework");
|
||||
m.put(Pattern.compile(".*randomizedtesting-runner-.*\\.jar$"), "es.security.jar.randomizedtesting.runner");
|
||||
m.put(Pattern.compile(".*junit4-ant-.*\\.jar$"), "es.security.jar.randomizedtesting.junit4");
|
||||
m.put(Pattern.compile(".*securemock-.*\\.jar$"), "es.security.jar.elasticsearch.securemock");
|
||||
SPECIAL_JARS = Collections.unmodifiableMap(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets properties (codebase URLs) for policy files.
|
||||
* JAR locations are not fixed so we have to find the locations of
|
||||
* the ones we want.
|
||||
*/
|
||||
@SuppressForbidden(reason = "proper use of URL")
|
||||
static void setCodebaseProperties() {
|
||||
for (URL url : JarHell.parseClassPath()) {
|
||||
for (Map.Entry<Pattern,String> e : SPECIAL_JARS.entrySet()) {
|
||||
if (e.getKey().matcher(url.getPath()).matches()) {
|
||||
String prop = e.getValue();
|
||||
if (System.getProperty(prop) != null) {
|
||||
throw new IllegalStateException("property: " + prop + " is unexpectedly set: " + System.getProperty(prop));
|
||||
}
|
||||
System.setProperty(prop, url.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String prop : SPECIAL_JARS.values()) {
|
||||
if (System.getProperty(prop) == null) {
|
||||
System.setProperty(prop, "file:/dev/null"); // no chance to be interpreted as "all"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets properties (codebase URLs) for policy files.
|
||||
* we look for matching plugins and set URLs to fit
|
||||
*/
|
||||
@SuppressForbidden(reason = "proper use of URL")
|
||||
static Map<String,PermissionCollection> getPluginPermissions(Environment environment) throws IOException, NoSuchAlgorithmException {
|
||||
Map<String,PermissionCollection> map = new HashMap<>();
|
||||
static Map<String,Policy> getPluginPermissions(Environment environment) throws IOException, NoSuchAlgorithmException {
|
||||
Map<String,Policy> map = new HashMap<>();
|
||||
if (Files.exists(environment.pluginsFile())) {
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(environment.pluginsFile())) {
|
||||
for (Path plugin : stream) {
|
||||
Path policyFile = plugin.resolve(PluginInfo.ES_PLUGIN_POLICY);
|
||||
if (Files.exists(policyFile)) {
|
||||
// parse the plugin's policy file into a set of permissions
|
||||
Policy policy = Policy.getInstance("JavaPolicy", new URIParameter(policyFile.toUri()));
|
||||
PermissionCollection permissions = policy.getPermissions(Security.class.getProtectionDomain());
|
||||
// this method is supported with the specific implementation we use, but just check for safety.
|
||||
if (permissions == Policy.UNSUPPORTED_EMPTY_COLLECTION) {
|
||||
throw new UnsupportedOperationException("JavaPolicy implementation does not support retrieving permissions");
|
||||
}
|
||||
// grant the permissions to each jar in the plugin
|
||||
// first get a list of URLs for the plugins' jars:
|
||||
List<URL> codebases = new ArrayList<>();
|
||||
try (DirectoryStream<Path> jarStream = Files.newDirectoryStream(plugin, "*.jar")) {
|
||||
for (Path jar : jarStream) {
|
||||
if (map.put(jar.toUri().toURL().getFile(), permissions) != null) {
|
||||
// just be paranoid ok?
|
||||
throw new IllegalStateException("per-plugin permissions already granted for jar file: " + jar);
|
||||
}
|
||||
codebases.add(jar.toUri().toURL());
|
||||
}
|
||||
}
|
||||
|
||||
// parse the plugin's policy file into a set of permissions
|
||||
Policy policy = readPolicy(policyFile.toUri().toURL(), codebases.toArray(new URL[codebases.size()]));
|
||||
|
||||
// consult this policy for each of the plugin's jars:
|
||||
for (URL url : codebases) {
|
||||
if (map.put(url.getFile(), policy) != null) {
|
||||
// just be paranoid ok?
|
||||
throw new IllegalStateException("per-plugin permissions already granted for jar file: " + url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -194,6 +157,35 @@ final class Security {
|
|||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and returns the specified {@code policyFile}.
|
||||
* <p>
|
||||
* Resources (e.g. jar files and directories) listed in {@code codebases} location
|
||||
* will be provided to the policy file via a system property of the short name:
|
||||
* e.g. <code>${codebase.joda-convert-1.2.jar}</code> would map to full URL.
|
||||
*/
|
||||
@SuppressForbidden(reason = "accesses fully qualified URLs to configure security")
|
||||
static Policy readPolicy(URL policyFile, URL codebases[]) {
|
||||
try {
|
||||
try {
|
||||
// set codebase properties
|
||||
for (URL url : codebases) {
|
||||
String shortName = PathUtils.get(url.toURI()).getFileName().toString();
|
||||
System.setProperty("codebase." + shortName, url.toString());
|
||||
}
|
||||
return Policy.getInstance("JavaPolicy", new URIParameter(policyFile.toURI()));
|
||||
} finally {
|
||||
// clear codebase properties
|
||||
for (URL url : codebases) {
|
||||
String shortName = PathUtils.get(url.toURI()).getFileName().toString();
|
||||
System.clearProperty("codebase." + shortName);
|
||||
}
|
||||
}
|
||||
} catch (NoSuchAlgorithmException | URISyntaxException e) {
|
||||
throw new IllegalArgumentException("unable to parse policy file `" + policyFile + "`", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** returns dynamic Permissions to configured paths */
|
||||
static Permissions createPermissions(Environment environment) throws IOException {
|
||||
Permissions policy = new Permissions();
|
||||
|
|
|
@ -31,46 +31,12 @@ grant codeBase "file:${{java.ext.dirs}}/*" {
|
|||
//// Very special jar permissions:
|
||||
//// These are dangerous permissions that we don't want to grant to everything.
|
||||
|
||||
grant codeBase "${es.security.jar.lucene.core}" {
|
||||
grant codeBase "${codebase.lucene-core-5.4.0-snapshot-1708254.jar}" {
|
||||
// needed to allow MMapDirectory's "unmap hack"
|
||||
permission java.lang.RuntimePermission "accessClassInPackage.sun.misc";
|
||||
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
|
||||
};
|
||||
|
||||
//// test framework permissions.
|
||||
//// These are mock objects and test management that we allow test framework libs
|
||||
//// to provide on our behalf. But tests themselves cannot do this stuff!
|
||||
|
||||
grant codeBase "${es.security.jar.elasticsearch.securemock}" {
|
||||
// needed to access ReflectionFactory (see below)
|
||||
permission java.lang.RuntimePermission "accessClassInPackage.sun.reflect";
|
||||
// needed to support creation of mocks
|
||||
permission java.lang.RuntimePermission "reflectionFactoryAccess";
|
||||
// needed for spy interception, etc
|
||||
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
|
||||
};
|
||||
|
||||
grant codeBase "${es.security.jar.lucene.testframework}" {
|
||||
// needed by RamUsageTester
|
||||
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
|
||||
};
|
||||
|
||||
grant codeBase "${es.security.jar.randomizedtesting.runner}" {
|
||||
// optionally needed for access to private test methods (e.g. beforeClass)
|
||||
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
|
||||
|
||||
// needed for top threads handling
|
||||
permission java.lang.RuntimePermission "modifyThreadGroup";
|
||||
};
|
||||
|
||||
grant codeBase "${es.security.jar.randomizedtesting.junit4}" {
|
||||
// needed for gson serialization
|
||||
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
|
||||
|
||||
// needed for stream redirection
|
||||
permission java.lang.RuntimePermission "setIO";
|
||||
};
|
||||
|
||||
//// Everything else:
|
||||
|
||||
grant {
|
||||
|
@ -126,10 +92,4 @@ grant {
|
|||
|
||||
// needed by JDKESLoggerTests
|
||||
permission java.util.logging.LoggingPermission "control";
|
||||
|
||||
// needed to install SSLFactories, advanced SSL configuration, etc.
|
||||
permission java.lang.RuntimePermission "setFactory";
|
||||
|
||||
// needed to allow installation of bouncycastle crypto provider
|
||||
permission java.security.SecurityPermission "putProviderProperty.BC";
|
||||
};
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//// additional test framework permissions.
|
||||
//// These are mock objects and test management that we allow test framework libs
|
||||
//// to provide on our behalf. But tests themselves cannot do this stuff!
|
||||
|
||||
grant codeBase "${codebase.securemock-1.1.jar}" {
|
||||
// needed to access ReflectionFactory (see below)
|
||||
permission java.lang.RuntimePermission "accessClassInPackage.sun.reflect";
|
||||
// needed to support creation of mocks
|
||||
permission java.lang.RuntimePermission "reflectionFactoryAccess";
|
||||
// needed for spy interception, etc
|
||||
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
|
||||
};
|
||||
|
||||
grant codeBase "${codebase.lucene-test-framework-5.4.0-snapshot-1708254.jar}" {
|
||||
// needed by RamUsageTester
|
||||
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
|
||||
};
|
||||
|
||||
grant codeBase "${codebase.randomizedtesting-runner-2.1.17.jar}" {
|
||||
// optionally needed for access to private test methods (e.g. beforeClass)
|
||||
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
|
||||
|
||||
// needed for top threads handling
|
||||
permission java.lang.RuntimePermission "modifyThreadGroup";
|
||||
};
|
||||
|
||||
grant codeBase "${codebase.junit4-ant-2.1.17.jar}" {
|
||||
// needed for gson serialization
|
||||
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
|
||||
|
||||
// needed for stream redirection
|
||||
permission java.lang.RuntimePermission "setIO";
|
||||
};
|
|
@ -19,28 +19,37 @@
|
|||
|
||||
package org.elasticsearch.bootstrap;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.RandomizedRunner;
|
||||
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.lucene.util.TestSecurityManager;
|
||||
import org.elasticsearch.bootstrap.Bootstrap;
|
||||
import org.elasticsearch.bootstrap.ESPolicy;
|
||||
import org.elasticsearch.bootstrap.Security;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.SuppressForbidden;
|
||||
import org.elasticsearch.common.io.PathUtils;
|
||||
import org.elasticsearch.plugins.PluginInfo;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.FilePermission;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.security.Permission;
|
||||
import java.security.PermissionCollection;
|
||||
import java.security.Permissions;
|
||||
import java.security.Policy;
|
||||
import java.security.URIParameter;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.carrotsearch.randomizedtesting.RandomizedTest.systemPropertyAsBoolean;
|
||||
|
||||
|
@ -83,7 +92,6 @@ public class BootstrapForTesting {
|
|||
// install security manager if requested
|
||||
if (systemPropertyAsBoolean("tests.security.manager", true)) {
|
||||
try {
|
||||
Security.setCodebaseProperties();
|
||||
// initialize paths the same exact way as bootstrap
|
||||
Permissions perms = new Permissions();
|
||||
// add permissions to everything in classpath
|
||||
|
@ -120,31 +128,17 @@ public class BootstrapForTesting {
|
|||
if (System.getProperty("tests.maven") == null) {
|
||||
perms.add(new RuntimePermission("setIO"));
|
||||
}
|
||||
|
||||
final Policy policy;
|
||||
// if its a plugin with special permissions, we use a wrapper policy impl to try
|
||||
// to simulate what happens with a real distribution
|
||||
List<URL> pluginPolicies = Collections.list(BootstrapForTesting.class.getClassLoader().getResources(PluginInfo.ES_PLUGIN_POLICY));
|
||||
if (!pluginPolicies.isEmpty()) {
|
||||
Permissions extra = new Permissions();
|
||||
for (URL url : pluginPolicies) {
|
||||
URI uri = url.toURI();
|
||||
Policy pluginPolicy = Policy.getInstance("JavaPolicy", new URIParameter(uri));
|
||||
PermissionCollection permissions = pluginPolicy.getPermissions(BootstrapForTesting.class.getProtectionDomain());
|
||||
// this method is supported with the specific implementation we use, but just check for safety.
|
||||
if (permissions == Policy.UNSUPPORTED_EMPTY_COLLECTION) {
|
||||
throw new UnsupportedOperationException("JavaPolicy implementation does not support retrieving permissions");
|
||||
}
|
||||
for (Permission permission : Collections.list(permissions.elements())) {
|
||||
extra.add(permission);
|
||||
}
|
||||
|
||||
// read test-framework permissions
|
||||
final Policy testFramework = Security.readPolicy(Bootstrap.class.getResource("test-framework.policy"), JarHell.parseClassPath());
|
||||
final Policy esPolicy = new ESPolicy(perms, getPluginPermissions());
|
||||
Policy.setPolicy(new Policy() {
|
||||
@Override
|
||||
public boolean implies(ProtectionDomain domain, Permission permission) {
|
||||
// implements union
|
||||
return esPolicy.implies(domain, permission) || testFramework.implies(domain, permission);
|
||||
}
|
||||
// TODO: try to get rid of this class now that the world is simpler?
|
||||
policy = new MockPluginPolicy(perms, extra);
|
||||
} else {
|
||||
policy = new ESPolicy(perms, Collections.emptyMap());
|
||||
}
|
||||
Policy.setPolicy(policy);
|
||||
});
|
||||
System.setSecurityManager(new TestSecurityManager());
|
||||
Security.selfTest();
|
||||
|
||||
|
@ -168,6 +162,58 @@ public class BootstrapForTesting {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* we dont know which codesources belong to which plugin, so just remove the permission from key codebases
|
||||
* like core, test-framework, etc. this way tests fail if accesscontroller blocks are missing.
|
||||
*/
|
||||
@SuppressForbidden(reason = "accesses fully qualified URLs to configure security")
|
||||
static Map<String,Policy> getPluginPermissions() throws Exception {
|
||||
List<URL> pluginPolicies = Collections.list(BootstrapForTesting.class.getClassLoader().getResources(PluginInfo.ES_PLUGIN_POLICY));
|
||||
if (pluginPolicies.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
// compute classpath minus obvious places, all other jars will get the permission.
|
||||
Set<URL> codebases = new HashSet<>(Arrays.asList(JarHell.parseClassPath()));
|
||||
Set<URL> excluded = new HashSet<>(Arrays.asList(
|
||||
// es core
|
||||
Bootstrap.class.getProtectionDomain().getCodeSource().getLocation(),
|
||||
// es test framework
|
||||
BootstrapForTesting.class.getProtectionDomain().getCodeSource().getLocation(),
|
||||
// lucene test framework
|
||||
LuceneTestCase.class.getProtectionDomain().getCodeSource().getLocation(),
|
||||
// randomized runner
|
||||
RandomizedRunner.class.getProtectionDomain().getCodeSource().getLocation(),
|
||||
// junit library
|
||||
Assert.class.getProtectionDomain().getCodeSource().getLocation()
|
||||
));
|
||||
codebases.removeAll(excluded);
|
||||
|
||||
// parse each policy file, with codebase substitution from the classpath
|
||||
final List<Policy> policies = new ArrayList<>();
|
||||
for (URL policyFile : pluginPolicies) {
|
||||
policies.add(Security.readPolicy(policyFile, codebases.toArray(new URL[codebases.size()])));
|
||||
}
|
||||
|
||||
// consult each policy file for those codebases
|
||||
Map<String,Policy> map = new HashMap<>();
|
||||
for (URL url : codebases) {
|
||||
map.put(url.getFile(), new Policy() {
|
||||
@Override
|
||||
public boolean implies(ProtectionDomain domain, Permission permission) {
|
||||
// implements union
|
||||
for (Policy p : policies) {
|
||||
if (p.implies(domain, permission)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
// does nothing, just easy way to make sure the class is loaded.
|
||||
public static void ensureInitialized() {}
|
||||
}
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* 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.logging.Loggers;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.net.URL;
|
||||
import java.security.CodeSource;
|
||||
import java.security.Permission;
|
||||
import java.security.PermissionCollection;
|
||||
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.
|
||||
* <p>
|
||||
* 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<CodeSource> excludedSources;
|
||||
|
||||
/**
|
||||
* Create a new MockPluginPolicy with dynamic {@code permissions} and
|
||||
* adding the extra plugin permissions from {@code insecurePluginProp} to
|
||||
* all code except test classes.
|
||||
*/
|
||||
MockPluginPolicy(PermissionCollection standard, PermissionCollection extra) throws Exception {
|
||||
// the hack begins!
|
||||
|
||||
this.standardPolicy = new ESPolicy(standard, Collections.emptyMap());
|
||||
this.extraPermissions = extra;
|
||||
|
||||
excludedSources = new HashSet<CodeSource>();
|
||||
// exclude some obvious places
|
||||
// es core
|
||||
excludedSources.add(Bootstrap.class.getProtectionDomain().getCodeSource());
|
||||
// es test framework
|
||||
excludedSources.add(getClass().getProtectionDomain().getCodeSource());
|
||||
// lucene test framework
|
||||
excludedSources.add(LuceneTestCase.class.getProtectionDomain().getCodeSource());
|
||||
// test runner
|
||||
excludedSources.add(RandomizedRunner.class.getProtectionDomain().getCodeSource());
|
||||
// junit library
|
||||
excludedSources.add(Assert.class.getProtectionDomain().getCodeSource());
|
||||
// scripts
|
||||
excludedSources.add(new CodeSource(new URL("file:" + BootstrapInfo.UNTRUSTED_CODEBASE), (Certificate[])null));
|
||||
|
||||
Loggers.getLogger(getClass()).debug("Apply extra permissions [{}] excluding codebases [{}]", extraPermissions, excludedSources);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean implies(ProtectionDomain domain, Permission permission) {
|
||||
CodeSource codeSource = domain.getCodeSource();
|
||||
// codesource can be null when reducing privileges via doPrivileged()
|
||||
if (codeSource == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (standardPolicy.implies(domain, permission)) {
|
||||
return true;
|
||||
} else if (excludedSources.contains(codeSource) == false &&
|
||||
codeSource.toString().contains("test-classes") == false) {
|
||||
return extraPermissions.implies(permission);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
5
pom.xml
5
pom.xml
|
@ -45,10 +45,15 @@
|
|||
<doclint.options>-Xdoclint:-missing</doclint.options>
|
||||
|
||||
<!-- libraries -->
|
||||
|
||||
<!-- NOTE: when changing these versions, also update security.policy and test-framework.policy to match -->
|
||||
<!-- (we don't do any automatic property substitution on those resources, it could get confusing... -->
|
||||
<lucene.version>5.4.0</lucene.version>
|
||||
<lucene.snapshot.revision>1708254</lucene.snapshot.revision>
|
||||
<lucene.maven.version>5.4.0-snapshot-${lucene.snapshot.revision}</lucene.maven.version>
|
||||
<testframework.version>2.1.17</testframework.version>
|
||||
<securemock.version>1.1</securemock.version>
|
||||
|
||||
<jackson.version>2.6.2</jackson.version>
|
||||
<slf4j.version>1.6.2</slf4j.version>
|
||||
<log4j.version>1.2.17</log4j.version>
|
||||
|
|
Loading…
Reference in New Issue