Limit _FILE env var support to specific vars (#52645)
Backport of #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.
This commit is contained in:
parent
376932a47d
commit
ce7ebb2d39
|
@ -7,11 +7,15 @@ set -e -o pipefail
|
||||||
# point to it. This can be used to provide secrets to a container, without
|
# point to it. This can be used to provide secrets to a container, without
|
||||||
# the values being specified explicitly when running the container.
|
# the values being specified explicitly when running the container.
|
||||||
#
|
#
|
||||||
|
# Note that only supported environment variables are processed, in order
|
||||||
|
# to avoid unexpected failures when an environment sets a "*_FILE" variable
|
||||||
|
# that doesn't contain a filename.
|
||||||
|
#
|
||||||
# This script is intended to be sourced, not executed, and modifies the
|
# This script is intended to be sourced, not executed, and modifies the
|
||||||
# environment.
|
# environment.
|
||||||
|
|
||||||
for VAR_NAME_FILE in $(env | cut -f1 -d= | grep '_FILE$'); do
|
for VAR_NAME_FILE in ELASTIC_PASSWORD_FILE KEYSTORE_PASSWORD_FILE ; do
|
||||||
if [[ -n "$VAR_NAME_FILE" ]]; then
|
if [[ -n "${!VAR_NAME_FILE}" ]]; then
|
||||||
VAR_NAME="${VAR_NAME_FILE%_FILE}"
|
VAR_NAME="${VAR_NAME_FILE%_FILE}"
|
||||||
|
|
||||||
if env | grep "^${VAR_NAME}="; then
|
if env | grep "^${VAR_NAME}="; then
|
||||||
|
|
|
@ -57,7 +57,6 @@ import static org.elasticsearch.packaging.util.FileMatcher.p775;
|
||||||
import static org.elasticsearch.packaging.util.FileUtils.append;
|
import static org.elasticsearch.packaging.util.FileUtils.append;
|
||||||
import static org.elasticsearch.packaging.util.FileUtils.getTempDir;
|
import static org.elasticsearch.packaging.util.FileUtils.getTempDir;
|
||||||
import static org.elasticsearch.packaging.util.FileUtils.rm;
|
import static org.elasticsearch.packaging.util.FileUtils.rm;
|
||||||
import static org.elasticsearch.packaging.util.ServerUtils.makeRequest;
|
|
||||||
import static org.hamcrest.Matchers.arrayWithSize;
|
import static org.hamcrest.Matchers.arrayWithSize;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.emptyString;
|
import static org.hamcrest.Matchers.emptyString;
|
||||||
|
@ -219,38 +218,10 @@ public class DockerTests extends PackagingTestCase {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check that environment variables can be populated by setting variables with the suffix "_FILE",
|
|
||||||
* which point to files that hold the required values.
|
|
||||||
*/
|
|
||||||
public void test080SetEnvironmentVariablesUsingFiles() throws Exception {
|
|
||||||
final String optionsFilename = "esJavaOpts.txt";
|
|
||||||
|
|
||||||
// ES_JAVA_OPTS_FILE
|
|
||||||
append(tempDir.resolve(optionsFilename), "-XX:-UseCompressedOops\n");
|
|
||||||
|
|
||||||
Map<String, String> envVars = singletonMap("ES_JAVA_OPTS_FILE", "/run/secrets/" + optionsFilename);
|
|
||||||
|
|
||||||
// File permissions need to be secured in order for the ES wrapper to accept
|
|
||||||
// them for populating env var values
|
|
||||||
Files.setPosixFilePermissions(tempDir.resolve(optionsFilename), p600);
|
|
||||||
|
|
||||||
final Map<Path, Path> volumes = singletonMap(tempDir, Paths.get("/run/secrets"));
|
|
||||||
|
|
||||||
// Restart the container
|
|
||||||
runContainer(distribution(), volumes, envVars);
|
|
||||||
|
|
||||||
waitForElasticsearch(installation);
|
|
||||||
|
|
||||||
final String nodesResponse = makeRequest(Request.Get("http://localhost:9200/_nodes"));
|
|
||||||
|
|
||||||
assertThat(nodesResponse, containsString("\"using_compressed_ordinary_object_pointers\":\"false\""));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that the elastic user's password can be configured via a file and the ELASTIC_PASSWORD_FILE environment variable.
|
* Check that the elastic user's password can be configured via a file and the ELASTIC_PASSWORD_FILE environment variable.
|
||||||
*/
|
*/
|
||||||
public void test081ConfigurePasswordThroughEnvironmentVariableFile() throws Exception {
|
public void test080ConfigurePasswordThroughEnvironmentVariableFile() throws Exception {
|
||||||
// Test relies on configuring security
|
// Test relies on configuring security
|
||||||
assumeTrue(distribution.isDefault());
|
assumeTrue(distribution.isDefault());
|
||||||
|
|
||||||
|
@ -293,7 +264,7 @@ public class DockerTests extends PackagingTestCase {
|
||||||
* Check that when verifying the file permissions of _FILE environment variables, symlinks
|
* Check that when verifying the file permissions of _FILE environment variables, symlinks
|
||||||
* are followed.
|
* are followed.
|
||||||
*/
|
*/
|
||||||
public void test082SymlinksAreFollowedWithEnvironmentVariableFiles() throws Exception {
|
public void test081SymlinksAreFollowedWithEnvironmentVariableFiles() throws Exception {
|
||||||
// Test relies on configuring security
|
// Test relies on configuring security
|
||||||
assumeTrue(distribution.isDefault());
|
assumeTrue(distribution.isDefault());
|
||||||
// Test relies on symlinks
|
// Test relies on symlinks
|
||||||
|
@ -330,19 +301,18 @@ public class DockerTests extends PackagingTestCase {
|
||||||
/**
|
/**
|
||||||
* Check that environment variables cannot be used with _FILE environment variables.
|
* Check that environment variables cannot be used with _FILE environment variables.
|
||||||
*/
|
*/
|
||||||
public void test083CannotUseEnvVarsAndFiles() throws Exception {
|
public void test082CannotUseEnvVarsAndFiles() throws Exception {
|
||||||
final String optionsFilename = "esJavaOpts.txt";
|
final String passwordFilename = "password.txt";
|
||||||
|
|
||||||
// ES_JAVA_OPTS_FILE
|
Files.write(tempDir.resolve(passwordFilename), "other_hunter2\n".getBytes(StandardCharsets.UTF_8));
|
||||||
append(tempDir.resolve(optionsFilename), "-XX:-UseCompressedOops\n");
|
|
||||||
|
|
||||||
Map<String, String> envVars = new HashMap<>();
|
Map<String, String> envVars = new HashMap<>();
|
||||||
envVars.put("ES_JAVA_OPTS", "-XX:+UseCompressedOops");
|
envVars.put("ELASTIC_PASSWORD", "hunter2");
|
||||||
envVars.put("ES_JAVA_OPTS_FILE", "/run/secrets/" + optionsFilename);
|
envVars.put("ELASTIC_PASSWORD_FILE", "/run/secrets/" + passwordFilename);
|
||||||
|
|
||||||
// File permissions need to be secured in order for the ES wrapper to accept
|
// File permissions need to be secured in order for the ES wrapper to accept
|
||||||
// them for populating env var values
|
// them for populating env var values
|
||||||
Files.setPosixFilePermissions(tempDir.resolve(optionsFilename), p600);
|
Files.setPosixFilePermissions(tempDir.resolve(passwordFilename), p600);
|
||||||
|
|
||||||
final Map<Path, Path> volumes = singletonMap(tempDir, Paths.get("/run/secrets"));
|
final Map<Path, Path> volumes = singletonMap(tempDir, Paths.get("/run/secrets"));
|
||||||
|
|
||||||
|
@ -350,7 +320,7 @@ public class DockerTests extends PackagingTestCase {
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
dockerLogs.stderr,
|
dockerLogs.stderr,
|
||||||
containsString("ERROR: Both ES_JAVA_OPTS_FILE and ES_JAVA_OPTS are set. These are mutually " + "exclusive.")
|
containsString("ERROR: Both ELASTIC_PASSWORD_FILE and ELASTIC_PASSWORD are set. These are mutually exclusive.")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,16 +328,16 @@ public class DockerTests extends PackagingTestCase {
|
||||||
* Check that when populating environment variables by setting variables with the suffix "_FILE",
|
* Check that when populating environment variables by setting variables with the suffix "_FILE",
|
||||||
* the files' permissions are checked.
|
* the files' permissions are checked.
|
||||||
*/
|
*/
|
||||||
public void test084EnvironmentVariablesUsingFilesHaveCorrectPermissions() throws Exception {
|
public void test083EnvironmentVariablesUsingFilesHaveCorrectPermissions() throws Exception {
|
||||||
final String optionsFilename = "esJavaOpts.txt";
|
final String passwordFilename = "password.txt";
|
||||||
|
|
||||||
// ES_JAVA_OPTS_FILE
|
Files.write(tempDir.resolve(passwordFilename), "hunter2\n".getBytes(StandardCharsets.UTF_8));
|
||||||
append(tempDir.resolve(optionsFilename), "-XX:-UseCompressedOops\n");
|
|
||||||
|
|
||||||
Map<String, String> envVars = singletonMap("ES_JAVA_OPTS_FILE", "/run/secrets/" + optionsFilename);
|
Map<String, String> envVars = new HashMap<>();
|
||||||
|
envVars.put("ELASTIC_PASSWORD_FILE", "/run/secrets/" + passwordFilename);
|
||||||
|
|
||||||
// Set invalid file permissions
|
// Set invalid file permissions
|
||||||
Files.setPosixFilePermissions(tempDir.resolve(optionsFilename), p660);
|
Files.setPosixFilePermissions(tempDir.resolve(passwordFilename), p660);
|
||||||
|
|
||||||
final Map<Path, Path> volumes = singletonMap(tempDir, Paths.get("/run/secrets"));
|
final Map<Path, Path> volumes = singletonMap(tempDir, Paths.get("/run/secrets"));
|
||||||
|
|
||||||
|
@ -377,7 +347,7 @@ public class DockerTests extends PackagingTestCase {
|
||||||
assertThat(
|
assertThat(
|
||||||
dockerLogs.stderr,
|
dockerLogs.stderr,
|
||||||
containsString(
|
containsString(
|
||||||
"ERROR: File /run/secrets/" + optionsFilename + " from ES_JAVA_OPTS_FILE must have " + "file permissions 400 or 600"
|
"ERROR: File /run/secrets/" + passwordFilename + " from ELASTIC_PASSWORD_FILE must have file permissions 400 or 600"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -386,7 +356,7 @@ public class DockerTests extends PackagingTestCase {
|
||||||
* Check that when verifying the file permissions of _FILE environment variables, symlinks
|
* Check that when verifying the file permissions of _FILE environment variables, symlinks
|
||||||
* are followed, and that invalid target permissions are detected.
|
* are followed, and that invalid target permissions are detected.
|
||||||
*/
|
*/
|
||||||
public void test085SymlinkToFileWithInvalidPermissionsIsRejected() throws Exception {
|
public void test084SymlinkToFileWithInvalidPermissionsIsRejected() throws Exception {
|
||||||
// Test relies on configuring security
|
// Test relies on configuring security
|
||||||
assumeTrue(distribution.isDefault());
|
assumeTrue(distribution.isDefault());
|
||||||
// Test relies on symlinks
|
// Test relies on symlinks
|
||||||
|
@ -432,7 +402,7 @@ public class DockerTests extends PackagingTestCase {
|
||||||
* Check that environment variables are translated to -E options even for commands invoked under
|
* Check that environment variables are translated to -E options even for commands invoked under
|
||||||
* `docker exec`, where the Docker image's entrypoint is not executed.
|
* `docker exec`, where the Docker image's entrypoint is not executed.
|
||||||
*/
|
*/
|
||||||
public void test086EnvironmentVariablesAreRespectedUnderDockerExec() {
|
public void test085EnvironmentVariablesAreRespectedUnderDockerExec() {
|
||||||
// This test relies on a CLI tool attempting to connect to Elasticsearch, and the
|
// This test relies on a CLI tool attempting to connect to Elasticsearch, and the
|
||||||
// tool in question is only in the default distribution.
|
// tool in question is only in the default distribution.
|
||||||
assumeTrue(distribution.isDefault());
|
assumeTrue(distribution.isDefault());
|
||||||
|
|
|
@ -49,6 +49,7 @@ import static org.elasticsearch.packaging.util.Docker.waitForElasticsearch;
|
||||||
import static org.elasticsearch.packaging.util.Docker.waitForPathToExist;
|
import static org.elasticsearch.packaging.util.Docker.waitForPathToExist;
|
||||||
import static org.elasticsearch.packaging.util.FileMatcher.Fileness.File;
|
import static org.elasticsearch.packaging.util.FileMatcher.Fileness.File;
|
||||||
import static org.elasticsearch.packaging.util.FileMatcher.file;
|
import static org.elasticsearch.packaging.util.FileMatcher.file;
|
||||||
|
import static org.elasticsearch.packaging.util.FileMatcher.p600;
|
||||||
import static org.elasticsearch.packaging.util.FileMatcher.p660;
|
import static org.elasticsearch.packaging.util.FileMatcher.p660;
|
||||||
import static org.elasticsearch.packaging.util.FileUtils.getTempDir;
|
import static org.elasticsearch.packaging.util.FileUtils.getTempDir;
|
||||||
import static org.elasticsearch.packaging.util.FileUtils.rm;
|
import static org.elasticsearch.packaging.util.FileUtils.rm;
|
||||||
|
@ -309,11 +310,51 @@ public class KeystoreManagementTests extends PackagingTestCase {
|
||||||
ServerUtils.runElasticsearchTests();
|
ServerUtils.runElasticsearchTests();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that we can mount a password-protected keystore to a docker image
|
||||||
|
* and provide a password via a file, pointed at from an environment variable.
|
||||||
|
*/
|
||||||
|
public void test61DockerEnvironmentVariablePasswordFromFile() throws Exception {
|
||||||
|
assumeTrue(distribution().isDocker());
|
||||||
|
|
||||||
|
Path tempDir = null;
|
||||||
|
try {
|
||||||
|
tempDir = Files.createTempDirectory(getTempDir(), DockerTests.class.getSimpleName());
|
||||||
|
|
||||||
|
String password = "password";
|
||||||
|
String passwordFilename = "password.txt";
|
||||||
|
Files.write(tempDir.resolve(passwordFilename), (password + "\n").getBytes(StandardCharsets.UTF_8));
|
||||||
|
Files.setPosixFilePermissions(tempDir.resolve(passwordFilename), p600);
|
||||||
|
|
||||||
|
Path dockerKeystore = installation.config("elasticsearch.keystore");
|
||||||
|
|
||||||
|
Path localKeystoreFile = getKeystoreFileFromDockerContainer(password, dockerKeystore);
|
||||||
|
|
||||||
|
// restart ES with password and mounted keystore
|
||||||
|
Map<Path, Path> volumes = new HashMap<>();
|
||||||
|
volumes.put(localKeystoreFile, dockerKeystore);
|
||||||
|
volumes.put(tempDir, Paths.get("/run/secrets"));
|
||||||
|
|
||||||
|
Map<String, String> envVars = new HashMap<>();
|
||||||
|
envVars.put("KEYSTORE_PASSWORD_FILE", "/run/secrets/" + passwordFilename);
|
||||||
|
|
||||||
|
runContainer(distribution(), volumes, envVars);
|
||||||
|
|
||||||
|
waitForElasticsearch(installation);
|
||||||
|
ServerUtils.runElasticsearchTests();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (tempDir != null) {
|
||||||
|
rm(tempDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that if we provide the wrong password for a mounted and password-protected
|
* Check that if we provide the wrong password for a mounted and password-protected
|
||||||
* keystore, Elasticsearch doesn't start.
|
* keystore, Elasticsearch doesn't start.
|
||||||
*/
|
*/
|
||||||
public void test61DockerEnvironmentVariableBadPassword() throws Exception {
|
public void test62DockerEnvironmentVariableBadPassword() throws Exception {
|
||||||
assumeTrue(distribution().isDocker());
|
assumeTrue(distribution().isDocker());
|
||||||
String password = "password";
|
String password = "password";
|
||||||
Path dockerKeystore = installation.config("elasticsearch.keystore");
|
Path dockerKeystore = installation.config("elasticsearch.keystore");
|
||||||
|
|
Loading…
Reference in New Issue