Skip to content

Commit ae68e4f

Browse files
authored
Limit _FILE env var support to specific vars (#52525)
Closes #52503. Implement a list of `_FILE` env vars that will be used to populate env vars with file content, instead of processing all `_FILE` vars in the environment.
1 parent d793036 commit ae68e4f

File tree

3 files changed

+64
-55
lines changed

3 files changed

+64
-55
lines changed

distribution/src/bin/elasticsearch-env-from-file

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@ set -e -o pipefail
77
# point to it. This can be used to provide secrets to a container, without
88
# the values being specified explicitly when running the container.
99
#
10+
# Note that only supported environment variables are processed, in order
11+
# to avoid unexpected failures when an environment sets a "*_FILE" variable
12+
# that doesn't contain a filename.
13+
#
1014
# This script is intended to be sourced, not executed, and modifies the
1115
# environment.
1216

13-
for VAR_NAME_FILE in $(env | cut -f1 -d= | grep '_FILE$'); do
14-
if [[ -n "$VAR_NAME_FILE" ]]; then
17+
for VAR_NAME_FILE in ELASTIC_PASSWORD_FILE KEYSTORE_PASSWORD_FILE ; do
18+
if [[ -n "${!VAR_NAME_FILE}" ]]; then
1519
VAR_NAME="${VAR_NAME_FILE%_FILE}"
1620

1721
if env | grep "^${VAR_NAME}="; then

qa/os/src/test/java/org/elasticsearch/packaging/test/DockerTests.java

Lines changed: 20 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@
5656
import static org.elasticsearch.packaging.util.FileUtils.append;
5757
import static org.elasticsearch.packaging.util.FileUtils.getTempDir;
5858
import static org.elasticsearch.packaging.util.FileUtils.rm;
59-
import static org.elasticsearch.packaging.util.ServerUtils.makeRequest;
6059
import static org.hamcrest.Matchers.arrayWithSize;
6160
import static org.hamcrest.Matchers.containsString;
6261
import static org.hamcrest.Matchers.emptyString;
@@ -211,38 +210,10 @@ public void test071BindMountCustomPathWithDifferentUID() throws Exception {
211210
});
212211
}
213212

