Skip to content

Commit a0009f5

Browse files
author
David Roberts
committed
Change approach to pass temp directory to Environment
1 parent 04a9f69 commit a0009f5

File tree

9 files changed

+128
-16
lines changed

9 files changed

+128
-16
lines changed

core/src/main/java/org/elasticsearch/bootstrap/Bootstrap.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.elasticsearch.common.PidFile;
3636
import org.elasticsearch.common.SuppressForbidden;
3737
import org.elasticsearch.common.inject.CreationException;
38+
import org.elasticsearch.common.io.PathUtils;
3839
import org.elasticsearch.common.logging.ESLoggerFactory;
3940
import org.elasticsearch.common.logging.LogConfigurator;
4041
import org.elasticsearch.common.logging.Loggers;
@@ -43,6 +44,7 @@
4344
import org.elasticsearch.common.settings.SecureSettings;
4445
import org.elasticsearch.common.settings.Settings;
4546
import org.elasticsearch.common.transport.BoundTransportAddress;
47+
import org.elasticsearch.common.util.set.Sets;
4648
import org.elasticsearch.env.Environment;
4749
import org.elasticsearch.monitor.jvm.JvmInfo;
4850
import org.elasticsearch.monitor.os.OsProbe;
@@ -56,10 +58,14 @@
5658
import java.io.PrintStream;
5759
import java.io.UnsupportedEncodingException;
5860
import java.net.URISyntaxException;
61+
import java.nio.file.Files;
5962
import java.nio.file.Path;
63+
import java.nio.file.attribute.PosixFilePermission;
64+
import java.nio.file.attribute.PosixFilePermissions;
6065
import java.security.NoSuchAlgorithmException;
6166
import java.util.List;
6267
import java.util.Collections;
68+
import java.util.Set;
6369
import java.util.concurrent.CountDownLatch;
6470

