diff options
author | Tor Brede Vekterli <vekterli@yahooinc.com> | 2022-11-04 10:37:05 +0100 |
---|---|---|
committer | Tor Brede Vekterli <vekterli@yahooinc.com> | 2022-11-04 10:37:05 +0100 |
commit | 0da08eab292f4745a3d1620691a8753da0a62984 (patch) | |
tree | 911f86519390f029405d5a212c923a7b4eceb220 /node-admin | |
parent | 37e61132b52f4d198caf37142023c84051253a7f (diff) |
Wire core dump encryption public key ID to core dump handler logic
Diffstat (limited to 'node-admin')
3 files changed, 67 insertions, 9 deletions
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandler.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandler.java index 3a61f8b2619..f4cd35bc5bd 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandler.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/CoredumpHandler.java @@ -5,12 +5,14 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.yahoo.config.provision.DockerImage; +import com.yahoo.security.KeyId; import com.yahoo.security.SecretSharedKey; import com.yahoo.security.SharedKeyGenerator; import com.yahoo.vespa.flags.BooleanFlag; import com.yahoo.vespa.flags.FetchVector; import com.yahoo.vespa.flags.FlagSource; import com.yahoo.vespa.flags.Flags; +import com.yahoo.vespa.flags.StringFlag; import com.yahoo.vespa.hosted.node.admin.configserver.cores.CoreDumpMetadata; import com.yahoo.vespa.hosted.node.admin.configserver.cores.Cores; import com.yahoo.vespa.hosted.node.admin.configserver.cores.bindings.ReportCoreDumpRequest; @@ -47,6 +49,7 @@ import java.util.function.Supplier; import java.util.logging.Logger; import java.util.regex.Pattern; import java.util.stream.IntStream; +import java.util.stream.Stream; import static com.yahoo.vespa.hosted.node.admin.task.util.file.FileFinder.nameEndsWith; import static com.yahoo.vespa.hosted.node.admin.task.util.file.FileFinder.nameMatches; @@ -79,8 +82,9 @@ public class CoredumpHandler { private final Metrics metrics; private final Clock clock; private final Supplier<String> coredumpIdSupplier; - private final Supplier<SecretSharedKey> secretSharedKeySupplier; + private final SecretSharedKeySupplier secretSharedKeySupplier; private final BooleanFlag reportCoresViaCfgFlag; + private final StringFlag coreEncryptionPublicKeyIdFlag; /** * @param crashPathInContainer path inside the container where core dump are dumped @@ -90,14 +94,23 @@ public class CoredumpHandler { String crashPathInContainer, Path doneCoredumpsPath, Metrics metrics, FlagSource flagSource) { this(coreCollector, cores, coredumpReporter, crashPathInContainer, doneCoredumpsPath, - metrics, Clock.systemUTC(), () -> UUID.randomUUID().toString(), () -> null /*TODO*/, + metrics, Clock.systemUTC(), () -> UUID.randomUUID().toString(), (ctx) -> Optional.empty() /*TODO*/, flagSource); } + // TODO remove redundant constructor once internal callsite has been updated + public CoredumpHandler(CoreCollector coreCollector, Cores cores, CoredumpReporter coredumpReporter, + String crashPathInContainer, Path doneCoredumpsPath, Metrics metrics, + SecretSharedKeySupplier secretSharedKeySupplier, FlagSource flagSource) { + this(coreCollector, cores, coredumpReporter, crashPathInContainer, doneCoredumpsPath, + metrics, Clock.systemUTC(), () -> UUID.randomUUID().toString(), secretSharedKeySupplier, + flagSource); + } + CoredumpHandler(CoreCollector coreCollector, Cores cores, CoredumpReporter coredumpReporter, String crashPathInContainer, Path doneCoredumpsPath, Metrics metrics, Clock clock, Supplier<String> coredumpIdSupplier, - Supplier<SecretSharedKey> secretSharedKeySupplier, FlagSource flagSource) { + SecretSharedKeySupplier secretSharedKeySupplier, FlagSource flagSource) { this.coreCollector = coreCollector; this.cores = cores; this.coredumpReporter = coredumpReporter; @@ -108,6 +121,7 @@ public class CoredumpHandler { this.coredumpIdSupplier = coredumpIdSupplier; this.secretSharedKeySupplier = secretSharedKeySupplier; this.reportCoresViaCfgFlag = Flags.REPORT_CORES_VIA_CFG.bindTo(flagSource); + this.coreEncryptionPublicKeyIdFlag = Flags.CORE_ENCRYPTION_PUBLIC_KEY_ID.bindTo(flagSource); } @@ -196,9 +210,16 @@ public class CoredumpHandler { return Optional.of(enqueuedDir); } + private String corePublicKeyFlagValue(NodeAgentContext context) { + return coreEncryptionPublicKeyIdFlag.with(FetchVector.Dimension.NODE_TYPE, context.nodeType().name()).value(); + } + void processAndReportSingleCoredump(NodeAgentContext context, ContainerPath coredumpDirectory, Supplier<Map<String, Object>> nodeAttributesSupplier) { try { - Optional<SecretSharedKey> sharedCoreKey = Optional.ofNullable(secretSharedKeySupplier.get()); + Optional<SecretSharedKey> sharedCoreKey = Optional.of(corePublicKeyFlagValue(context)) + .filter(k -> !k.isEmpty()) + .map(KeyId::ofString) + .flatMap(secretSharedKeySupplier::create); Optional<String> decryptionToken = sharedCoreKey.map(k -> k.sealedSharedKey().toTokenString()); String metadata = getMetadata(context, coredumpDirectory, nodeAttributesSupplier, decryptionToken); String coredumpId = coredumpDirectory.getFileName().toString(); @@ -359,7 +380,10 @@ public class CoredumpHandler { dockerImage.ifPresent(metadata::setDockerImage); dockerImage.flatMap(DockerImage::tag).ifPresent(metadata::setVespaVersion); dockerImage.ifPresent(metadata::setDockerImage); - Optional<SecretSharedKey> sharedCoreKey = Optional.ofNullable(secretSharedKeySupplier.get()); + Optional<SecretSharedKey> sharedCoreKey = Optional.of(corePublicKeyFlagValue(context)) + .filter(k -> !k.isEmpty()) + .map(KeyId::ofString) + .flatMap(secretSharedKeySupplier::create); sharedCoreKey.map(key -> key.sealedSharedKey().toTokenString()).ifPresent(metadata::setDecryptionToken); String coreDumpId = coreDumpDirectory.getFileName().toString(); diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/SecretSharedKeySupplier.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/SecretSharedKeySupplier.java new file mode 100644 index 00000000000..5d33c80e0cb --- /dev/null +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/coredump/SecretSharedKeySupplier.java @@ -0,0 +1,17 @@ +// 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.KeyId; +import com.yahoo.security.SecretSharedKey; + +import java.util.Optional; + +/** + * @author vekterli + */ +@FunctionalInterface +public interface SecretSharedKeySupplier { + + Optional<SecretSharedKey> create(KeyId publicKeyId); + +} 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 235007d7499..b096db2eaf4 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 @@ -5,6 +5,7 @@ import com.yahoo.security.KeyId; import com.yahoo.security.SealedSharedKey; import com.yahoo.security.SecretSharedKey; import com.yahoo.test.ManualClock; +import com.yahoo.vespa.flags.Flags; import com.yahoo.vespa.flags.InMemoryFlagSource; import com.yahoo.vespa.hosted.node.admin.configserver.cores.Cores; import com.yahoo.vespa.hosted.node.admin.container.metrics.DimensionMetrics; @@ -61,8 +62,7 @@ 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 SecretSharedKeySupplier secretSharedKeySupplier = mock(SecretSharedKeySupplier.class); private final InMemoryFlagSource flagSource = new InMemoryFlagSource(); private final CoredumpHandler coredumpHandler = new CoredumpHandler(coreCollector, cores, coredumpReporter, containerCrashPath.pathInContainer(), @@ -198,7 +198,7 @@ public class CoredumpHandlerTest { @Test void get_metadata_test_with_encryption() throws IOException { - when(secretSharedKeySupplier.get()).thenReturn(makeFixedSecretSharedKey()); + when(secretSharedKeySupplier.create(any())).thenReturn(Optional.of(makeFixedSecretSharedKey())); do_get_metadata_test(Optional.of("AVeryCoolToken"), Optional.of("AnEvenCoolerToken")); } @@ -250,10 +250,27 @@ public class CoredumpHandlerTest { @Test void process_single_coredump_test_with_encryption() throws IOException { - when(secretSharedKeySupplier.get()).thenReturn(makeFixedSecretSharedKey()); + flagSource.withStringFlag(Flags.CORE_ENCRYPTION_PUBLIC_KEY_ID.id(), "bar-key"); + when(secretSharedKeySupplier.create(KeyId.ofString("bar-key"))).thenReturn(Optional.of(makeFixedSecretSharedKey())); do_process_single_coredump_test("dump_bash.core.431.zst.enc"); } + // TODO fail closed instead of open + @Test + void encryption_disabled_when_no_public_key_set_in_feature_flag() throws IOException { + flagSource.withStringFlag(Flags.CORE_ENCRYPTION_PUBLIC_KEY_ID.id(), ""); // empty -> not set + verify(secretSharedKeySupplier, never()).create(any()); + do_process_single_coredump_test("dump_bash.core.431.zst"); // No .enc suffix; not encrypted + } + + // TODO fail closed instead of open + @Test + void encryption_disabled_when_no_key_returned_for_key_id_specified_by_feature_flag() throws IOException { + flagSource.withStringFlag(Flags.CORE_ENCRYPTION_PUBLIC_KEY_ID.id(), "baz-key"); + when(secretSharedKeySupplier.create(KeyId.ofString("baz-key"))).thenReturn(Optional.empty()); + do_process_single_coredump_test("dump_bash.core.431.zst"); // No .enc suffix; not encrypted + } + @Test void report_enqueued_and_processed_metrics() throws IOException { Path processingPath = containerCrashPath.resolve("processing"); |