Split test runner security permissions (#64748)

The test framework security policy contains permissions for both gradle
and intellij running tests. These currently coexist in the same file,
though only one set of the jars permissions are granted to exist in any
given run. This works because java policy parsing is lenient, so if a
system property referenced in the file does not exist, the entire grant
is silently skipped. This commit splits these permissions into separate
policy files so that we do not rely on leniency, and can (in a followup)
add our own validation to fix java's leniency.
This commit is contained in:
Ryan Ernst 2020-11-09 11:41:49 -08:00 committed by GitHub
parent 2bf3e36144
commit b92c9b7147
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 35 additions and 28 deletions

View file

@ -335,7 +335,6 @@ configure(subprojects.findAll { ['archives', 'packages'].contains(it.name) }) {
dependencies { dependencies {
libs project(':server') libs project(':server')
libs project(':libs:elasticsearch-plugin-classloader')
libs project(':distribution:tools:java-version-checker') libs project(':distribution:tools:java-version-checker')
libs project(':distribution:tools:launchers') libs project(':distribution:tools:launchers')

View file

@ -39,8 +39,7 @@ dependencies {
api project(':libs:elasticsearch-x-content') api project(':libs:elasticsearch-x-content')
api project(":libs:elasticsearch-geo") api project(":libs:elasticsearch-geo")
compileOnly project(':libs:elasticsearch-plugin-classloader') implementation project(':libs:elasticsearch-plugin-classloader')
testRuntimeOnly project(':libs:elasticsearch-plugin-classloader')
// lucene // lucene
api "org.apache.lucene:lucene-core:${versions.lucene}" api "org.apache.lucene:lucene-core:${versions.lucene}"

View file

@ -0,0 +1,16 @@
grant codeBase "file:${gradle.dist.lib}/-" {
// gradle test worker code needs a slew of permissions, we give full access here since gradle isn't a production
// dependency and there's no point in exercising the security policy against it
permission java.security.AllPermission;
};
grant codeBase "file:${gradle.worker.jar}" {
// gradle test worker code needs a slew of permissions, we give full access here since gradle isn't a production
// dependency and there's no point in exercising the security policy against it
permission java.security.AllPermission;
};
grant {
// since the gradle test worker jar is on the test classpath, our tests should be able to read it
permission java.io.FilePermission "${gradle.worker.jar}", "read";
};

View file

@ -0,0 +1,6 @@
grant codeBase "${codebase.junit-rt.jar}" {
// allows IntelliJ IDEA JUnit test runner to control number of test iterations
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
};

View file

@ -88,25 +88,3 @@ grant codeBase "${codebase.httpasyncclient}" {
// rest client uses system properties which gets the default proxy // rest client uses system properties which gets the default proxy
permission java.net.NetPermission "getProxySelector"; permission java.net.NetPermission "getProxySelector";
}; };
grant codeBase "${codebase.junit-rt.jar}" {
// allows IntelliJ IDEA JUnit test runner to control number of test iterations
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
};
grant codeBase "file:${gradle.dist.lib}/-" {
// gradle test worker code needs a slew of permissions, we give full access here since gradle isn't a production
// dependency and there's no point in exercising the security policy against it
permission java.security.AllPermission;
};
grant codeBase "file:${gradle.worker.jar}" {
// gradle test worker code needs a slew of permissions, we give full access here since gradle isn't a production
// dependency and there's no point in exercising the security policy against it
permission java.security.AllPermission;
};
grant {
// since the gradle test worker jar is on the test classpath, our tests should be able to read it
permission java.io.FilePermission "${gradle.worker.jar}", "read";
};

View file

@ -138,18 +138,26 @@ public class BootstrapForTesting {
// TODO: cut over all tests to bind to ephemeral ports // TODO: cut over all tests to bind to ephemeral ports
perms.add(new SocketPermission("localhost:1024-", "listen,resolve")); perms.add(new SocketPermission("localhost:1024-", "listen,resolve"));
boolean inGradle = System.getProperty("tests.gradle") != null;
// read test-framework permissions // read test-framework permissions
Map<String, URL> codebases = PolicyUtil.getCodebaseJarMap(JarHell.parseClassPath()); Map<String, URL> codebases = PolicyUtil.getCodebaseJarMap(JarHell.parseClassPath());
// when testing server, the main elasticsearch code is not yet in a jar, so we need to manually add it // when testing server, the main elasticsearch code is not yet in a jar, so we need to manually add it
addClassCodebase(codebases,"elasticsearch", "org.elasticsearch.plugins.PluginsService"); addClassCodebase(codebases,"elasticsearch", "org.elasticsearch.plugins.PluginsService");
if (System.getProperty("tests.gradle") == null) { if (inGradle == false) {
// intellij and eclipse don't package our internal libs, so we need to set the codebases for them manually // intellij and eclipse don't package our internal libs, so we need to set the codebases for them manually
addClassCodebase(codebases,"plugin-classloader", "org.elasticsearch.plugins.ExtendedPluginsClassLoader"); addClassCodebase(codebases,"elasticsearch-plugin-classloader", "org.elasticsearch.plugins.ExtendedPluginsClassLoader");
addClassCodebase(codebases,"elasticsearch-nio", "org.elasticsearch.nio.ChannelFactory"); addClassCodebase(codebases,"elasticsearch-nio", "org.elasticsearch.nio.ChannelFactory");
addClassCodebase(codebases, "elasticsearch-secure-sm", "org.elasticsearch.secure_sm.SecureSM"); addClassCodebase(codebases, "elasticsearch-secure-sm", "org.elasticsearch.secure_sm.SecureSM");
addClassCodebase(codebases, "elasticsearch-rest-client", "org.elasticsearch.client.RestClient"); addClassCodebase(codebases, "elasticsearch-rest-client", "org.elasticsearch.client.RestClient");
} }
final Policy testFramework = PolicyUtil.readPolicy(Bootstrap.class.getResource("test-framework.policy"), codebases); final Policy testFramework = PolicyUtil.readPolicy(Bootstrap.class.getResource("test-framework.policy"), codebases);
final Policy runnerPolicy;
if (inGradle) {
runnerPolicy = PolicyUtil.readPolicy(Bootstrap.class.getResource("gradle.policy"), codebases);
} else {
runnerPolicy = PolicyUtil.readPolicy(Bootstrap.class.getResource("intellij.policy"), codebases);
}
// this mimicks the recursive data path permission added in Security.java // this mimicks the recursive data path permission added in Security.java
Permissions fastPathPermissions = new Permissions(); Permissions fastPathPermissions = new Permissions();
addDirectoryPath(fastPathPermissions, "java.io.tmpdir-fastpath", javaTmpDir, "read,readlink,write,delete", true); addDirectoryPath(fastPathPermissions, "java.io.tmpdir-fastpath", javaTmpDir, "read,readlink,write,delete", true);
@ -159,7 +167,8 @@ public class BootstrapForTesting {
@Override @Override
public boolean implies(ProtectionDomain domain, Permission permission) { public boolean implies(ProtectionDomain domain, Permission permission) {
// implements union // implements union
return esPolicy.implies(domain, permission) || testFramework.implies(domain, permission); return esPolicy.implies(domain, permission) || testFramework.implies(domain, permission) ||
runnerPolicy.implies(domain, permission);
} }
}); });
System.setSecurityManager(SecureSM.createTestSecureSM()); System.setSecurityManager(SecureSM.createTestSecureSM());