simplify security rules
This commit is contained in:
parent
8c0d03c3ee
commit
86fc8ceac7
|
@ -19,18 +19,19 @@
|
||||||
|
|
||||||
package org.elasticsearch.bootstrap;
|
package org.elasticsearch.bootstrap;
|
||||||
|
|
||||||
import com.google.common.io.ByteStreams;
|
|
||||||
|
|
||||||
import org.apache.lucene.util.StringHelper;
|
import org.apache.lucene.util.StringHelper;
|
||||||
import org.elasticsearch.common.logging.ESLogger;
|
|
||||||
import org.elasticsearch.common.logging.Loggers;
|
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.net.URI;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.NoSuchFileException;
|
|
||||||
import java.nio.file.Path;
|
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.
|
* Initializes securitymanager with necessary permissions.
|
||||||
|
@ -47,84 +48,74 @@ class Security {
|
||||||
* Initializes securitymanager for the environment
|
* Initializes securitymanager for the environment
|
||||||
* Can only happen once!
|
* Can only happen once!
|
||||||
*/
|
*/
|
||||||
static void configure(Environment environment) throws IOException {
|
static void configure(Environment environment) throws Exception {
|
||||||
ESLogger log = Loggers.getLogger(Security.class);
|
// init lucene random seed. it will use /dev/urandom where available:
|
||||||
// init lucene random seed. it will use /dev/urandom where available.
|
|
||||||
StringHelper.randomId();
|
StringHelper.randomId();
|
||||||
InputStream config = Security.class.getResourceAsStream(POLICY_RESOURCE);
|
|
||||||
if (config == null) {
|
// enable security policy: union of template and environment-based paths.
|
||||||
throw new NoSuchFileException(POLICY_RESOURCE);
|
URI template = Security.class.getResource(POLICY_RESOURCE).toURI();
|
||||||
}
|
Policy.setPolicy(new ESPolicy(template, createPermissions(environment)));
|
||||||
Path newConfig = processTemplate(config, environment);
|
|
||||||
System.setProperty("java.security.policy", newConfig.toString());
|
// enable security manager
|
||||||
System.setSecurityManager(new SecurityManager());
|
System.setSecurityManager(new SecurityManager());
|
||||||
try {
|
|
||||||
// don't hide securityexception here, it means java.io.tmpdir is not accessible!
|
// do some basic tests
|
||||||
Files.delete(newConfig);
|
selfTest();
|
||||||
} catch (SecurityException broken) {
|
|
||||||
log.error("unable to properly access temporary files, run with -Djava.security.debug=policy for more information");
|
|
||||||
throw broken;
|
|
||||||
} catch (IOException ignore) {
|
|
||||||
// e.g. virus scanner on windows
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// package-private for testing
|
|
||||||
static Path processTemplate(InputStream template, Environment environment) throws IOException {
|
|
||||||
Path processed = Files.createTempFile(null, null);
|
|
||||||
try (OutputStream output = new BufferedOutputStream(Files.newOutputStream(processed))) {
|
|
||||||
// copy the template as-is.
|
|
||||||
try (InputStream in = new BufferedInputStream(template)) {
|
|
||||||
ByteStreams.copy(in, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
// all policy files are UTF-8:
|
/** returns dynamic Permissions to configured paths */
|
||||||
// https://docs.oracle.com/javase/7/docs/technotes/guides/security/PolicyFiles.html
|
static Permissions createPermissions(Environment environment) throws IOException {
|
||||||
try (Writer writer = new OutputStreamWriter(output, StandardCharsets.UTF_8)) {
|
// TODO: improve test infra so we can reduce permissions where read/write
|
||||||
writer.write(System.lineSeparator());
|
// is not really needed...
|
||||||
writer.write("grant {");
|
Permissions policy = new Permissions();
|
||||||
writer.write(System.lineSeparator());
|
addPath(policy, environment.homeFile(), "read,readlink,write,delete");
|
||||||
|
addPath(policy, environment.configFile(), "read,readlink,write,delete");
|
||||||
// add permissions for all configured paths.
|
addPath(policy, environment.logsFile(), "read,readlink,write,delete");
|
||||||
// TODO: improve test infra so we can reduce permissions where read/write
|
addPath(policy, environment.pluginsFile(), "read,readlink,write,delete");
|
||||||
// is not really needed...
|
for (Path path : environment.dataFiles()) {
|
||||||
addPath(writer, environment.homeFile(), "read,readlink,write,delete");
|
addPath(policy, path, "read,readlink,write,delete");
|
||||||
addPath(writer, environment.configFile(), "read,readlink,write,delete");
|
|
||||||
addPath(writer, environment.logsFile(), "read,readlink,write,delete");
|
|
||||||
addPath(writer, environment.pluginsFile(), "read,readlink,write,delete");
|
|
||||||
|
|
||||||
// generate explicit perms for actual temp dir:
|
|
||||||
// (in case there is java.io.tmpdir sheistiness on windows)
|
|
||||||
addPath(writer, processed.getParent(), "read,readlink,write,delete");
|
|
||||||
|
|
||||||
for (Path path : environment.dataFiles()) {
|
|
||||||
addPath(writer, path, "read,readlink,write,delete");
|
|
||||||
}
|
|
||||||
for (Path path : environment.dataWithClusterFiles()) {
|
|
||||||
addPath(writer, path, "read,readlink,write,delete");
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.write("};");
|
|
||||||
writer.write(System.lineSeparator());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return processed;
|
for (Path path : environment.dataWithClusterFiles()) {
|
||||||
|
addPath(policy, path, "read,readlink,write,delete");
|
||||||
|
}
|
||||||
|
|
||||||
|
return policy;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void addPath(Writer writer, Path path, String permissions) throws IOException {
|
/** Add access to path (and all files underneath it */
|
||||||
|
static void addPath(Permissions policy, Path path, String permissions) throws IOException {
|
||||||
// paths may not exist yet
|
// paths may not exist yet
|
||||||
Files.createDirectories(path);
|
Files.createDirectories(path);
|
||||||
// add each path twice: once for itself, again for files underneath it
|
// add each path twice: once for itself, again for files underneath it
|
||||||
writer.write("permission java.io.FilePermission \"" + encode(path) + "\", \"" + permissions + "\";");
|
policy.add(new FilePermission(path.toString(), permissions));
|
||||||
writer.write(System.lineSeparator());
|
policy.add(new FilePermission(path.toString() + path.getFileSystem().getSeparator() + "-", permissions));
|
||||||
writer.write("permission java.io.FilePermission \"" + encode(path) + "${/}-\", \"" + permissions + "\";");
|
|
||||||
writer.write(System.lineSeparator());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any backslashes in paths must be escaped, because it is the escape character when parsing.
|
/** Simple checks that everything is ok */
|
||||||
// See "Note Regarding File Path Specifications on Windows Systems".
|
static void selfTest() {
|
||||||
// https://docs.oracle.com/javase/7/docs/technotes/guides/security/PolicyFiles.html
|
// check we can manipulate temporary files
|
||||||
static String encode(Path path) {
|
try {
|
||||||
return path.toString().replace("\\", "\\\\");
|
Files.delete(Files.createTempFile(null, null));
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
// potentially virus scanner
|
||||||
|
} catch (SecurityException problem) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,12 +24,9 @@ import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.FilePermission;
|
import java.io.FilePermission;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.security.Policy;
|
import java.security.Permissions;
|
||||||
import java.security.ProtectionDomain;
|
|
||||||
import java.security.URIParameter;
|
|
||||||
|
|
||||||
public class SecurityTests extends ElasticsearchTestCase {
|
public class SecurityTests extends ElasticsearchTestCase {
|
||||||
|
|
||||||
|
@ -42,17 +39,15 @@ public class SecurityTests extends ElasticsearchTestCase {
|
||||||
settingsBuilder.put("path.home", esHome.toString());
|
settingsBuilder.put("path.home", esHome.toString());
|
||||||
Settings settings = settingsBuilder.build();
|
Settings settings = settingsBuilder.build();
|
||||||
|
|
||||||
Environment environment = new Environment(settings);
|
Environment environment = new Environment(settings);
|
||||||
Path policyFile = Security.processTemplate(new ByteArrayInputStream(new byte[0]), environment);
|
Permissions permissions = Security.createPermissions(environment);
|
||||||
|
|
||||||
ProtectionDomain domain = getClass().getProtectionDomain();
|
|
||||||
Policy policy = Policy.getInstance("JavaPolicy", new URIParameter(policyFile.toUri()));
|
|
||||||
// the fake es home
|
// the fake es home
|
||||||
assertTrue(policy.implies(domain, new FilePermission(esHome.toString(), "read")));
|
assertTrue(permissions.implies(new FilePermission(esHome.toString(), "read")));
|
||||||
// its parent
|
// its parent
|
||||||
assertFalse(policy.implies(domain, new FilePermission(path.toString(), "read")));
|
assertFalse(permissions.implies(new FilePermission(path.toString(), "read")));
|
||||||
// some other sibling
|
// some other sibling
|
||||||
assertFalse(policy.implies(domain, new FilePermission(path.resolve("other").toString(), "read")));
|
assertFalse(permissions.implies(new FilePermission(path.resolve("other").toString(), "read")));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** test generated permissions for all configured paths */
|
/** test generated permissions for all configured paths */
|
||||||
|
@ -67,29 +62,26 @@ public class SecurityTests extends ElasticsearchTestCase {
|
||||||
settingsBuilder.put("path.logs", path.resolve("logs").toString());
|
settingsBuilder.put("path.logs", path.resolve("logs").toString());
|
||||||
Settings settings = settingsBuilder.build();
|
Settings settings = settingsBuilder.build();
|
||||||
|
|
||||||
Environment environment = new Environment(settings);
|
Environment environment = new Environment(settings);
|
||||||
Path policyFile = Security.processTemplate(new ByteArrayInputStream(new byte[0]), environment);
|
Permissions permissions = Security.createPermissions(environment);
|
||||||
|
|
||||||
ProtectionDomain domain = getClass().getProtectionDomain();
|
|
||||||
Policy policy = Policy.getInstance("JavaPolicy", new URIParameter(policyFile.toUri()));
|
|
||||||
|
|
||||||
// check that all directories got permissions:
|
// check that all directories got permissions:
|
||||||
// homefile: this is needed unless we break out rules for "lib" dir.
|
// homefile: this is needed unless we break out rules for "lib" dir.
|
||||||
// TODO: make read-only
|
// TODO: make read-only
|
||||||
assertTrue(policy.implies(domain, new FilePermission(environment.homeFile().toString(), "read,readlink,write,delete")));
|
assertTrue(permissions.implies(new FilePermission(environment.homeFile().toString(), "read,readlink,write,delete")));
|
||||||
// config file
|
// config file
|
||||||
// TODO: make read-only
|
// TODO: make read-only
|
||||||
assertTrue(policy.implies(domain, new FilePermission(environment.configFile().toString(), "read,readlink,write,delete")));
|
assertTrue(permissions.implies(new FilePermission(environment.configFile().toString(), "read,readlink,write,delete")));
|
||||||
// plugins: r/w, TODO: can this be minimized?
|
// plugins: r/w, TODO: can this be minimized?
|
||||||
assertTrue(policy.implies(domain, new FilePermission(environment.pluginsFile().toString(), "read,readlink,write,delete")));
|
assertTrue(permissions.implies(new FilePermission(environment.pluginsFile().toString(), "read,readlink,write,delete")));
|
||||||
// data paths: r/w
|
// data paths: r/w
|
||||||
for (Path dataPath : environment.dataFiles()) {
|
for (Path dataPath : environment.dataFiles()) {
|
||||||
assertTrue(policy.implies(domain, new FilePermission(dataPath.toString(), "read,readlink,write,delete")));
|
assertTrue(permissions.implies(new FilePermission(dataPath.toString(), "read,readlink,write,delete")));
|
||||||
}
|
}
|
||||||
for (Path dataPath : environment.dataWithClusterFiles()) {
|
for (Path dataPath : environment.dataWithClusterFiles()) {
|
||||||
assertTrue(policy.implies(domain, new FilePermission(dataPath.toString(), "read,readlink,write,delete")));
|
assertTrue(permissions.implies(new FilePermission(dataPath.toString(), "read,readlink,write,delete")));
|
||||||
}
|
}
|
||||||
// logs: r/w
|
// logs: r/w
|
||||||
assertTrue(policy.implies(domain, new FilePermission(environment.logsFile().toString(), "read,readlink,write,delete")));
|
assertTrue(permissions.implies(new FilePermission(environment.logsFile().toString(), "read,readlink,write,delete")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue