|
28 | 28 | import org.elasticsearch.packaging.util.Shell; |
29 | 29 | import org.junit.Ignore; |
30 | 30 |
|
| 31 | +import java.io.IOException; |
31 | 32 | import java.nio.charset.StandardCharsets; |
32 | 33 | import java.nio.file.Files; |
33 | 34 | import java.nio.file.Path; |
34 | 35 | import java.nio.file.StandardOpenOption; |
| 36 | +import java.util.Map; |
35 | 37 |
|
36 | 38 | import static org.elasticsearch.packaging.util.Archives.ARCHIVE_OWNER; |
37 | 39 | import static org.elasticsearch.packaging.util.Archives.installArchive; |
38 | 40 | import static org.elasticsearch.packaging.util.Archives.verifyArchiveInstallation; |
39 | 41 | import static org.elasticsearch.packaging.util.Docker.assertPermissionsAndOwnership; |
| 42 | +import static org.elasticsearch.packaging.util.Docker.runContainer; |
| 43 | +import static org.elasticsearch.packaging.util.Docker.runContainerExpectingFailure; |
| 44 | +import static org.elasticsearch.packaging.util.Docker.waitForElasticsearch; |
40 | 45 | import static org.elasticsearch.packaging.util.Docker.waitForPathToExist; |
41 | 46 | import static org.elasticsearch.packaging.util.FileMatcher.Fileness.File; |
42 | 47 | import static org.elasticsearch.packaging.util.FileMatcher.file; |
43 | 48 | import static org.elasticsearch.packaging.util.FileMatcher.p660; |
| 49 | +import static org.elasticsearch.packaging.util.FileUtils.getTempDir; |
44 | 50 | import static org.elasticsearch.packaging.util.FileUtils.rm; |
45 | 51 | import static org.elasticsearch.packaging.util.Packages.assertInstalled; |
46 | 52 | import static org.elasticsearch.packaging.util.Packages.assertRemoved; |
@@ -253,6 +259,83 @@ public void test51WrongKeystorePasswordFromFile() throws Exception { |
253 | 259 | } |
254 | 260 | } |
255 | 261 |
|
| 262 | + /** |
| 263 | + * Check that we can mount a password-protected keystore to a docker image |
| 264 | + * and provide a password via an environment variable. |
| 265 | + */ |
| 266 | + public void test60DockerEnvironmentVariablePassword() throws Exception { |
| 267 | + assumeTrue(distribution().isDocker()); |
| 268 | + String password = "password"; |
| 269 | + Path dockerKeystore = installation.config("elasticsearch.keystore"); |
| 270 | + |
| 271 | + Path localKeystoreFile = getKeystoreFileFromDockerContainer(password, dockerKeystore); |
| 272 | + |
| 273 | + // restart ES with password and mounted keystore |
| 274 | + Map<Path, Path> volumes = Map.of(localKeystoreFile, dockerKeystore); |
| 275 | + Map<String, String> envVars = Map.of("KEYSTORE_PASSWORD", password); |
| 276 | + runContainer(distribution(), volumes, envVars); |
| 277 | + waitForElasticsearch(installation); |
| 278 | + ServerUtils.runElasticsearchTests(); |
| 279 | + } |
| 280 | + |
| 281 | + /** |
| 282 | + * Check that if we provide the wrong password for a mounted and password-protected |
| 283 | + * keystore, Elasticsearch doesn't start. |
| 284 | + */ |
| 285 | + public void test61DockerEnvironmentVariableBadPassword() throws Exception { |
| 286 | + assumeTrue(distribution().isDocker()); |
| 287 | + String password = "password"; |
| 288 | + Path dockerKeystore = installation.config("elasticsearch.keystore"); |
| 289 | + |
| 290 | + Path localKeystoreFile = getKeystoreFileFromDockerContainer(password, dockerKeystore); |
| 291 | + |
| 292 | + // restart ES with password and mounted keystore |
| 293 | + Map<Path, Path> volumes = Map.of(localKeystoreFile, dockerKeystore); |
| 294 | + Map<String, String> envVars = Map.of("KEYSTORE_PASSWORD", "wrong"); |
| 295 | + Shell.Result r = runContainerExpectingFailure(distribution(), volumes, envVars); |
| 296 | + assertThat(r.stderr, containsString(PASSWORD_ERROR_MESSAGE)); |
| 297 | + } |
| 298 | + |
| 299 | + /** |
| 300 | + * In the Docker context, it's a little bit tricky to get a password-protected |
| 301 | + * keystore. All of the utilities we'd want to use are on the Docker image. |
| 302 | + * This method mounts a temporary directory to a Docker container, password-protects |
| 303 | + * the keystore, and then returns the path of the file that appears in the |
| 304 | + * mounted directory (now accessible from the local filesystem). |
| 305 | + */ |
| 306 | + private Path getKeystoreFileFromDockerContainer(String password, Path dockerKeystore) throws IOException { |
| 307 | + // Mount a temporary directory for copying the keystore |
| 308 | + Path dockerTemp = Path.of("/usr/tmp/keystore-tmp"); |
| 309 | + Path tempDirectory = Files.createTempDirectory(getTempDir(), KeystoreManagementTests.class.getSimpleName()); |
| 310 | + Map<Path, Path> volumes = Map.of(tempDirectory, dockerTemp); |
| 311 | + |
| 312 | + // It's very tricky to properly quote a pipeline that you're passing to |
| 313 | + // a docker exec command, so we're just going to put a small script in the |
| 314 | + // temp folder. |
| 315 | + String setPasswordScript = "echo \"" + password + "\n" + password |
| 316 | + + "\n\" | " + installation.executables().keystoreTool.toString() + " passwd"; |
| 317 | + Files.writeString(tempDirectory.resolve("set-pass.sh"), setPasswordScript); |
| 318 | + |
| 319 | + runContainer(distribution(), volumes, null); |
| 320 | + try { |
| 321 | + waitForPathToExist(dockerTemp); |
| 322 | + waitForPathToExist(dockerKeystore); |
| 323 | + } catch (InterruptedException e) { |
| 324 | + throw new RuntimeException(e); |
| 325 | + } |
| 326 | + |
| 327 | + // We need a local shell to put the correct permissions on our mounted directory. |
| 328 | + Shell localShell = new Shell(); |
| 329 | + localShell.run("docker exec --tty " + Docker.getContainerId() + " chown elasticsearch:root " + dockerTemp); |
| 330 | + localShell.run("docker exec --tty " + Docker.getContainerId() + " chown elasticsearch:root " + dockerTemp.resolve("set-pass.sh")); |
| 331 | + |
| 332 | + sh.run("bash " + dockerTemp.resolve("set-pass.sh")); |
| 333 | + |
| 334 | + // copy keystore to temp file to make it available to docker host |
| 335 | + sh.run("cp " + dockerKeystore + " " + dockerTemp); |
| 336 | + return tempDirectory.resolve("elasticsearch.keystore"); |
| 337 | + } |
| 338 | + |
256 | 339 | private void createKeystore() throws Exception { |
257 | 340 | Path keystore = installation.config("elasticsearch.keystore"); |
258 | 341 | final Installation.Executables bin = installation.executables(); |
|
0 commit comments