214-
/**
215-
* Check that environment variables can be populated by setting variables with the suffix "_FILE",
216-
* which point to files that hold the required values.
217-
*/
218-
public void test080SetEnvironmentVariablesUsingFiles() throws Exception {
219-
final String optionsFilename = "esJavaOpts.txt";
220-
221-
// ES_JAVA_OPTS_FILE
222-
Files.writeString(tempDir.resolve(optionsFilename), "-XX:-UseCompressedOops\n");
223-
224-
Map<String, String> envVars = Map.of("ES_JAVA_OPTS_FILE", "/run/secrets/" + optionsFilename);
225-
226-
// File permissions need to be secured in order for the ES wrapper to accept
227-
// them for populating env var values
228-
Files.setPosixFilePermissions(tempDir.resolve(optionsFilename), p600);
229-
230-
final Map<Path, Path> volumes = Map.of(tempDir, Path.of("/run/secrets"));
231-
232-
// Restart the container
233-
runContainer(distribution(), volumes, envVars);
234-
235-
waitForElasticsearch(installation);
236-
237-
final String nodesResponse = makeRequest(Request.Get("http://localhost:9200/_nodes"));
238-
239-
assertThat(nodesResponse, containsString("\"using_compressed_ordinary_object_pointers\":\"false\""));
240-
}
241-
242213
/**
243214
* Check that the elastic user's password can be configured via a file and the ELASTIC_PASSWORD_FILE environment variable.
244215
*/
245-
public void test081ConfigurePasswordThroughEnvironmentVariableFile() throws Exception {
216+
public void test080ConfigurePasswordThroughEnvironmentVariableFile() throws Exception {
246217
// Test relies on configuring security
247218
assumeTrue(distribution.isDefault());
248219

@@ -289,7 +260,7 @@ public void test081ConfigurePasswordThroughEnvironmentVariableFile() throws Exce
289260
* Check that when verifying the file permissions of _FILE environment variables, symlinks
290261
* are followed.
291262
*/
292-
public void test082SymlinksAreFollowedWithEnvironmentVariableFiles() throws Exception {
263+
public void test081SymlinksAreFollowedWithEnvironmentVariableFiles() throws Exception {
293264
// Test relies on configuring security
294265
assumeTrue(distribution.isDefault());
295266
// Test relies on symlinks
@@ -329,47 +300,42 @@ public void test082SymlinksAreFollowedWithEnvironmentVariableFiles() throws Exce
329300
/**
330301
* Check that environment variables cannot be used with _FILE environment variables.
331302
*/
332-
public void test083CannotUseEnvVarsAndFiles() throws Exception {
333-
final String optionsFilename = "esJavaOpts.txt";
303+
public void test082CannotUseEnvVarsAndFiles() throws Exception {
304+
final String passwordFilename = "password.txt";
334305

335-
// ES_JAVA_OPTS_FILE
336-
Files.writeString(tempDir.resolve(optionsFilename), "-XX:-UseCompressedOops\n");
306+
Files.writeString(tempDir.resolve(passwordFilename), "other_hunter2\n");
337307

338-
Map<String, String> envVars = Map.of(
339-
"ES_JAVA_OPTS",
340-
"-XX:+UseCompressedOops",
341-
"ES_JAVA_OPTS_FILE",
342-
"/run/secrets/" + optionsFilename
343-
);
308+
Map<String, String> envVars = new HashMap<>();
309+
envVars.put("ELASTIC_PASSWORD", "hunter2");
310+
envVars.put("ELASTIC_PASSWORD_FILE", "/run/secrets/" + passwordFilename);
344311

345312
// File permissions need to be secured in order for the ES wrapper to accept
346313
// them for populating env var values
347-
Files.setPosixFilePermissions(tempDir.resolve(optionsFilename), p600);
314+
Files.setPosixFilePermissions(tempDir.resolve(passwordFilename), p600);
348315

349316
final Map<Path, Path> volumes = Map.of(tempDir, Path.of("/run/secrets"));
350317

351318
final Result dockerLogs = runContainerExpectingFailure(distribution, volumes, envVars);
352319

353320
assertThat(
354321
dockerLogs.stderr,
355-
containsString("ERROR: Both ES_JAVA_OPTS_FILE and ES_JAVA_OPTS are set. These are mutually exclusive.")
322+
containsString("ERROR: Both ELASTIC_PASSWORD_FILE and ELASTIC_PASSWORD are set. These are mutually exclusive.")
356323
);
357324
}
358325

359326
/**
360327
* Check that when populating environment variables by setting variables with the suffix "_FILE",
361328
* the files' permissions are checked.
362329
*/
363-
public void test084EnvironmentVariablesUsingFilesHaveCorrectPermissions() throws Exception {
364-
final String optionsFilename = "esJavaOpts.txt";
330+
public void test083EnvironmentVariablesUsingFilesHaveCorrectPermissions() throws Exception {
331+
final String passwordFilename = "password.txt";
365332

366-
// ES_JAVA_OPTS_FILE
367-
Files.writeString(tempDir.resolve(optionsFilename), "-XX:-UseCompressedOops\n");
333+
Files.writeString(tempDir.resolve(passwordFilename), "hunter2\n");
368334

369-
Map<String, String> envVars = Map.of("ES_JAVA_OPTS_FILE", "/run/secrets/" + optionsFilename);
335+
Map<String, String> envVars = Map.of("ELASTIC_PASSWORD_FILE", "/run/secrets/" + passwordFilename);
370336

371337
// Set invalid file permissions
372-
Files.setPosixFilePermissions(tempDir.resolve(optionsFilename), p660);
338+
Files.setPosixFilePermissions(tempDir.resolve(passwordFilename), p660);
373339

374340
final Map<Path, Path> volumes = Map.of(tempDir, Path.of("/run/secrets"));
375341

@@ -378,15 +344,17 @@ public void test084EnvironmentVariablesUsingFilesHaveCorrectPermissions() throws
378344

379345
assertThat(
380346
dockerLogs.stderr,
381-
containsString("ERROR: File /run/secrets/" + optionsFilename + " from ES_JAVA_OPTS_FILE must have file permissions 400 or 600")
347+
containsString(
348+
"ERROR: File /run/secrets/" + passwordFilename + " from ELASTIC_PASSWORD_FILE must have file permissions 400 or 600"
349+
)
382350
);
383351
}
384352

385353
/**
386354
* Check that when verifying the file permissions of _FILE environment variables, symlinks
387355
* are followed, and that invalid target permissions are detected.
388356
*/
389-
public void test085SymlinkToFileWithInvalidPermissionsIsRejected() throws Exception {
357+
public void test084SymlinkToFileWithInvalidPermissionsIsRejected() throws Exception {
390358
// Test relies on configuring security
391359
assumeTrue(distribution.isDefault());
392360
// Test relies on symlinks
@@ -435,7 +403,7 @@ public void test085SymlinkToFileWithInvalidPermissionsIsRejected() throws Except
435403
* Check that environment variables are translated to -E options even for commands invoked under
436404
* `docker exec`, where the Docker image's entrypoint is not executed.
437405
*/
438-
public void test086EnvironmentVariablesAreRespectedUnderDockerExec() {
406+
public void test085EnvironmentVariablesAreRespectedUnderDockerExec() {
439407
// This test relies on a CLI tool attempting to connect to Elasticsearch, and the
440408
// tool in question is only in the default distribution.
441409
assumeTrue(distribution.isDefault());

qa/os/src/test/java/org/elasticsearch/packaging/test/KeystoreManagementTests.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import static org.elasticsearch.packaging.util.Docker.waitForPathToExist;
4848
import static org.elasticsearch.packaging.util.FileMatcher.Fileness.File;
4949
import static org.elasticsearch.packaging.util.FileMatcher.file;
50+
import static org.elasticsearch.packaging.util.FileMatcher.p600;
5051
import static org.elasticsearch.packaging.util.FileMatcher.p660;
5152
import static org.elasticsearch.packaging.util.FileUtils.getTempDir;
5253
import static org.elasticsearch.packaging.util.FileUtils.rm;
@@ -305,11 +306,47 @@ public void test60DockerEnvironmentVariablePassword() throws Exception {
305306
ServerUtils.runElasticsearchTests();
306307
}
307308

309+
/**
310+
* Check that we can mount a password-protected keystore to a docker image
311+
* and provide a password via a file, pointed at from an environment variable.
312+
*/
313+
public void test61DockerEnvironmentVariablePasswordFromFile() throws Exception {
314+
assumeTrue(distribution().isDocker());
315+
316+
Path tempDir = null;
317+
try {
318+
tempDir = Files.createTempDirectory(getTempDir(), DockerTests.class.getSimpleName());
319+
320+
String password = "password";
321+
String passwordFilename = "password.txt";
322+
Files.writeString(tempDir.resolve(passwordFilename), password + "\n");
323+
Files.setPosixFilePermissions(tempDir.resolve(passwordFilename), p600);
324+
325+
Path dockerKeystore = installation.config("elasticsearch.keystore");
326+
327+
Path localKeystoreFile = getKeystoreFileFromDockerContainer(password, dockerKeystore);
328+
329+
// restart ES with password and mounted keystore
330+
Map<Path, Path> volumes = Map.of(localKeystoreFile, dockerKeystore, tempDir, Path.of("/run/secrets"));
331+
Map<String, String> envVars = Map.of("KEYSTORE_PASSWORD_FILE", "/run/secrets/" + passwordFilename);
332+
333+
runContainer(distribution(), volumes, envVars);
334+
335+
waitForElasticsearch(installation);
336+
ServerUtils.runElasticsearchTests();
337+
}
338+
finally {
339+
if (tempDir != null) {
340+
rm(tempDir);
341+
}
342+
}
343+
}
344+
308345
/**
309346
* Check that if we provide the wrong password for a mounted and password-protected
310347
* keystore, Elasticsearch doesn't start.
311348
*/
312-
public void test61DockerEnvironmentVariableBadPassword() throws Exception {
349+
public void test62DockerEnvironmentVariableBadPassword() throws Exception {
313350
assumeTrue(distribution().isDocker());
314351
String password = "password";
315352
Path dockerKeystore = installation.config("elasticsearch.keystore");

0 commit comments

Comments
 (0)