From 270cb9f349aad7a3b51edb26cf43984b9bb18372 Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Wed, 22 Apr 2015 03:04:50 -0400 Subject: [PATCH 1/9] enable securitymanager --- config/elasticsearch.yml | 9 + .../tests.policy => config/security.policy | 17 +- pom.xml | 2 +- .../elasticsearch/bootstrap/Bootstrap.java | 13 +- .../org/elasticsearch/bootstrap/Security.java | 160 ++++++++++++++++++ .../elasticsearch/env/NodeEnvironment.java | 4 +- 6 files changed, 194 insertions(+), 11 deletions(-) rename dev-tools/tests.policy => config/security.policy (93%) create mode 100644 src/main/java/org/elasticsearch/bootstrap/Security.java diff --git a/config/elasticsearch.yml b/config/elasticsearch.yml index f1359cd58d0..8842b93e35c 100644 --- a/config/elasticsearch.yml +++ b/config/elasticsearch.yml @@ -231,6 +231,15 @@ # #http.enabled: false +################################### Security ################################## + +# SecurityManager runs elasticsearch with a lower set of priviledges. +# For more information, see +# . + +# Disable security completely: +# +# security.enabled: false ################################### Gateway ################################### diff --git a/dev-tools/tests.policy b/config/security.policy similarity index 93% rename from dev-tools/tests.policy rename to config/security.policy index 724f001e422..44b89c47c58 100644 --- a/dev-tools/tests.policy +++ b/config/security.policy @@ -17,21 +17,26 @@ * under the License. */ -// Policy file to prevent tests from writing outside the test sandbox directory -// PLEASE NOTE: You may need to enable other permissions when new tests are added, -// everything not allowed here is forbidden! +// Default security policy file. +// On startup, BootStrap reads environment and adds additional permissions +// for configured paths to these. grant { - // contain read access to only what we need: + // 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 // project base directory permission java.io.FilePermission "${project.basedir}${/}target${/}-", "read"; // read permission for lib sigar permission java.io.FilePermission "${project.basedir}${/}lib/sigar{/}-", "read"; // mvn custom ./m2/repository for dependency jars permission java.io.FilePermission "${m2.repository}${/}-", "read"; - // system jar resources - permission java.io.FilePermission "${java.home}${/}-", "read"; // per-jvm directory permission java.io.FilePermission "${junit4.childvm.cwd}${/}temp", "read,write"; permission java.io.FilePermission "${junit4.childvm.cwd}${/}temp${/}-", "read,write,delete"; diff --git a/pom.xml b/pom.xml index 772d7ef6578..66df6cbd5d6 100644 --- a/pom.xml +++ b/pom.xml @@ -628,7 +628,7 @@ ${tests.compatibility} true - ${basedir}/dev-tools/tests.policy + ${basedir}/config/security.policy diff --git a/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index 9020d115a8b..72c13efa0f4 100644 --- a/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -26,7 +26,6 @@ import org.elasticsearch.common.SuppressForbidden; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.inject.CreationException; import org.elasticsearch.common.inject.spi.Message; -import org.elasticsearch.common.io.FileSystemUtils; import org.elasticsearch.common.io.PathUtils; import org.elasticsearch.common.jna.Natives; import org.elasticsearch.common.logging.ESLogger; @@ -40,7 +39,6 @@ import org.elasticsearch.node.Node; import org.elasticsearch.node.NodeBuilder; import org.elasticsearch.node.internal.InternalSettingsPreparer; -import java.nio.file.Paths; import java.util.Locale; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -61,6 +59,7 @@ public class Bootstrap { private static Bootstrap bootstrap; private void setup(boolean addShutdownHook, Tuple tuple) throws Exception { + setupSecurity(tuple.v1(), tuple.v2()); if (tuple.v1().getAsBoolean("bootstrap.mlockall", false)) { Natives.tryMlockall(); } @@ -92,6 +91,16 @@ public class Bootstrap { }); } } + + private void setupSecurity(Settings settings, Environment environment) throws Exception { + ESLogger logger = Loggers.getLogger(Bootstrap.class); + if (settings.getAsBoolean("security.enabled", true)) { + Security.configure(environment); + logger.info("security enabled"); + } else { + logger.info("security disabled"); + } + } @SuppressForbidden(reason = "Exception#printStackTrace()") private static void setupLogging(Tuple tuple) { diff --git a/src/main/java/org/elasticsearch/bootstrap/Security.java b/src/main/java/org/elasticsearch/bootstrap/Security.java new file mode 100644 index 00000000000..35de7edbe9f --- /dev/null +++ b/src/main/java/org/elasticsearch/bootstrap/Security.java @@ -0,0 +1,160 @@ +/* + * 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 org.apache.lucene.util.Constants; +import org.elasticsearch.common.io.PathUtils; +import org.elasticsearch.common.logging.Loggers; +import org.elasticsearch.env.Environment; +import org.elasticsearch.env.NodeEnvironment; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileStore; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashSet; +import java.util.Set; + +/** + * Initializes securitymanager with necessary permissions. + *