6571
/**
@@ -239,12 +245,13 @@ static SecureSettings loadSecureSettings(Environment initialEnv) throws Bootstra
239245
return keystore;
240246
}
241247

248+
@SuppressForbidden(reason = "gets java.io.tmpdir")
242249
private static Environment createEnvironment(
243250
final boolean foreground,
244251
final Path pidFile,
245252
final SecureSettings secureSettings,
246253
final Settings initialSettings,
247-
final Path configPath) {
254+
final Path configPath) throws IOException {
248255
Terminal terminal = foreground ? Terminal.DEFAULT : null;
249256
Settings.Builder builder = Settings.builder();
250257
if (pidFile != null) {
@@ -254,7 +261,22 @@ private static Environment createEnvironment(
254261
if (secureSettings != null) {
255262
builder.setSecureSettings(secureSettings);
256263
}
257-
return InternalSettingsPreparer.prepareEnvironment(builder.build(), terminal, Collections.emptyMap(), configPath);
264+
return InternalSettingsPreparer.prepareEnvironment(builder.build(), terminal, Collections.emptyMap(), configPath,
265+
makeSecureTmpPath(PathUtils.get(System.getProperty("java.io.tmpdir"))));
266+
}
267+
268+
static Path makeSecureTmpPath(Path baseDir) throws IOException {
269+
try {
270+
// On POSIX file systems everyone can see the contents of the /tmp directory, so create a
271+
// sub-directory under it that only the user running Elasticsearch can list the contents of.
272+
Set<PosixFilePermission> attrs = Sets.newHashSet(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE,
273+
PosixFilePermission.OWNER_EXECUTE);
274+
return Files.createTempDirectory(baseDir, "elasticsearch", PosixFilePermissions.asFileAttribute(attrs));
275+
} catch (UnsupportedOperationException e) {
276+
// Assume this isn't a POSIX file system. On Windows each user's %TEMP% directory is visible only to
277+
// them and administrators, so the exact permissions of this sub-directory are less important.
278+
return Files.createTempDirectory(baseDir, "elasticsearch");
279+
}
258280
}
259281

260282
private void start() throws NodeValidationException {
@@ -285,8 +307,9 @@ static void init(
285307
INSTANCE = new Bootstrap();
286308

287309
final SecureSettings keystore = loadSecureSettings(initialEnv);
288-
final Environment environment = createEnvironment(foreground, pidFile, keystore, initialEnv.settings(), initialEnv.configFile());
310+
final Environment environment;
289311
try {
312+
environment = createEnvironment(foreground, pidFile, keystore, initialEnv.settings(), initialEnv.configFile());
290313
LogConfigurator.configure(environment);
291314
} catch (IOException e) {
292315
throw new BootstrapException(e);

core/src/main/java/org/elasticsearch/bootstrap/Security.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ static void addClasspathPermissions(Permissions policy) throws IOException {
268268
/**
269269
* Adds access to all configurable paths.
270270
*/
271+
@SuppressForbidden(reason = "gets java.io.tmpdir")
271272
static void addFilePermissions(Permissions policy, Environment environment) throws IOException {
272273
// read-only dirs
273274
addDirectoryPath(policy, Environment.PATH_HOME_SETTING.getKey(), environment.binFile(), "read,readlink");
@@ -276,7 +277,8 @@ static void addFilePermissions(Permissions policy, Environment environment) thro
276277
addDirectoryPath(policy, Environment.PATH_HOME_SETTING.getKey(), environment.pluginsFile(), "read,readlink");
277278
addDirectoryPath(policy, "path.conf'", environment.configFile(), "read,readlink");
278279
// read-write dirs
279-
addDirectoryPath(policy, "java.io.tmpdir", environment.tmpFile(), "read,readlink,write,delete");
280+
addDirectoryPath(policy, "java.io.tmpdir", PathUtils.get(System.getProperty("java.io.tmpdir")),
281+
"read,readlink,write,delete");
280282
addDirectoryPath(policy, Environment.PATH_LOGS_SETTING.getKey(), environment.logsFile(), "read,readlink,write,delete");
281283
if (environment.sharedDataFile() != null) {
282284
addDirectoryPath(policy, Environment.PATH_SHARED_DATA_SETTING.getKey(), environment.sharedDataFile(),

core/src/main/java/org/elasticsearch/cli/EnvironmentAwareCommand.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import joptsimple.OptionSpec;
2424
import joptsimple.util.KeyValuePair;
2525
import org.elasticsearch.common.SuppressForbidden;
26+
import org.elasticsearch.common.io.PathUtils;
2627
import org.elasticsearch.common.settings.Settings;
2728
import org.elasticsearch.env.Environment;
2829
import org.elasticsearch.node.InternalSettingsPreparer;
@@ -70,12 +71,14 @@ protected void execute(Terminal terminal, OptionSet options) throws Exception {
7071
}
7172

7273
/** Create an {@link Environment} for the command to use. Overrideable for tests. */
74+
@SuppressForbidden(reason = "gets java.io.tmpdir")
7375
protected Environment createEnv(final Terminal terminal, final Map<String, String> settings) throws UserException {
7476
final String esPathConf = System.getProperty("es.path.conf");
7577
if (esPathConf == null) {
7678
throw new UserException(ExitCodes.CONFIG, "the system property [es.path.conf] must be set");
7779
}
78-
return InternalSettingsPreparer.prepareEnvironment(Settings.EMPTY, terminal, settings, getConfigPath(esPathConf));
80+
return InternalSettingsPreparer.prepareEnvironment(Settings.EMPTY, terminal, settings, getConfigPath(esPathConf),
81+
PathUtils.get(System.getProperty("java.io.tmpdir")));
7982
}
8083

8184
@SuppressForbidden(reason = "need path to construct environment")

core/src/main/java/org/elasticsearch/env/Environment.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,20 @@ public class Environment {
8282
/** Path to the PID file (can be null if no PID file is configured) **/
8383
private final Path pidFile;
8484

85-
/** Path to the temporary file directory used by the JDK */
86-
private final Path tmpFile = PathUtils.get(System.getProperty("java.io.tmpdir"));
85+
/** Path to the temporary file directory */
86+
private final Path tmpFile;
8787

88+
/** Convenience method for tests - do not use in production code */
8889
public Environment(Settings settings) {
8990
this(settings, null);
9091
}
9192

92-
public Environment(final Settings settings, final Path configPath) {
93+
/** Convenience method for tests - do not use in production code */
94+
public Environment(Settings settings, Path configPath) {
95+
this(settings, configPath, PathUtils.get(System.getProperty("java.io.tmpdir")));
96+
}
97+
98+
public Environment(final Settings settings, final Path configPath, final Path tmpPath) {
9399
final Path homeFile;
94100
if (PATH_HOME_SETTING.exists(settings)) {
95101
homeFile = PathUtils.get(PATH_HOME_SETTING.get(settings)).normalize();
@@ -103,6 +109,8 @@ public Environment(final Settings settings, final Path configPath) {
103109
configFile = homeFile.resolve("config");
104110
}
105111

112+
tmpFile = Objects.requireNonNull(tmpPath);
113+
106114
pluginsFile = homeFile.resolve("plugins");
107115

108116
List<String> dataPaths = PATH_DATA_SETTING.get(settings);

core/src/main/java/org/elasticsearch/node/InternalSettingsPreparer.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131
import org.elasticsearch.cli.Terminal;
3232
import org.elasticsearch.cluster.ClusterName;
3333
import org.elasticsearch.common.Strings;
34+
import org.elasticsearch.common.SuppressForbidden;
3435
import org.elasticsearch.common.collect.Tuple;
36+
import org.elasticsearch.common.io.PathUtils;
3537
import org.elasticsearch.common.settings.Settings;
3638
import org.elasticsearch.common.settings.SettingsException;
3739
import org.elasticsearch.env.Environment;
@@ -62,8 +64,10 @@ public static Settings prepareSettings(Settings input) {
6264
* @param terminal the Terminal to use for input/output
6365
* @return the {@link Settings} and {@link Environment} as a {@link Tuple}
6466
*/
67+
@SuppressForbidden(reason = "gets java.io.tmpdir")
6568
public static Environment prepareEnvironment(Settings input, Terminal terminal) {
66-
return prepareEnvironment(input, terminal, Collections.emptyMap(), null);
69+
return prepareEnvironment(input, terminal, Collections.emptyMap(), null,
70+
PathUtils.get(System.getProperty("java.io.tmpdir")));
6771
}
6872

6973
/**
@@ -76,13 +80,15 @@ public static Environment prepareEnvironment(Settings input, Terminal terminal)
7680
* @param terminal the Terminal to use for input/output
7781
* @param properties map of properties key/value pairs (usually from the command-line)
7882
* @param configPath path to config directory; (use null to indicate the default)
83+
* @param tmpPath path to use for temporary files
7984
* @return the {@link Settings} and {@link Environment} as a {@link Tuple}
8085
*/
81-
public static Environment prepareEnvironment(Settings input, Terminal terminal, Map<String, String> properties, Path configPath) {
86+
public static Environment prepareEnvironment(Settings input, Terminal terminal, Map<String, String> properties, Path configPath,
87+
Path tmpPath) {
8288
// just create enough settings to build the environment, to get the config dir
8389
Settings.Builder output = Settings.builder();
8490
initializeSettings(output, input, properties);
85-
Environment environment = new Environment(output.build(), configPath);
91+
Environment environment = new Environment(output.build(), configPath, tmpPath);
8692

8793
if (Files.exists(environment.configFile().resolve("elasticsearch.yaml"))) {
8894
throw new SettingsException("elasticsearch.yaml was deprecated in 5.5.0 and must be renamed to elasticsearch.yml");
@@ -106,11 +112,11 @@ public static Environment prepareEnvironment(Settings input, Terminal terminal,
106112
initializeSettings(output, input, properties);
107113
finalizeSettings(output, terminal);
108114

109-
environment = new Environment(output.build(), configPath);
115+
environment = new Environment(output.build(), configPath, tmpPath);
110116

111117
// we put back the path.logs so we can use it in the logging configuration file
112118
output.put(Environment.PATH_LOGS_SETTING.getKey(), environment.logsFile().toAbsolutePath().normalize().toString());
113-
return new Environment(output.build(), configPath);
119+
return new Environment(output.build(), configPath, tmpPath);
114120
}
115121

116122
/**

core/src/main/java/org/elasticsearch/node/Node.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ protected Node(final Environment environment, Collection<Class<? extends Plugin>
305305

306306
// create the environment based on the finalized (processed) view of the settings
307307
// this is just to makes sure that people get the same settings, no matter where they ask them from
308-
this.environment = new Environment(this.settings, environment.configFile());
308+
this.environment = new Environment(this.settings, environment.configFile(), environment.tmpFile());
309309
Environment.assertEquivalent(environment, this.environment);
310310

311311
final List<ExecutorBuilder<?>> executorBuilders = pluginsService.getExecutorBuilders(settings);

core/src/test/java/org/elasticsearch/node/InternalSettingsPreparerTests.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.elasticsearch.cli.MockTerminal;
2323
import org.elasticsearch.cluster.ClusterName;
24+
import org.elasticsearch.common.io.PathUtils;
2425
import org.elasticsearch.common.settings.MockSecureSettings;
2526
import org.elasticsearch.common.settings.SecureSetting;
2627
import org.elasticsearch.common.settings.SecureString;
@@ -181,7 +182,8 @@ public void testSecureSettings() {
181182

182183
public void testDefaultPropertiesDoNothing() throws Exception {
183184
Map<String, String> props = Collections.singletonMap("default.setting", "foo");
184-
Environment env = InternalSettingsPreparer.prepareEnvironment(baseEnvSettings, null, props, null);
185+
Environment env = InternalSettingsPreparer.prepareEnvironment(baseEnvSettings, null, props, null,
186+
PathUtils.get(System.getProperty("java.io.tmpdir")));
185187
assertEquals("foo", env.settings().get("default.setting"));
186188
assertNull(env.settings().get("setting"));
187189
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.bootstrap;
20+
21+
import org.elasticsearch.test.ESTestCase;
22+
import org.junit.BeforeClass;
23+
24+
import java.io.IOException;
25+
import java.nio.file.Files;
26+
import java.nio.file.Path;
27+
import java.nio.file.attribute.PosixFileAttributeView;
28+
import java.nio.file.attribute.PosixFilePermission;
29+
import java.util.Set;
30+
31+
import static org.hamcrest.Matchers.contains;
32+
import static org.hamcrest.Matchers.containsInAnyOrder;
33+
import static org.hamcrest.Matchers.not;
34+
35+
/**
36+
* This has to be an evil test because it checks file permissions, and security manager does not allow that.
37+
*/
38+
public class EvilBootstrapTests extends ESTestCase {
39+
40+
private static boolean isPosix;
41+
42+
@BeforeClass
43+
public static void checkPosix() throws IOException {
44+
isPosix = Files.getFileAttributeView(createTempFile(), PosixFileAttributeView.class) != null;
45+
}
46+
47+
public void testTmpDirPerms() throws IOException {
48+
// %TEMP% directories are restricted by default on Windows, so in this case it doesn't
49+
// really matter what permissions the directory we create beneath java.io.tmpdir have.
50+
// And if the user has changed java.io.tmpdir then they should take responsibility for
51+
// ensuring the chosen location is as secure as they require.
52+
assumeTrue("Temp directory permissions not set on Windows", isPosix);
53+
54+
final Path tmpPath = Bootstrap.makeSecureTmpPath(createTempDir());
55+
56+
Set<PosixFilePermission> perms = Files.getPosixFilePermissions(tmpPath);
57+
assertThat(perms,
58+
containsInAnyOrder(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE));
59+
assertThat(perms, not(contains(PosixFilePermission.GROUP_READ)));
60+
assertThat(perms, not(contains(PosixFilePermission.GROUP_WRITE)));
61+
assertThat(perms, not(contains(PosixFilePermission.GROUP_EXECUTE)));
62+
assertThat(perms, not(contains(PosixFilePermission.OTHERS_READ)));
63+
assertThat(perms, not(contains(PosixFilePermission.OTHERS_WRITE)));
64+
assertThat(perms, not(contains(PosixFilePermission.OTHERS_EXECUTE)));
65+
}
66+
}

test/framework/src/main/java/org/elasticsearch/node/MockNode.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.elasticsearch.cluster.MockInternalClusterInfoService;
2626
import org.elasticsearch.cluster.node.DiscoveryNode;
2727
import org.elasticsearch.cluster.service.ClusterService;
28+
import org.elasticsearch.common.io.PathUtils;
2829
import org.elasticsearch.common.settings.ClusterSettings;
2930
import org.elasticsearch.common.settings.Settings;
3031
import org.elasticsearch.common.transport.BoundTransportAddress;
@@ -66,7 +67,8 @@ public MockNode(Settings settings, Collection<Class<? extends Plugin>> classpath
6667
}
6768

6869
public MockNode(Settings settings, Collection<Class<? extends Plugin>> classpathPlugins, Path configPath) {
69-
this(InternalSettingsPreparer.prepareEnvironment(settings, null, Collections.emptyMap(), configPath), classpathPlugins);
70+
this(InternalSettingsPreparer.prepareEnvironment(settings, null, Collections.emptyMap(), configPath,
71+
PathUtils.get(System.getProperty("java.io.tmpdir"))), classpathPlugins);
7072
}
7173

7274
public MockNode(Environment environment, Collection<Class<? extends Plugin>> classpathPlugins) {

0 commit comments

Comments
 (0)