aboutsummaryrefslogtreecommitdiffstats
path: root/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java
diff options
context:
space:
mode:
authorTor Brede Vekterli <vekterli@yahooinc.com>2022-10-31 11:41:27 +0100
committerTor Brede Vekterli <vekterli@yahooinc.com>2022-10-31 12:09:27 +0100
commite1df7cc47c9ec0dedb17a5bc0ad2a14967ddd6a0 (patch)
treed4598676adf654e61780ba99905877dc4b76cfb0 /node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java
parent80cec9a4412e65a756f8819c648071e4f15ca595 (diff)
Add encryption capabilities to core dump handler
Once wired in (not currently the case), a Supplier of non-null `SecretSharedKey` instances will trigger: 1. Wrapping the output stream with an encrypting output stream using the secret component of the supplied key. Zstd compression is handled on the input stream, so this should transparently encrypt compressed data. To disambiguate, encrypted core dumps are suffixed with an additional `.enc` file extension. 2. Emitting a public decryption token as part of the metadata using the shared component of the supplied key.
Diffstat (limited to 'node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java')
-rw-r--r--node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java64
1 files changed, 54 insertions, 10 deletions
diff --git a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java
index 082a1f3de58..1a1edbd597a 100644
--- a/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java
+++ b/node-admin/src/test/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandlerTest.java
@@ -1,6 +1,8 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.maintenance.coredump;
+import com.yahoo.security.SealedSharedKey;
+import com.yahoo.security.SecretSharedKey;
import com.yahoo.test.ManualClock;
import com.yahoo.vespa.hosted.node.admin.container.metrics.DimensionMetrics;
import com.yahoo.vespa.hosted.node.admin.container.metrics.Metrics;
@@ -12,7 +14,9 @@ import com.yahoo.vespa.test.file.TestFileSystem;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -53,8 +57,11 @@ public class CoredumpHandlerTest {
private final ManualClock clock = new ManualClock();
@SuppressWarnings("unchecked")
private final Supplier<String> coredumpIdSupplier = mock(Supplier.class);
+ @SuppressWarnings("unchecked")
+ private final Supplier<SecretSharedKey> secretSharedKeySupplier = mock(Supplier.class);
private final CoredumpHandler coredumpHandler = new CoredumpHandler(coreCollector, coredumpReporter,
- containerCrashPath.pathInContainer(), doneCoredumpsPath, metrics, clock, coredumpIdSupplier);
+ containerCrashPath.pathInContainer(), doneCoredumpsPath, metrics, clock, coredumpIdSupplier,
+ secretSharedKeySupplier);
@Test
@@ -136,8 +143,7 @@ public class CoredumpHandlerTest {
verify(coredumpIdSupplier, times(1)).get();
}
- @Test
- void get_metadata_test() throws IOException {
+ void do_get_metadata_test(Optional<String> decryptionToken) throws IOException {
Map<String, Object> metadata = new HashMap<>();
metadata.put("bin_path", "/bin/bash");
metadata.put("backtrace", List.of("call 1", "function 2", "something something"));
@@ -156,6 +162,7 @@ public class CoredumpHandlerTest {
"\"bin_path\":\"/bin/bash\"," +
"\"coredump_path\":\"/home/docker/dumps/container-123/id-123/dump_core.456\"," +
"\"docker_image\":\"vespa/ci:6.48.4\"" +
+ decryptionToken.map(",\"decryption_token\":\"%s\""::formatted).orElse("") +
"}}";
@@ -165,33 +172,44 @@ public class CoredumpHandlerTest {
when(coreCollector.collect(eq(context), eq(coredumpDirectory.resolve("dump_core.456"))))
.thenReturn(metadata);
- assertEquals(expectedMetadataStr, coredumpHandler.getMetadata(context, coredumpDirectory, () -> attributes));
+ assertEquals(expectedMetadataStr, coredumpHandler.getMetadata(context, coredumpDirectory, () -> attributes, decryptionToken));
verify(coreCollector, times(1)).collect(any(), any());
// Calling it again will simply read the previously generated metadata from disk
- assertEquals(expectedMetadataStr, coredumpHandler.getMetadata(context, coredumpDirectory, () -> attributes));
+ assertEquals(expectedMetadataStr, coredumpHandler.getMetadata(context, coredumpDirectory, () -> attributes, decryptionToken));
verify(coreCollector, times(1)).collect(any(), any());
}
@Test
+ void get_metadata_test_without_encryption() throws IOException {
+ do_get_metadata_test(Optional.empty()); // No token in metadata
+ }
+
+ @Test
+ void get_metadata_test_with_encryption() throws IOException {
+ when(secretSharedKeySupplier.get()).thenReturn(makeFixedSecretSharedKey());
+ do_get_metadata_test(Optional.of("AVeryCoolToken"));
+ }
+
+ @Test
void cant_get_metadata_if_no_core_file() {
assertThrows(IllegalStateException.class, () -> {
- coredumpHandler.getMetadata(context, context.paths().of("/fake/path"), Map::of);
+ coredumpHandler.getMetadata(context, context.paths().of("/fake/path"), Map::of, Optional.empty());
});
}
@Test
- void fails_to_get_core_file_if_only_compressed() {
+ void fails_to_get_core_file_if_only_compressed_or_encrypted() {
assertThrows(IllegalStateException.class, () -> {
ContainerPath coredumpDirectory = context.paths().of("/path/to/coredump/proccessing/id-123");
Files.createDirectories(coredumpDirectory);
Files.createFile(coredumpDirectory.resolve("dump_bash.core.431.zstd"));
+ Files.createFile(coredumpDirectory.resolve("dump_bash.core.543.zstd.enc"));
coredumpHandler.findCoredumpFileInProcessingDirectory(coredumpDirectory);
});
}
- @Test
- void process_single_coredump_test() throws IOException {
+ void do_process_single_coredump_test(String expectedCoreFileName) throws IOException {
ContainerPath coredumpDirectory = context.paths().of("/path/to/coredump/proccessing/id-123");
Files.createDirectories(coredumpDirectory);
Files.write(coredumpDirectory.resolve("metadata.json"), "metadata".getBytes());
@@ -203,7 +221,18 @@ public class CoredumpHandlerTest {
verify(coredumpReporter, times(1)).reportCoredump(eq("id-123"), eq("metadata"));
assertFalse(Files.exists(coredumpDirectory));
assertFolderContents(doneCoredumpsPath.resolve("container-123"), "id-123");
- assertFolderContents(doneCoredumpsPath.resolve("container-123").resolve("id-123"), "metadata.json", "dump_bash.core.431.zstd");
+ assertFolderContents(doneCoredumpsPath.resolve("container-123").resolve("id-123"), "metadata.json", expectedCoreFileName);
+ }
+
+ @Test
+ void process_single_coredump_test_without_encryption() throws IOException {
+ do_process_single_coredump_test("dump_bash.core.431.zstd");
+ }
+
+ @Test
+ void process_single_coredump_test_with_encryption() throws IOException {
+ when(secretSharedKeySupplier.get()).thenReturn(makeFixedSecretSharedKey());
+ do_process_single_coredump_test("dump_bash.core.431.zstd.enc");
}
@Test
@@ -248,4 +277,19 @@ public class CoredumpHandlerTest {
Files.createFile(path),
FileTime.from(clock.instant().minus(age))));
}
+
+ private static byte[] bytesOf(String str) {
+ return str.getBytes(StandardCharsets.UTF_8);
+ }
+
+ private static SecretSharedKey makeFixedSecretSharedKey() {
+ byte[] keyBytes = bytesOf("very secret yes!"); // 128 bits
+ var secretKey = new SecretKeySpec(keyBytes, "AES");
+ int keyId = 123;
+ // We don't parse any of these fields in the test, so just use dummy contents.
+ byte[] enc = bytesOf("hello world");
+ byte[] ciphertext = bytesOf("imaginary ciphertext");
+ return new SecretSharedKey(secretKey, new SealedSharedKey(keyId, enc, ciphertext));
+ }
+
}