+ * We use a template file (the one we test with), and add additional + * permissions based on the environment (data paths, etc) + */ +class Security { + + /** + * Initializes securitymanager for the environment + * Can only happen once! + */ + static void configure(Environment environment) throws IOException { + Path newConfig = processTemplate(environment.configFile().resolve("security.policy"), environment); + System.setProperty("java.security.policy", newConfig.toString()); + System.setSecurityManager(new SecurityManager()); + try { + Files.delete(newConfig); + } catch (Exception e) { + Loggers.getLogger(Security.class).warn("unable to remove temporary file: " + newConfig, e); + } + } + + // package-private for testing + static Path processTemplate(Path template, Environment environment) throws IOException { + Path processed = Files.createTempFile(null, null); + try (OutputStream output = new BufferedOutputStream(Files.newOutputStream(processed))) { + // copy the template as-is. + Files.copy(template, output); + + // add permissions for all configured paths. + Set paths = new HashSet<>(); + paths.add(environment.homeFile()); + paths.add(environment.configFile()); + paths.add(environment.logsFile()); + paths.add(environment.pluginsFile()); + paths.add(environment.workFile()); + paths.add(environment.workWithClusterFile()); + for (Path path : environment.dataFiles()) { + paths.add(path); + } + for (Path path : environment.dataWithClusterFiles()) { + paths.add(path); + } + output.write(createPermissions(paths)); + } + return processed; + } + + // package private for testing + static byte[] createPermissions(Set paths) throws IOException { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + + // all policy files are UTF-8: + // https://docs.oracle.com/javase/7/docs/technotes/guides/security/PolicyFiles.html + try (Writer writer = new OutputStreamWriter(stream, StandardCharsets.UTF_8)) { + writer.write(System.lineSeparator()); + writer.write("grant {"); + writer.write(System.lineSeparator()); + for (Path path : paths) { + // add each path twice: once for itself, again for files underneath it + addPath(writer, encode(path), "read,readlink,write,delete"); + addRecursivePath(writer, encode(path), "read,readlink,write,delete"); + } + + // on *nix, try to grant read perms to file stores / SSD detection + if (!Constants.WINDOWS) { + Set stores = new HashSet<>(); + for (FileStore store : PathUtils.getDefaultFileSystem().getFileStores()) { + try { + String mount = NodeEnvironment.getMountPoint(store); + // mount point for fstat() calls against it + if (mount.startsWith("/")) { + stores.add(mount); + } + // block device: add it for SSD detection + if (store.name().startsWith("/")) { + stores.add(store.name()); + } + } catch (Throwable t) { + // these are hacks that are not guaranteed + } + } + for (String store : stores) { + addPath(writer, encode(store), "read,readlink"); + } + addRecursivePath(writer, "/sys/block", "read,readlink"); + addRecursivePath(writer, "/sys/devices", "read,readlink"); + addRecursivePath(writer, "/dev", "read,readlink"); + addRecursivePath(writer, "/devices", "read,readlink"); + } + + writer.write("};"); + writer.write(System.lineSeparator()); + } + + return stream.toByteArray(); + } + + static void addPath(Writer writer, String path, String permissions) throws IOException { + writer.write("permission java.io.FilePermission \"" + path + "\", \"" + permissions + "\";"); + writer.write(System.lineSeparator()); + } + + static void addRecursivePath(Writer writer, String path, String permissions) throws IOException { + writer.write("permission java.io.FilePermission \"" + path + "${/}-\", \"" + permissions + "\";"); + writer.write(System.lineSeparator()); + } + + // Any backslashes in paths must be escaped, because it is the escape character when parsing. + // See "Note Regarding File Path Specifications on Windows Systems". + // https://docs.oracle.com/javase/7/docs/technotes/guides/security/PolicyFiles.html + static String encode(Path path) { + return encode(path.toString()); + } + + static String encode(String path) { + return path.replace("\\", "\\\\"); + } +} diff --git a/src/main/java/org/elasticsearch/env/NodeEnvironment.java b/src/main/java/org/elasticsearch/env/NodeEnvironment.java index 9436888e070..aef08e60b38 100644 --- a/src/main/java/org/elasticsearch/env/NodeEnvironment.java +++ b/src/main/java/org/elasticsearch/env/NodeEnvironment.java @@ -295,7 +295,7 @@ public class NodeEnvironment extends AbstractComponent implements Closeable { // NOTE: poached from Lucene's IOUtils: /** Files.getFileStore(Path) useless here! Don't complain, just try it yourself. */ - private static FileStore getFileStore(Path path) throws IOException { + public static FileStore getFileStore(Path path) throws IOException { FileStore store = Files.getFileStore(path); try { @@ -317,7 +317,7 @@ public class NodeEnvironment extends AbstractComponent implements Closeable { // NOTE: poached from Lucene's IOUtils: // these are hacks that are not guaranteed - private static String getMountPoint(FileStore store) { + public static String getMountPoint(FileStore store) { String desc = store.toString(); return desc.substring(0, desc.lastIndexOf('(') - 1); } From 0865d220f4d55d9dca858710d2f8240b0c87e7d2 Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Thu, 23 Apr 2015 15:04:58 -0400 Subject: [PATCH 2/9] Remove crazy permissions for filestores, ssds, now that this logic has been refactored. Log a warning when security is disabled. --- .../elasticsearch/bootstrap/Bootstrap.java | 2 +- .../org/elasticsearch/bootstrap/Security.java | 35 ++----------------- 2 files changed, 4 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index 72c13efa0f4..f69a9c35409 100644 --- a/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -98,7 +98,7 @@ public class Bootstrap { Security.configure(environment); logger.info("security enabled"); } else { - logger.info("security disabled"); + logger.warn("security disabled"); } } diff --git a/src/main/java/org/elasticsearch/bootstrap/Security.java b/src/main/java/org/elasticsearch/bootstrap/Security.java index 35de7edbe9f..287cfa060e1 100644 --- a/src/main/java/org/elasticsearch/bootstrap/Security.java +++ b/src/main/java/org/elasticsearch/bootstrap/Security.java @@ -19,11 +19,9 @@ package org.elasticsearch.bootstrap; -import org.apache.lucene.util.Constants; -import org.elasticsearch.common.io.PathUtils; +import org.apache.lucene.util.StringHelper; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.env.Environment; -import org.elasticsearch.env.NodeEnvironment; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; @@ -32,7 +30,6 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.StandardCharsets; -import java.nio.file.FileStore; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashSet; @@ -51,6 +48,8 @@ class Security { * Can only happen once! */ static void configure(Environment environment) throws IOException { + // init lucene random seed. it will use /dev/urandom where available. + StringHelper.randomId(); Path newConfig = processTemplate(environment.configFile().resolve("security.policy"), environment); System.setProperty("java.security.policy", newConfig.toString()); System.setSecurityManager(new SecurityManager()); @@ -102,34 +101,6 @@ class Security { addPath(writer, encode(path), "read,readlink,write,delete"); addRecursivePath(writer, encode(path), "read,readlink,write,delete"); } - - // on *nix, try to grant read perms to file stores / SSD detection - if (!Constants.WINDOWS) { - Set stores = new HashSet<>(); - for (FileStore store : PathUtils.getDefaultFileSystem().getFileStores()) { - try { - String mount = NodeEnvironment.getMountPoint(store); - // mount point for fstat() calls against it - if (mount.startsWith("/")) { - stores.add(mount); - } - // block device: add it for SSD detection - if (store.name().startsWith("/")) { - stores.add(store.name()); - } - } catch (Throwable t) { - // these are hacks that are not guaranteed - } - } - for (String store : stores) { - addPath(writer, encode(store), "read,readlink"); - } - addRecursivePath(writer, "/sys/block", "read,readlink"); - addRecursivePath(writer, "/sys/devices", "read,readlink"); - addRecursivePath(writer, "/dev", "read,readlink"); - addRecursivePath(writer, "/devices", "read,readlink"); - } - writer.write("};"); writer.write(System.lineSeparator()); } From e2861bd7be0e9534d1145fab138edb577ee1723d Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Thu, 23 Apr 2015 15:32:00 -0400 Subject: [PATCH 3/9] ensure we only pull system filestores once time --- .../org/elasticsearch/env/Environment.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/elasticsearch/env/Environment.java b/src/main/java/org/elasticsearch/env/Environment.java index dab2d22f3f3..b19407cf262 100644 --- a/src/main/java/org/elasticsearch/env/Environment.java +++ b/src/main/java/org/elasticsearch/env/Environment.java @@ -58,7 +58,20 @@ public class Environment { private final Path logsFile; /** List of filestores on the system */ - private final FileStore[] fileStores; + private static final FileStore[] fileStores; + + /** + * We have to do this in clinit instead of init, because ES code is pretty messy, + * and makes these environments, throws them away, makes them again, etc. + */ + static { + // gather information about filesystems + ArrayList allStores = new ArrayList<>(); + for (FileStore store : PathUtils.getDefaultFileSystem().getFileStores()) { + allStores.add(new ESFileStore(store)); + } + fileStores = allStores.toArray(new ESFileStore[allStores.size()]); + } public Environment() { this(EMPTY_SETTINGS); @@ -109,13 +122,6 @@ public class Environment { } else { logsFile = homeFile.resolve("logs"); } - - // gather information about filesystems - ArrayList allStores = new ArrayList<>(); - for (FileStore store : PathUtils.getDefaultFileSystem().getFileStores()) { - allStores.add(new ESFileStore(store)); - } - fileStores = allStores.toArray(new ESFileStore[allStores.size()]); } /** From 573e81d2ea914f0aeb4c97128de8961e8bc68816 Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Thu, 23 Apr 2015 16:01:16 -0400 Subject: [PATCH 4/9] Ensure paths exist (or more permissions are needed later) --- src/main/java/org/elasticsearch/bootstrap/Security.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/elasticsearch/bootstrap/Security.java b/src/main/java/org/elasticsearch/bootstrap/Security.java index 287cfa060e1..2242b970259 100644 --- a/src/main/java/org/elasticsearch/bootstrap/Security.java +++ b/src/main/java/org/elasticsearch/bootstrap/Security.java @@ -76,6 +76,7 @@ class Security { paths.add(environment.workFile()); paths.add(environment.workWithClusterFile()); for (Path path : environment.dataFiles()) { + System.out.println("datapath=" + path); paths.add(path); } for (Path path : environment.dataWithClusterFiles()) { @@ -97,6 +98,8 @@ class Security { writer.write("grant {"); writer.write(System.lineSeparator()); for (Path path : paths) { + // data paths actually may not exist yet. + Files.createDirectories(path); // add each path twice: once for itself, again for files underneath it addPath(writer, encode(path), "read,readlink,write,delete"); addRecursivePath(writer, encode(path), "read,readlink,write,delete"); From 5d2153fe9be8ca4b8454a42666ed9821898ce55d Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Thu, 23 Apr 2015 16:03:19 -0400 Subject: [PATCH 5/9] remove stray sop --- src/main/java/org/elasticsearch/bootstrap/Security.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/elasticsearch/bootstrap/Security.java b/src/main/java/org/elasticsearch/bootstrap/Security.java index 2242b970259..ac2bf6b0913 100644 --- a/src/main/java/org/elasticsearch/bootstrap/Security.java +++ b/src/main/java/org/elasticsearch/bootstrap/Security.java @@ -76,7 +76,6 @@ class Security { paths.add(environment.workFile()); paths.add(environment.workWithClusterFile()); for (Path path : environment.dataFiles()) { - System.out.println("datapath=" + path); paths.add(path); } for (Path path : environment.dataWithClusterFiles()) { From b2850bff47a3fffe005c8fd05adcacc973ffcbaa Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Thu, 23 Apr 2015 21:18:56 -0400 Subject: [PATCH 6/9] remove logging statements for another bikeshed --- src/main/java/org/elasticsearch/bootstrap/Bootstrap.java | 4 ---- src/main/java/org/elasticsearch/bootstrap/Security.java | 7 ++----- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index f69a9c35409..55d3712af71 100644 --- a/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -93,12 +93,8 @@ public class Bootstrap { } private void setupSecurity(Settings settings, Environment environment) throws Exception { - ESLogger logger = Loggers.getLogger(Bootstrap.class); if (settings.getAsBoolean("security.enabled", true)) { Security.configure(environment); - logger.info("security enabled"); - } else { - logger.warn("security disabled"); } } diff --git a/src/main/java/org/elasticsearch/bootstrap/Security.java b/src/main/java/org/elasticsearch/bootstrap/Security.java index ac2bf6b0913..0a2bfec614e 100644 --- a/src/main/java/org/elasticsearch/bootstrap/Security.java +++ b/src/main/java/org/elasticsearch/bootstrap/Security.java @@ -19,6 +19,7 @@ package org.elasticsearch.bootstrap; +import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.StringHelper; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.env.Environment; @@ -53,11 +54,7 @@ class Security { Path newConfig = processTemplate(environment.configFile().resolve("security.policy"), environment); System.setProperty("java.security.policy", newConfig.toString()); System.setSecurityManager(new SecurityManager()); - try { - Files.delete(newConfig); - } catch (Exception e) { - Loggers.getLogger(Security.class).warn("unable to remove temporary file: " + newConfig, e); - } + IOUtils.deleteFilesIgnoringExceptions(newConfig); // TODO: maybe log something if it fails? } // package-private for testing From 500c956b45f25a8bc6a6d7ff640fa38350d6b093 Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Thu, 23 Apr 2015 22:02:57 -0400 Subject: [PATCH 7/9] Remove policy config file, its a resource. Remove exposed boolean to turn off security. Add unit test --- config/elasticsearch.yml | 10 ---- pom.xml | 2 +- .../elasticsearch/bootstrap/Bootstrap.java | 2 +- .../org/elasticsearch/bootstrap/Security.java | 20 ++++++-- .../elasticsearch/bootstrap}/security.policy | 0 .../bootstrap/SecurityTests.java | 48 +++++++++++++++++++ 6 files changed, 66 insertions(+), 16 deletions(-) rename {config => src/main/resources/org/elasticsearch/bootstrap}/security.policy (100%) create mode 100644 src/test/java/org/elasticsearch/bootstrap/SecurityTests.java diff --git a/config/elasticsearch.yml b/config/elasticsearch.yml index 8842b93e35c..35383a4c5ac 100644 --- a/config/elasticsearch.yml +++ b/config/elasticsearch.yml @@ -231,16 +231,6 @@ # #http.enabled: false -################################### Security ################################## - -# SecurityManager runs elasticsearch with a lower set of priviledges. -# For more information, see -# . - -# Disable security completely: -# -# security.enabled: false - ################################### Gateway ################################### # The gateway allows for persisting the cluster state between full cluster diff --git a/pom.xml b/pom.xml index 6dd7050bbd6..a6c0d2f8517 100644 --- a/pom.xml +++ b/pom.xml @@ -630,7 +630,7 @@ ${tests.compatibility} true - ${basedir}/config/security.policy + ${basedir}/src/main/resources/org/elasticsearch/bootstrap/security.policy diff --git a/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index 55d3712af71..6d1652f2525 100644 --- a/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -93,7 +93,7 @@ public class Bootstrap { } private void setupSecurity(Settings settings, Environment environment) throws Exception { - if (settings.getAsBoolean("security.enabled", true)) { + if (settings.getAsBoolean("security.manager.enabled", true)) { Security.configure(environment); } } diff --git a/src/main/java/org/elasticsearch/bootstrap/Security.java b/src/main/java/org/elasticsearch/bootstrap/Security.java index 0a2bfec614e..7e5739761c7 100644 --- a/src/main/java/org/elasticsearch/bootstrap/Security.java +++ b/src/main/java/org/elasticsearch/bootstrap/Security.java @@ -19,19 +19,22 @@ package org.elasticsearch.bootstrap; +import com.google.common.io.ByteStreams; + import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.StringHelper; -import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.env.Environment; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.HashSet; import java.util.Set; @@ -44,6 +47,9 @@ import java.util.Set; */ class Security { + /** template policy file, the one used in tests */ + static final String POLICY_RESOURCE = "security.policy"; + /** * Initializes securitymanager for the environment * Can only happen once! @@ -51,18 +57,24 @@ class Security { static void configure(Environment environment) throws IOException { // init lucene random seed. it will use /dev/urandom where available. StringHelper.randomId(); - Path newConfig = processTemplate(environment.configFile().resolve("security.policy"), environment); + InputStream config = Security.class.getResourceAsStream(POLICY_RESOURCE); + if (config == null) { + throw new NoSuchFileException(POLICY_RESOURCE); + } + Path newConfig = processTemplate(config, environment); System.setProperty("java.security.policy", newConfig.toString()); System.setSecurityManager(new SecurityManager()); IOUtils.deleteFilesIgnoringExceptions(newConfig); // TODO: maybe log something if it fails? } // package-private for testing - static Path processTemplate(Path template, Environment environment) throws IOException { + 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. - Files.copy(template, output); + try (InputStream in = template) { + ByteStreams.copy(in, output); + } // add permissions for all configured paths. Set paths = new HashSet<>(); diff --git a/config/security.policy b/src/main/resources/org/elasticsearch/bootstrap/security.policy similarity index 100% rename from config/security.policy rename to src/main/resources/org/elasticsearch/bootstrap/security.policy diff --git a/src/test/java/org/elasticsearch/bootstrap/SecurityTests.java b/src/test/java/org/elasticsearch/bootstrap/SecurityTests.java new file mode 100644 index 00000000000..d3a27f56b1f --- /dev/null +++ b/src/test/java/org/elasticsearch/bootstrap/SecurityTests.java @@ -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 org.elasticsearch.test.ElasticsearchTestCase; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.Collections; + +public class SecurityTests extends ElasticsearchTestCase { + + /** backslash escaping (e.g. windows paths) */ + public void testEncode() { + assertEquals("c:\\\\foobar", Security.encode("c:\\foobar")); + } + + /** test template processing */ + public void testTemplateProcessing() throws Exception { + Path path = createTempDir(); + + byte results[] = Security.createPermissions(Collections.singleton(path)); + String unicode = new String(results, StandardCharsets.UTF_8); + // try not to make this test too fragile or useless + assertTrue(unicode.contains("grant {")); + assertTrue(unicode.contains(Security.encode(path))); + assertTrue(unicode.contains("read")); + assertTrue(unicode.contains("write")); + } + +} From de109bdb3c59d03af33456e2690de31fb3290a82 Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Thu, 23 Apr 2015 22:15:16 -0400 Subject: [PATCH 8/9] Buffer this inputstream out of paranoia. guava copy goes byte-by-byte... --- src/main/java/org/elasticsearch/bootstrap/Security.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/elasticsearch/bootstrap/Security.java b/src/main/java/org/elasticsearch/bootstrap/Security.java index 7e5739761c7..7ac7e3b5e95 100644 --- a/src/main/java/org/elasticsearch/bootstrap/Security.java +++ b/src/main/java/org/elasticsearch/bootstrap/Security.java @@ -25,6 +25,7 @@ import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.StringHelper; import org.elasticsearch.env.Environment; +import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -72,7 +73,7 @@ class Security { Path processed = Files.createTempFile(null, null); try (OutputStream output = new BufferedOutputStream(Files.newOutputStream(processed))) { // copy the template as-is. - try (InputStream in = template) { + try (InputStream in = new BufferedInputStream(template)) { ByteStreams.copy(in, output); } From fca05edbd4fdf19884bd087007163a74922012dd Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Fri, 24 Apr 2015 09:51:01 -0400 Subject: [PATCH 9/9] add constant only used once to make it harder to read the code --- src/main/java/org/elasticsearch/bootstrap/Bootstrap.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java b/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java index 6d1652f2525..56643105dd5 100644 --- a/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java +++ b/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java @@ -92,8 +92,14 @@ public class Bootstrap { } } + /** + * option for elasticsearch.yml etc to turn off our security manager completely, + * for example if you want to have your own configuration or just disable. + */ + static final String SECURITY_SETTING = "security.manager.enabled"; + private void setupSecurity(Settings settings, Environment environment) throws Exception { - if (settings.getAsBoolean("security.manager.enabled", true)) { + if (settings.getAsBoolean(SECURITY_SETTING, true)) { Security.configure(environment); } }