Merge pull request #10965 from rmuir/lockdown4

make testing better mimic reality for securitymanager
This commit is contained in:
Robert Muir 2015-05-04 17:06:24 -07:00
commit d62771ac5d
8 changed files with 115 additions and 47 deletions

View File

@ -43,6 +43,7 @@
<tests.locale>random</tests.locale>
<tests.timezone>random</tests.timezone>
<tests.slow>false</tests.slow>
<tests.security.manager>true</tests.security.manager>
<es.logger.level>ERROR</es.logger.level>
<tests.heap.size>512m</tests.heap.size>
<tests.heapdump.path>${basedir}/logs/</tests.heapdump.path>
@ -638,8 +639,6 @@
<!-- true if we are running tests from maven (as opposed to IDE, etc).
allows us to assert certain things work, like libsigar -->
<tests.maven>true</tests.maven>
<!-- security manager / test.policy -->
<java.security.policy>${basedir}/src/main/resources/org/elasticsearch/bootstrap/security.policy</java.security.policy>
</systemProperties>
</configuration>
</execution>

View File

@ -0,0 +1,48 @@
/*
* 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 java.net.URI;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Policy;
import java.security.ProtectionDomain;
import java.security.URIParameter;
/** custom policy for union of static and dynamic permissions */
public class ESPolicy extends Policy {
/** template policy file, the one used in tests */
static final String POLICY_RESOURCE = "security.policy";
final Policy template;
final PermissionCollection dynamic;
public ESPolicy(PermissionCollection dynamic) throws Exception {
URI uri = getClass().getResource(POLICY_RESOURCE).toURI();
this.template = Policy.getInstance("JavaPolicy", new URIParameter(uri));
this.dynamic = dynamic;
}
@Override
public boolean implies(ProtectionDomain domain, Permission permission) {
return template.implies(domain, permission) || dynamic.implies(permission);
}
}

View File

@ -19,18 +19,14 @@
package org.elasticsearch.bootstrap;
import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.env.Environment;
import java.io.*;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.Policy;
import java.security.ProtectionDomain;
import java.security.URIParameter;
/**
* Initializes securitymanager with necessary permissions.
@ -38,19 +34,15 @@ import java.security.URIParameter;
* We use a template file (the one we test with), and add additional
* permissions based on the environment (data paths, etc)
*/
class Security {
/** template policy file, the one used in tests */
static final String POLICY_RESOURCE = "security.policy";
public class Security {
/**
* Initializes securitymanager for the environment
* Can only happen once!
*/
static void configure(Environment environment) throws Exception {
// enable security policy: union of template and environment-based paths.
URI template = Security.class.getResource(POLICY_RESOURCE).toURI();
Policy.setPolicy(new ESPolicy(template, createPermissions(environment)));
Policy.setPolicy(new ESPolicy(createPermissions(environment)));
// enable security manager
System.setSecurityManager(new SecurityManager());
@ -64,6 +56,7 @@ class Security {
// TODO: improve test infra so we can reduce permissions where read/write
// is not really needed...
Permissions policy = new Permissions();
addPath(policy, PathUtils.get(System.getProperty("java.io.tmpdir")), "read,readlink,write,delete");
addPath(policy, environment.homeFile(), "read,readlink,write,delete");
addPath(policy, environment.configFile(), "read,readlink,write,delete");
addPath(policy, environment.logsFile(), "read,readlink,write,delete");
@ -79,7 +72,7 @@ class Security {
}
/** Add access to path (and all files underneath it */
static void addPath(Permissions policy, Path path, String permissions) throws IOException {
public static void addPath(Permissions policy, Path path, String permissions) throws IOException {
// paths may not exist yet
Files.createDirectories(path);
// add each path twice: once for itself, again for files underneath it
@ -88,7 +81,7 @@ class Security {
}
/** Simple checks that everything is ok */
static void selfTest() {
public static void selfTest() {
// check we can manipulate temporary files
try {
Files.delete(Files.createTempFile(null, null));
@ -98,20 +91,4 @@ class Security {
throw new SecurityException("Security misconfiguration: cannot access java.io.tmpdir", problem);
}
}
/** custom policy for union of static and dynamic permissions */
static class ESPolicy extends Policy {
final Policy template;
final PermissionCollection dynamic;
ESPolicy(URI template, PermissionCollection dynamic) throws Exception {
this.template = Policy.getInstance("JavaPolicy", new URIParameter(template));
this.dynamic = dynamic;
}
@Override
public boolean implies(ProtectionDomain domain, Permission permission) {
return template.implies(domain, permission) || dynamic.implies(permission);
}
}
}

View File

@ -21,15 +21,20 @@
// On startup, BootStrap reads environment and adds additional permissions
// for configured paths to these.
//// System code permissions:
//// These permissions apply to the JDK itself:
grant codeBase "file:${{java.ext.dirs}}/*" {
permission java.security.AllPermission;
};
//// Everything else:
grant {
// system jar resources
permission java.io.FilePermission "${java.home}${/}-", "read";
// temporary files
permission java.io.FilePermission "${java.io.tmpdir}", "read,write";
permission java.io.FilePermission "${java.io.tmpdir}${/}-", "read,write,delete";
// paths used for running tests
// compiled classes
permission java.io.FilePermission "${project.basedir}${/}target${/}classes${/}-", "read";

View File

@ -40,7 +40,15 @@ public class SecurityTests extends ElasticsearchTestCase {
Settings settings = settingsBuilder.build();
Environment environment = new Environment(settings);
Permissions permissions = Security.createPermissions(environment);
Path fakeTmpDir = createTempDir();
String realTmpDir = System.getProperty("java.io.tmpdir");
Permissions permissions;
try {
System.setProperty("java.io.tmpdir", fakeTmpDir.toString());
permissions = Security.createPermissions(environment);
} finally {
System.setProperty("java.io.tmpdir", realTmpDir);
}
// the fake es home
assertTrue(permissions.implies(new FilePermission(esHome.toString(), "read")));
@ -48,6 +56,8 @@ public class SecurityTests extends ElasticsearchTestCase {
assertFalse(permissions.implies(new FilePermission(path.toString(), "read")));
// some other sibling
assertFalse(permissions.implies(new FilePermission(path.resolve("other").toString(), "read")));
// double check we overwrote java.io.tmpdir correctly for the test
assertFalse(permissions.implies(new FilePermission(realTmpDir.toString(), "read")));
}
/** test generated permissions for all configured paths */
@ -63,7 +73,15 @@ public class SecurityTests extends ElasticsearchTestCase {
Settings settings = settingsBuilder.build();
Environment environment = new Environment(settings);
Permissions permissions = Security.createPermissions(environment);
Path fakeTmpDir = createTempDir();
String realTmpDir = System.getProperty("java.io.tmpdir");
Permissions permissions;
try {
System.setProperty("java.io.tmpdir", fakeTmpDir.toString());
permissions = Security.createPermissions(environment);
} finally {
System.setProperty("java.io.tmpdir", realTmpDir);
}
// check that all directories got permissions:
// homefile: this is needed unless we break out rules for "lib" dir.
@ -83,5 +101,9 @@ public class SecurityTests extends ElasticsearchTestCase {
}
// logs: r/w
assertTrue(permissions.implies(new FilePermission(environment.logsFile().toString(), "read,readlink,write,delete")));
// temp dir: r/w
assertTrue(permissions.implies(new FilePermission(fakeTmpDir.toString(), "read,readlink,write,delete")));
// double check we overwrote java.io.tmpdir correctly for the test
assertFalse(permissions.implies(new FilePermission(realTmpDir.toString(), "read")));
}
}

View File

@ -93,7 +93,7 @@ import static com.google.common.collect.Lists.newArrayList;
public abstract class ElasticsearchTestCase extends LuceneTestCase {
static {
SecurityHack.ensureInitialized();
SecurityBootstrap.ensureInitialized();
}
protected final ESLogger logger = Loggers.getLogger(getClass());

View File

@ -43,7 +43,7 @@ import org.elasticsearch.test.junit.listeners.ReproduceInfoPrinter;
public abstract class ElasticsearchTokenStreamTestCase extends BaseTokenStreamTestCase {
static {
SecurityHack.ensureInitialized();
SecurityBootstrap.ensureInitialized();
}
public static Version randomVersion() {

View File

@ -21,6 +21,12 @@ package org.elasticsearch.test;
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.io.PathUtils;
import java.security.Permissions;
import java.security.Policy;
import static com.carrotsearch.randomizedtesting.RandomizedTest.systemPropertyAsBoolean;
@ -28,18 +34,29 @@ import static com.carrotsearch.randomizedtesting.RandomizedTest.systemPropertyAs
* Installs test security manager (ensures it happens regardless of which
* test case happens to be first, test ordering, etc).
* <p>
* Note that this is BS, this should be done by the jvm (by passing -Djava.security.manager).
* turning it on/off needs to be the role of maven, not this stuff.
* The idea is to mimic as much as possible what happens with ES in production
* mode (e.g. assign permissions and install security manager the same way)
*/
class SecurityHack {
class SecurityBootstrap {
// TODO: can we share more code with the non-test side here
// without making things complex???
static {
// just like bootstrap, initialize natives, then SM
Bootstrap.initializeNatives(true, true);
// for IDEs, we check that security.policy is set
if (systemPropertyAsBoolean("tests.security.manager", true) &&
System.getProperty("java.security.policy") != null) {
System.setSecurityManager(new TestSecurityManager());
// install security manager if requested
if (systemPropertyAsBoolean("tests.security.manager", false)) {
try {
// initialize tmpdir the same exact way as bootstrap.
Permissions perms = new Permissions();
Security.addPath(perms, PathUtils.get(System.getProperty("java.io.tmpdir")), "read,readlink,write,delete");
Policy.setPolicy(new ESPolicy(perms));
System.setSecurityManager(new TestSecurityManager());
Security.selfTest();
} catch (Exception e) {
throw new RuntimeException("unable to install test security manager", e);
}
}
}