From 9a7001e4b546eb25f5355ebe3aead644bbf658ca Mon Sep 17 00:00:00 2001 From: Morten Tokle Date: Tue, 25 Apr 2023 10:47:05 +0200 Subject: Revert "change to new identity document format" --- .../src/main/java/com/yahoo/vespa/flags/Flags.java | 6 - .../identity/AthenzCredentialsMaintainer.java | 42 ++----- .../api/DefaultSignedIdentityDocument.java | 14 --- .../identityprovider/api/EntityBindingsMapper.java | 124 +++++---------------- .../identityprovider/api/IdentityDocument.java | 54 --------- .../api/IdentityDocumentClient.java | 6 +- .../api/LegacySignedIdentityDocument.java | 6 - .../api/SignedIdentityDocument.java | 52 +++++++-- .../DefaultSignedIdentityDocumentEntity.java | 12 -- .../api/bindings/IdentityDocumentEntity.java | 51 --------- .../LegacySignedIdentityDocumentEntity.java | 57 ---------- .../api/bindings/SignedIdentityDocumentEntity.java | 124 +++++++++------------ .../client/AthenzCredentialsService.java | 18 ++- .../client/AthenzIdentityProviderImpl.java | 2 +- .../client/DefaultIdentityDocumentClient.java | 11 +- .../client/IdentityDocumentSigner.java | 50 +-------- .../api/EntityBindingsMapperTest.java | 43 +------ .../client/IdentityDocumentSignerTest.java | 68 ++++------- 18 files changed, 181 insertions(+), 559 deletions(-) delete mode 100644 vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/DefaultSignedIdentityDocument.java delete mode 100644 vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/IdentityDocument.java delete mode 100644 vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/LegacySignedIdentityDocument.java delete mode 100644 vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/DefaultSignedIdentityDocumentEntity.java delete mode 100644 vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/IdentityDocumentEntity.java delete mode 100644 vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/LegacySignedIdentityDocumentEntity.java diff --git a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java index e89724dfb16..0a5cd1aacac 100644 --- a/flags/src/main/java/com/yahoo/vespa/flags/Flags.java +++ b/flags/src/main/java/com/yahoo/vespa/flags/Flags.java @@ -410,12 +410,6 @@ public class Flags { "Takes effect at redeployment", ZONE_ID); - public static final UnboundBooleanFlag NEW_IDDOC_LAYOUT = defineFeatureFlag( - "new_iddoc_layout", false, List.of("tokle", "bjorncs", "olaa"), "2023-04-24", "2023-05-31", - "Whether to use new identity document lauoyt", - "Takes effect on node reboot", - HOSTNAME); - /** WARNING: public for testing: All flags should be defined in {@link Flags}. */ public static UnboundBooleanFlag defineFeatureFlag(String flagId, boolean defaultValue, List owners, String createdAt, String expiresAt, String description, diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java index c9c76e1edd3..3fb9c73367d 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java @@ -13,7 +13,6 @@ import com.yahoo.vespa.athenz.client.zts.ZtsClient; import com.yahoo.vespa.athenz.client.zts.ZtsClientException; import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider; import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper; -import com.yahoo.vespa.athenz.identityprovider.api.IdentityDocument; import com.yahoo.vespa.athenz.identityprovider.api.IdentityDocumentClient; import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument; import com.yahoo.vespa.athenz.identityprovider.client.CsrGenerator; @@ -77,7 +76,6 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { private final ServiceIdentityProvider hostIdentityProvider; private final IdentityDocumentClient identityDocumentClient; private final BooleanFlag tenantServiceIdentityFlag; - private final BooleanFlag useNewIdentityDocumentLayout; // Used as an optimization to ensure ZTS is not DDoS'ed on continuously failing refresh attempts private final Map lastRefreshAttempt = new ConcurrentHashMap<>(); @@ -99,7 +97,6 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { new AthenzIdentityVerifier(Set.of(configServerInfo.getConfigServerIdentity()))); this.clock = clock; this.tenantServiceIdentityFlag = Flags.NODE_ADMIN_TENANT_SERVICE_REGISTRY.bindTo(flagSource); - this.useNewIdentityDocumentLayout = Flags.NEW_IDDOC_LAYOUT.bindTo(flagSource); } public boolean converge(NodeAgentContext context) { @@ -133,7 +130,7 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { Instant now = clock.instant(); Instant expiry = certificate.getNotAfter().toInstant(); var doc = EntityBindingsMapper.readSignedIdentityDocumentFromFile(identityDocumentFile); - if (refreshIdentityDocument(doc, context)) { + if (doc.outdated()) { context.log(logger, "Identity document is outdated (version=%d)", doc.documentVersion()); registerIdentity(context, privateKeyFile, certificateFile, identityDocumentFile, identityType, athenzIdentity); return true; @@ -153,7 +150,7 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { return false; } else { lastRefreshAttempt.put(context.containerName(), now); - refreshIdentity(context, privateKeyFile, certificateFile, identityDocumentFile, doc.identityDocument(), identityType, athenzIdentity); + refreshIdentity(context, privateKeyFile, certificateFile, identityDocumentFile, doc, identityType, athenzIdentity); return true; } } @@ -164,11 +161,6 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { } } - private boolean refreshIdentityDocument(SignedIdentityDocument signedIdentityDocument, NodeAgentContext context) { - int expectedVersion = documentVersion(context); - return signedIdentityDocument.outdated() || signedIdentityDocument.documentVersion() != expectedVersion; - } - public void clearCredentials(NodeAgentContext context) { FileFinder.files(context.paths().of(CONTAINER_SIA_DIRECTORY)) .deleteRecursively(context); @@ -208,8 +200,7 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { private void registerIdentity(NodeAgentContext context, ContainerPath privateKeyFile, ContainerPath certificateFile, ContainerPath identityDocumentFile, IdentityType identityType, AthenzIdentity identity) { KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA); - SignedIdentityDocument signedDoc = signedIdentityDocument(context, identityType); - IdentityDocument doc = signedDoc.identityDocument(); + SignedIdentityDocument doc = signedIdentityDocument(context, identityType); CsrGenerator csrGenerator = new CsrGenerator(certificateDnsSuffix, doc.providerService().getFullName()); Pkcs10Csr csr = csrGenerator.generateInstanceCsr( identity, doc.providerUniqueId(), doc.ipAddresses(), doc.clusterType(), keyPair); @@ -221,9 +212,9 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { ztsClient.registerInstance( doc.providerService(), identity, - EntityBindingsMapper.toAttestationData(signedDoc), + EntityBindingsMapper.toAttestationData(doc), csr); - EntityBindingsMapper.writeSignedIdentityDocumentToFile(identityDocumentFile, signedDoc); + EntityBindingsMapper.writeSignedIdentityDocumentToFile(identityDocumentFile, doc); writePrivateKeyAndCertificate(privateKeyFile, keyPair.getPrivate(), certificateFile, instanceIdentity.certificate()); context.log(logger, "Instance successfully registered and credentials written to file"); } @@ -232,14 +223,14 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { /** * Return zts url from identity document, fallback to ztsEndpoint */ - private URI ztsEndpoint(IdentityDocument doc) { + private URI ztsEndpoint(SignedIdentityDocument doc) { return Optional.ofNullable(doc.ztsUrl()) .filter(s -> !s.isBlank()) .map(URI::create) .orElse(ztsEndpoint); } private void refreshIdentity(NodeAgentContext context, ContainerPath privateKeyFile, ContainerPath certificateFile, - ContainerPath identityDocumentFile, IdentityDocument doc, IdentityType identityType, AthenzIdentity identity) { + ContainerPath identityDocumentFile, SignedIdentityDocument doc, IdentityType identityType, AthenzIdentity identity) { KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA); CsrGenerator csrGenerator = new CsrGenerator(certificateDnsSuffix, doc.providerService().getFullName()); Pkcs10Csr csr = csrGenerator.generateInstanceCsr( @@ -300,8 +291,8 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { private SignedIdentityDocument signedIdentityDocument(NodeAgentContext context, IdentityType identityType) { return switch (identityType) { - case NODE -> identityDocumentClient.getNodeIdentityDocument(context.hostname().value(), documentVersion(context)); - case TENANT -> identityDocumentClient.getTenantIdentityDocument(context.hostname().value(), documentVersion(context)); + case NODE -> identityDocumentClient.getNodeIdentityDocument(context.hostname().value()); + case TENANT -> identityDocumentClient.getTenantIdentityDocument(context.hostname().value()); }; } @@ -314,9 +305,9 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { private AthenzIdentity getTenantIdentity(NodeAgentContext context, ContainerPath identityDocumentFile) { if (Files.exists(identityDocumentFile)) { - return EntityBindingsMapper.readSignedIdentityDocumentFromFile(identityDocumentFile).identityDocument().serviceIdentity(); + return EntityBindingsMapper.readSignedIdentityDocumentFromFile(identityDocumentFile).serviceIdentity(); } else { - return identityDocumentClient.getTenantIdentityDocument(context.hostname().value(), documentVersion(context)).identityDocument().serviceIdentity(); + return identityDocumentClient.getTenantIdentityDocument(context.hostname().value()).serviceIdentity(); } } @@ -326,17 +317,6 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { .value(); } - /* - Get the document version to ask for - */ - private int documentVersion(NodeAgentContext context) { - return useNewIdentityDocumentLayout - .with(FetchVector.Dimension.HOSTNAME, context.hostname().value()) - .value() - ? SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION - : SignedIdentityDocument.LEGACY_DEFAULT_DOCUMENT_VERSION; - } - enum IdentityType { NODE("vespa-node-identity-document.json"), TENANT("vespa-tenant-identity-document.json"); diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/DefaultSignedIdentityDocument.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/DefaultSignedIdentityDocument.java deleted file mode 100644 index c2ab22f4921..00000000000 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/DefaultSignedIdentityDocument.java +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.athenz.identityprovider.api; - -public record DefaultSignedIdentityDocument(String signature, int signingKeyVersion, int documentVersion, - String data, IdentityDocument identityDocument) implements SignedIdentityDocument { - - public DefaultSignedIdentityDocument { - identityDocument = EntityBindingsMapper.fromIdentityDocumentData(data); - } - - public DefaultSignedIdentityDocument(String signature, int signingKeyVersion, int documentVersion, String data) { - this(signature,signingKeyVersion,documentVersion, data, null); - } -} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java index a695e10a29c..2d77d2ceda1 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java @@ -6,12 +6,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.yahoo.vespa.athenz.api.AthenzIdentity; import com.yahoo.vespa.athenz.api.AthenzService; -import com.yahoo.vespa.athenz.identityprovider.api.bindings.DefaultSignedIdentityDocumentEntity; -import com.yahoo.vespa.athenz.identityprovider.api.bindings.IdentityDocumentEntity; -import com.yahoo.vespa.athenz.identityprovider.api.bindings.LegacySignedIdentityDocumentEntity; import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity; import com.yahoo.vespa.athenz.utils.AthenzIdentities; -import com.yahoo.yolean.Exceptions; import java.io.IOException; import java.io.InputStream; @@ -20,8 +16,6 @@ import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.time.Instant; -import java.util.Base64; import java.util.Optional; import static com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId.fromDottedString; @@ -30,7 +24,6 @@ import static com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId. * Utility class for mapping objects model types and their Jackson binding versions. * * @author bjorncs - * @author mortent */ public class EntityBindingsMapper { @@ -55,60 +48,39 @@ public class EntityBindingsMapper { } public static SignedIdentityDocument toSignedIdentityDocument(SignedIdentityDocumentEntity entity) { - if (entity instanceof LegacySignedIdentityDocumentEntity docEntity) { - IdentityDocument doc = new IdentityDocument( - fromDottedString(docEntity.providerUniqueId()), - new AthenzService(docEntity.providerService()), - docEntity.configServerHostname(), - docEntity.instanceHostname(), - docEntity.createdAt(), - docEntity.ipAddresses(), - IdentityType.fromId(docEntity.identityType()), - Optional.ofNullable(docEntity.clusterType()).map(ClusterType::from).orElse(null), - docEntity.ztsUrl(), - Optional.ofNullable(docEntity.serviceIdentity()).map(AthenzIdentities::from).orElse(null), - docEntity.unknownAttributes()); - return new LegacySignedIdentityDocument( - docEntity.signature(), - docEntity.signingKeyVersion(), - entity.documentVersion(), - doc); - } else if (entity instanceof DefaultSignedIdentityDocumentEntity docEntity) { - return new DefaultSignedIdentityDocument(docEntity.signature(), - docEntity.signingKeyVersion(), - docEntity.documentVersion(), - docEntity.data()); - } else { - throw new IllegalArgumentException("Unknown signed identity document type: " + entity.getClass().getName()); - } + return new SignedIdentityDocument( + entity.signature(), + entity.signingKeyVersion(), + fromDottedString(entity.providerUniqueId()), + new AthenzService(entity.providerService()), + entity.documentVersion(), + entity.configServerHostname(), + entity.instanceHostname(), + entity.createdAt(), + entity.ipAddresses(), + IdentityType.fromId(entity.identityType()), + Optional.ofNullable(entity.clusterType()).map(ClusterType::from).orElse(null), + entity.ztsUrl(), + Optional.ofNullable(entity.serviceIdentity()).map(AthenzIdentities::from).orElse(null), + entity.unknownAttributes()); } public static SignedIdentityDocumentEntity toSignedIdentityDocumentEntity(SignedIdentityDocument model) { - if (model instanceof LegacySignedIdentityDocument legacyModel) { - IdentityDocument idDoc = legacyModel.identityDocument(); - return new LegacySignedIdentityDocumentEntity( - legacyModel.signature(), - legacyModel.signingKeyVersion(), - idDoc.providerUniqueId().asDottedString(), - idDoc.providerService().getFullName(), - legacyModel.documentVersion(), - idDoc.configServerHostname(), - idDoc.instanceHostname(), - idDoc.createdAt(), - idDoc.ipAddresses(), - idDoc.identityType().id(), - Optional.ofNullable(idDoc.clusterType()).map(ClusterType::toConfigValue).orElse(null), - idDoc.ztsUrl(), - Optional.ofNullable(idDoc.serviceIdentity()).map(AthenzIdentity::getFullName).orElse(null), - idDoc.unknownAttributes()); - } else if (model instanceof DefaultSignedIdentityDocument defaultModel){ - return new DefaultSignedIdentityDocumentEntity(defaultModel.signature(), - defaultModel.signingKeyVersion(), - defaultModel.documentVersion(), - defaultModel.data()); - } else { - throw new IllegalArgumentException("Unsupported model type: " + model.getClass().getName()); - } + return new SignedIdentityDocumentEntity( + model.signature(), + model.signingKeyVersion(), + model.providerUniqueId().asDottedString(), + model.providerService().getFullName(), + model.documentVersion(), + model.configServerHostname(), + model.instanceHostname(), + model.createdAt(), + model.ipAddresses(), + model.identityType().id(), + Optional.ofNullable(model.clusterType()).map(ClusterType::toConfigValue).orElse(null), + model.ztsUrl(), + Optional.ofNullable(model.serviceIdentity()).map(AthenzIdentity::getFullName).orElse(null), + model.unknownAttributes()); } public static SignedIdentityDocument readSignedIdentityDocumentFromFile(Path file) { @@ -132,40 +104,4 @@ public class EntityBindingsMapper { } } - public static IdentityDocument fromIdentityDocumentData(String data) { - byte[] decoded = Base64.getDecoder().decode(data); - IdentityDocumentEntity docEntity = Exceptions.uncheck(() -> mapper.readValue(decoded, IdentityDocumentEntity.class)); - return new IdentityDocument( - fromDottedString(docEntity.providerUniqueId()), - new AthenzService(docEntity.providerService()), - docEntity.configServerHostname(), - docEntity.instanceHostname(), - docEntity.createdAt(), - docEntity.ipAddresses(), - IdentityType.fromId(docEntity.identityType()), - Optional.ofNullable(docEntity.clusterType()).map(ClusterType::from).orElse(null), - docEntity.ztsUrl(), - Optional.ofNullable(docEntity.serviceIdentity()).map(AthenzIdentities::from).orElse(null), - docEntity.unknownAttributes()); - } - - public static String toIdentityDocmentData(IdentityDocument identityDocument) { - IdentityDocumentEntity documentEntity = new IdentityDocumentEntity( - identityDocument.providerUniqueId().asDottedString(), - identityDocument.providerService().getFullName(), - identityDocument.configServerHostname(), - identityDocument.instanceHostname(), - identityDocument.createdAt(), - identityDocument.ipAddresses(), - identityDocument.identityType().id(), - Optional.ofNullable(identityDocument.clusterType()).map(ClusterType::toConfigValue).orElse(null), - identityDocument.ztsUrl(), - identityDocument.serviceIdentity().getFullName()); - try { - byte[] bytes = mapper.writeValueAsBytes(documentEntity); - return Base64.getEncoder().encodeToString(bytes); - } catch (JsonProcessingException e) { - throw new RuntimeException("Error during serialization of identity document.", e); - } - } } diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/IdentityDocument.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/IdentityDocument.java deleted file mode 100644 index 577584db185..00000000000 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/IdentityDocument.java +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.athenz.identityprovider.api; - -import com.yahoo.vespa.athenz.api.AthenzIdentity; -import com.yahoo.vespa.athenz.api.AthenzService; - -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * Represents an unsigned identity document - * @author mortent - */ -public record IdentityDocument(VespaUniqueInstanceId providerUniqueId, AthenzService providerService, String configServerHostname, - String instanceHostname, Instant createdAt, Set ipAddresses, - IdentityType identityType, ClusterType clusterType, String ztsUrl, - AthenzIdentity serviceIdentity, Map unknownAttributes) { - - public IdentityDocument { - ipAddresses = Set.copyOf(ipAddresses); - - Map nonNull = new HashMap<>(); - unknownAttributes.forEach((key, value) -> { - if (value != null) nonNull.put(key, value); - }); - // Map.copyOf() does not allow null values - unknownAttributes = Map.copyOf(nonNull); - } - - public IdentityDocument(VespaUniqueInstanceId providerUniqueId, AthenzService providerService, String configServerHostname, - String instanceHostname, Instant createdAt, Set ipAddresses, - IdentityType identityType, ClusterType clusterType, String ztsUrl, - AthenzIdentity serviceIdentity) { - this(providerUniqueId, providerService, configServerHostname, instanceHostname, createdAt, ipAddresses, identityType, clusterType, ztsUrl, serviceIdentity, Map.of()); - } - - - public IdentityDocument withServiceIdentity(AthenzService athenzService) { - return new IdentityDocument( - this.providerUniqueId, - this.providerService, - this.configServerHostname, - this.instanceHostname, - this.createdAt, - this.ipAddresses, - this.identityType, - this.clusterType, - this.ztsUrl, - athenzService, - this.unknownAttributes); - } -} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/IdentityDocumentClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/IdentityDocumentClient.java index 0e13cba8de9..5a0f77ec765 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/IdentityDocumentClient.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/IdentityDocumentClient.java @@ -1,14 +1,12 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.athenz.identityprovider.api; -import java.util.OptionalInt; - /** * A client that communicates that fetches an identity document. * * @author bjorncs */ public interface IdentityDocumentClient { - SignedIdentityDocument getNodeIdentityDocument(String host, int documentVersion); - SignedIdentityDocument getTenantIdentityDocument(String host, int documentVersion); + SignedIdentityDocument getNodeIdentityDocument(String host); + SignedIdentityDocument getTenantIdentityDocument(String host); } diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/LegacySignedIdentityDocument.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/LegacySignedIdentityDocument.java deleted file mode 100644 index 220bc72a017..00000000000 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/LegacySignedIdentityDocument.java +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.athenz.identityprovider.api; - -public record LegacySignedIdentityDocument(String signature, int signingKeyVersion, int documentVersion, - IdentityDocument identityDocument) implements SignedIdentityDocument { -} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java index 4e3bd8dee91..de78d81cd1b 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java @@ -1,20 +1,54 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.athenz.identityprovider.api; +import com.yahoo.vespa.athenz.api.AthenzIdentity; +import com.yahoo.vespa.athenz.api.AthenzService; + +import java.net.URL; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + /** * A signed identity document. + * The {@link #unknownAttributes()} member provides forward compatibility and ensures any new/unknown fields are kept intact when serialized to JSON. + * * @author bjorncs - * @author mortent */ -public interface SignedIdentityDocument { +public record SignedIdentityDocument(String signature, int signingKeyVersion, VespaUniqueInstanceId providerUniqueId, + AthenzService providerService, int documentVersion, String configServerHostname, + String instanceHostname, Instant createdAt, Set ipAddresses, + IdentityType identityType, ClusterType clusterType, String ztsUrl, + AthenzIdentity serviceIdentity, Map unknownAttributes) { + + public SignedIdentityDocument { + ipAddresses = Set.copyOf(ipAddresses); + + Map nonNull = new HashMap<>(); + unknownAttributes.forEach((key, value) -> { + if (value != null) nonNull.put(key, value); + }); + // Map.copyOf() does not allow null values + unknownAttributes = Map.copyOf(nonNull); + } + + public SignedIdentityDocument(String signature, int signingKeyVersion, VespaUniqueInstanceId providerUniqueId, + AthenzService providerService, int documentVersion, String configServerHostname, + String instanceHostname, Instant createdAt, Set ipAddresses, + IdentityType identityType, ClusterType clusterType, String ztsUrl, AthenzIdentity serviceIdentity) { + this(signature, signingKeyVersion, providerUniqueId, providerService, documentVersion, configServerHostname, + instanceHostname, createdAt, ipAddresses, identityType, clusterType, ztsUrl, serviceIdentity, Map.of()); + } + + public static final int DEFAULT_DOCUMENT_VERSION = 3; + + public boolean outdated() { return documentVersion < DEFAULT_DOCUMENT_VERSION; } - int LEGACY_DEFAULT_DOCUMENT_VERSION = 3; - int DEFAULT_DOCUMENT_VERSION = 4; + public SignedIdentityDocument withServiceIdentity(AthenzIdentity identity) { + return new SignedIdentityDocument(signature, signingKeyVersion, providerUniqueId, providerService, documentVersion, configServerHostname, instanceHostname, createdAt, + ipAddresses, identityType, clusterType, ztsUrl, identity); + } - default boolean outdated() { return documentVersion() < LEGACY_DEFAULT_DOCUMENT_VERSION; } - IdentityDocument identityDocument(); - String signature(); - int signingKeyVersion(); - int documentVersion(); } diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/DefaultSignedIdentityDocumentEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/DefaultSignedIdentityDocumentEntity.java deleted file mode 100644 index 3aaff011415..00000000000 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/DefaultSignedIdentityDocumentEntity.java +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.athenz.identityprovider.api.bindings; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public record DefaultSignedIdentityDocumentEntity( - @JsonProperty("signature") String signature, - @JsonProperty("signing-key-version") int signingKeyVersion, - @JsonProperty("document-version") int documentVersion, - @JsonProperty("data") String data) - implements SignedIdentityDocumentEntity { -} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/IdentityDocumentEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/IdentityDocumentEntity.java deleted file mode 100644 index 946eacc67eb..00000000000 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/IdentityDocumentEntity.java +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.athenz.identityprovider.api.bindings; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * @author bjorncs - * @author mortent - */ -@JsonInclude(JsonInclude.Include.NON_NULL) -public record IdentityDocumentEntity(String providerUniqueId, String providerService, - String configServerHostname, String instanceHostname, Instant createdAt, Set ipAddresses, - String identityType, String clusterType, String ztsUrl, String serviceIdentity, Map unknownAttributes) { - - @JsonCreator - public IdentityDocumentEntity(@JsonProperty("provider-unique-id") String providerUniqueId, - @JsonProperty("provider-service") String providerService, - @JsonProperty("configserver-hostname") String configServerHostname, - @JsonProperty("instance-hostname") String instanceHostname, - @JsonProperty("created-at") Instant createdAt, - @JsonProperty("ip-addresses") Set ipAddresses, - @JsonProperty("identity-type") String identityType, - @JsonProperty("cluster-type") String clusterType, - @JsonProperty("zts-url") String ztsUrl, - @JsonProperty("service-identity") String serviceIdentity) { - this(providerUniqueId, providerService, configServerHostname, - instanceHostname, createdAt, ipAddresses, identityType, clusterType, ztsUrl, serviceIdentity, new HashMap<>()); - } - - @JsonProperty("provider-unique-id") @Override public String providerUniqueId() { return providerUniqueId; } - @JsonProperty("provider-service") @Override public String providerService() { return providerService; } - @JsonProperty("configserver-hostname") @Override public String configServerHostname() { return configServerHostname; } - @JsonProperty("instance-hostname") @Override public String instanceHostname() { return instanceHostname; } - @JsonProperty("created-at") @Override public Instant createdAt() { return createdAt; } - @JsonProperty("ip-addresses") @Override public Set ipAddresses() { return ipAddresses; } - @JsonProperty("identity-type") @Override public String identityType() { return identityType; } - @JsonProperty("cluster-type") @Override public String clusterType() { return clusterType; } - @JsonProperty("zts-url") @Override public String ztsUrl() { return ztsUrl; } - @JsonProperty("service-identity") @Override public String serviceIdentity() { return serviceIdentity; } - @JsonAnyGetter @Override public Map unknownAttributes() { return unknownAttributes; } - @JsonAnySetter public void set(String name, Object value) { unknownAttributes.put(name, value); } -} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/LegacySignedIdentityDocumentEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/LegacySignedIdentityDocumentEntity.java deleted file mode 100644 index e00ab9978f6..00000000000 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/LegacySignedIdentityDocumentEntity.java +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.athenz.identityprovider.api.bindings; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.time.Instant; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * @author bjorncs - */ -@JsonInclude(JsonInclude.Include.NON_NULL) -public record LegacySignedIdentityDocumentEntity ( - String signature, int signingKeyVersion, String providerUniqueId, String providerService, int documentVersion, - String configServerHostname, String instanceHostname, Instant createdAt, Set ipAddresses, - String identityType, String clusterType, String ztsUrl, String serviceIdentity, Map unknownAttributes) implements SignedIdentityDocumentEntity { - - @JsonCreator - public LegacySignedIdentityDocumentEntity(@JsonProperty("signature") String signature, - @JsonProperty("signing-key-version") int signingKeyVersion, - @JsonProperty("provider-unique-id") String providerUniqueId, - @JsonProperty("provider-service") String providerService, - @JsonProperty("document-version") int documentVersion, - @JsonProperty("configserver-hostname") String configServerHostname, - @JsonProperty("instance-hostname") String instanceHostname, - @JsonProperty("created-at") Instant createdAt, - @JsonProperty("ip-addresses") Set ipAddresses, - @JsonProperty("identity-type") String identityType, - @JsonProperty("cluster-type") String clusterType, - @JsonProperty("zts-url") String ztsUrl, - @JsonProperty("service-identity") String serviceIdentity) { - this(signature, signingKeyVersion, providerUniqueId, providerService, documentVersion, configServerHostname, - instanceHostname, createdAt, ipAddresses, identityType, clusterType, ztsUrl, serviceIdentity, new HashMap<>()); - } - - @JsonProperty("signature") @Override public String signature() { return signature; } - @JsonProperty("signing-key-version") @Override public int signingKeyVersion() { return signingKeyVersion; } - @JsonProperty("provider-unique-id") @Override public String providerUniqueId() { return providerUniqueId; } - @JsonProperty("provider-service") @Override public String providerService() { return providerService; } - @JsonProperty("document-version") @Override public int documentVersion() { return documentVersion; } - @JsonProperty("configserver-hostname") @Override public String configServerHostname() { return configServerHostname; } - @JsonProperty("instance-hostname") @Override public String instanceHostname() { return instanceHostname; } - @JsonProperty("created-at") @Override public Instant createdAt() { return createdAt; } - @JsonProperty("ip-addresses") @Override public Set ipAddresses() { return ipAddresses; } - @JsonProperty("identity-type") @Override public String identityType() { return identityType; } - @JsonProperty("cluster-type") @Override public String clusterType() { return clusterType; } - @JsonProperty("zts-url") @Override public String ztsUrl() { return ztsUrl; } - @JsonProperty("service-identity") @Override public String serviceIdentity() { return serviceIdentity; } - @JsonAnyGetter @Override public Map unknownAttributes() { return unknownAttributes; } - @JsonAnySetter public void set(String name, Object value) { unknownAttributes.put(name, value); } -} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java index 174c76f7fa9..fc0dff3b97b 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java @@ -1,77 +1,57 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.athenz.identityprovider.api.bindings; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.databind.DatabindContext; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver; -import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; -import com.fasterxml.jackson.databind.type.TypeFactory; -import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument; - -import java.io.IOException; -import java.util.Objects; - -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM, property = "document-version", visible = true) -@JsonTypeIdResolver(SignedIdentityDocumentEntityTypeResolver.class) -public interface SignedIdentityDocumentEntity { - int documentVersion(); -} - -class SignedIdentityDocumentEntityTypeResolver implements TypeIdResolver { - JavaType javaType; - - @Override - public void init(JavaType javaType) { - this.javaType = javaType; - } - - @Override - public String idFromValue(Object o) { - return idFromValueAndType(o, o.getClass()); - } - - @Override - public String idFromValueAndType(Object o, Class aClass) { - if (Objects.isNull(o)) { - throw new IllegalArgumentException("Cannot serialize null oject"); - } else { - if (o instanceof SignedIdentityDocumentEntity s) { - return Integer.toString(s.documentVersion()); - } else { - throw new IllegalArgumentException("Cannot serialize class: " + o.getClass()); - } - } - } - - @Override - public String idFromBaseType() { - return idFromValueAndType(null, javaType.getRawClass()); - } - - @Override - public JavaType typeFromId(DatabindContext databindContext, String s) throws IOException { - try { - int version = Integer.parseInt(s); - Class cls = version <= SignedIdentityDocument.LEGACY_DEFAULT_DOCUMENT_VERSION - ? LegacySignedIdentityDocumentEntity.class - : DefaultSignedIdentityDocumentEntity.class; - return TypeFactory.defaultInstance().constructSpecializedType(javaType,cls); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Unable to deserialize document with version: \"%s\"".formatted(s)); - } +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * @author bjorncs + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public record SignedIdentityDocumentEntity( + String signature, int signingKeyVersion, String providerUniqueId, String providerService, int documentVersion, + String configServerHostname, String instanceHostname, Instant createdAt, Set ipAddresses, + String identityType, String clusterType, String ztsUrl, String serviceIdentity, Map unknownAttributes) { + + @JsonCreator + public SignedIdentityDocumentEntity(@JsonProperty("signature") String signature, + @JsonProperty("signing-key-version") int signingKeyVersion, + @JsonProperty("provider-unique-id") String providerUniqueId, + @JsonProperty("provider-service") String providerService, + @JsonProperty("document-version") int documentVersion, + @JsonProperty("configserver-hostname") String configServerHostname, + @JsonProperty("instance-hostname") String instanceHostname, + @JsonProperty("created-at") Instant createdAt, + @JsonProperty("ip-addresses") Set ipAddresses, + @JsonProperty("identity-type") String identityType, + @JsonProperty("cluster-type") String clusterType, + @JsonProperty("zts-url") String ztsUrl, + @JsonProperty("service-identity") String serviceIdentity) { + this(signature, signingKeyVersion, providerUniqueId, providerService, documentVersion, configServerHostname, + instanceHostname, createdAt, ipAddresses, identityType, clusterType, ztsUrl, serviceIdentity, new HashMap<>()); } - @Override - public String getDescForKnownTypeIds() { - return "Type resolver for SignedIdentityDocumentEntity"; - } - - @Override - public JsonTypeInfo.Id getMechanism() { - return JsonTypeInfo.Id.CUSTOM; - } -} \ No newline at end of file + @JsonProperty("signature") @Override public String signature() { return signature; } + @JsonProperty("signing-key-version") @Override public int signingKeyVersion() { return signingKeyVersion; } + @JsonProperty("provider-unique-id") @Override public String providerUniqueId() { return providerUniqueId; } + @JsonProperty("provider-service") @Override public String providerService() { return providerService; } + @JsonProperty("document-version") @Override public int documentVersion() { return documentVersion; } + @JsonProperty("configserver-hostname") @Override public String configServerHostname() { return configServerHostname; } + @JsonProperty("instance-hostname") @Override public String instanceHostname() { return instanceHostname; } + @JsonProperty("created-at") @Override public Instant createdAt() { return createdAt; } + @JsonProperty("ip-addresses") @Override public Set ipAddresses() { return ipAddresses; } + @JsonProperty("identity-type") @Override public String identityType() { return identityType; } + @JsonProperty("cluster-type") @Override public String clusterType() { return clusterType; } + @JsonProperty("zts-url") @Override public String ztsUrl() { return ztsUrl; } + @JsonProperty("service-identity") @Override public String serviceIdentity() { return serviceIdentity; } + @JsonAnyGetter @Override public Map unknownAttributes() { return unknownAttributes; } + @JsonAnySetter public void set(String name, Object value) { unknownAttributes.put(name, value); } +} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java index 1858653c9b4..cc9d3b2be65 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java @@ -11,7 +11,6 @@ import com.yahoo.vespa.athenz.client.zts.InstanceIdentity; import com.yahoo.vespa.athenz.client.zts.ZtsClient; import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider; import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper; -import com.yahoo.vespa.athenz.identityprovider.api.IdentityDocument; import com.yahoo.vespa.athenz.identityprovider.api.IdentityDocumentClient; import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument; import com.yahoo.vespa.athenz.tls.AthenzIdentityVerifier; @@ -75,9 +74,7 @@ class AthenzCredentialsService { } KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA); IdentityDocumentClient identityDocumentClient = createIdentityDocumentClient(); - // Use legacy version for now. - SignedIdentityDocument signedDocument = identityDocumentClient.getTenantIdentityDocument(hostname, SignedIdentityDocument.LEGACY_DEFAULT_DOCUMENT_VERSION); - IdentityDocument document = signedDocument.identityDocument(); + SignedIdentityDocument document = identityDocumentClient.getTenantIdentityDocument(hostname); Pkcs10Csr csr = csrGenerator.generateInstanceCsr( tenantIdentity, document.providerUniqueId(), @@ -90,17 +87,16 @@ class AthenzCredentialsService { ztsClient.registerInstance( configserverIdentity, tenantIdentity, - EntityBindingsMapper.toAttestationData(signedDocument), + EntityBindingsMapper.toAttestationData(document), csr); X509Certificate certificate = instanceIdentity.certificate(); - writeCredentialsToDisk(keyPair.getPrivate(), certificate, signedDocument); - return new AthenzCredentials(certificate, keyPair, signedDocument); + writeCredentialsToDisk(keyPair.getPrivate(), certificate, document); + return new AthenzCredentials(certificate, keyPair, document); } } - AthenzCredentials updateCredentials(SignedIdentityDocument signedDocument, SSLContext sslContext) { + AthenzCredentials updateCredentials(SignedIdentityDocument document, SSLContext sslContext) { KeyPair newKeyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA); - IdentityDocument document = signedDocument.identityDocument(); Pkcs10Csr csr = csrGenerator.generateInstanceCsr( tenantIdentity, document.providerUniqueId(), @@ -116,8 +112,8 @@ class AthenzCredentialsService { document.providerUniqueId().asDottedString(), csr); X509Certificate certificate = instanceIdentity.certificate(); - writeCredentialsToDisk(newKeyPair.getPrivate(), certificate, signedDocument); - return new AthenzCredentials(certificate, newKeyPair, signedDocument); + writeCredentialsToDisk(newKeyPair.getPrivate(), certificate, document); + return new AthenzCredentials(certificate, newKeyPair, document); } } diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java index b5579cbfcd8..b9f9f3862c2 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java @@ -298,7 +298,7 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen } private X509Certificate requestRoleCertificate(AthenzRole role) { - var doc = credentials.getIdentityDocument().identityDocument(); + var doc = credentials.getIdentityDocument(); Pkcs10Csr csr = csrGenerator.generateRoleCsr( identity, role, doc.providerUniqueId(), doc.clusterType(), credentials.getKeyPair()); try (ZtsClient client = createZtsClient()) { diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/DefaultIdentityDocumentClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/DefaultIdentityDocumentClient.java index 48fc021dced..5b884e3dfb3 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/DefaultIdentityDocumentClient.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/DefaultIdentityDocumentClient.java @@ -56,16 +56,16 @@ public class DefaultIdentityDocumentClient implements IdentityDocumentClient { } @Override - public SignedIdentityDocument getNodeIdentityDocument(String host, int documentVersion) { - return getIdentityDocument(host, "node", documentVersion); + public SignedIdentityDocument getNodeIdentityDocument(String host) { + return getIdentityDocument(host, "node"); } @Override - public SignedIdentityDocument getTenantIdentityDocument(String host, int documentVersion) { - return getIdentityDocument(host, "tenant", documentVersion); + public SignedIdentityDocument getTenantIdentityDocument(String host) { + return getIdentityDocument(host, "tenant"); } - private SignedIdentityDocument getIdentityDocument(String host, String type, int documentVersion) { + private SignedIdentityDocument getIdentityDocument(String host, String type) { try (CloseableHttpClient client = createHttpClient(sslContextSupplier.get(), hostnameVerifier)) { URI uri = configserverUri @@ -76,7 +76,6 @@ public class DefaultIdentityDocumentClient implements IdentityDocumentClient { .setUri(uri) .addHeader("Connection", "close") .addHeader("Accept", "application/json") - .addParameter("documentVersion", Integer.toString(documentVersion)) .build(); try (CloseableHttpResponse response = client.execute(request)) { String responseContent = EntityUtils.toString(response.getEntity()); diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSigner.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSigner.java index 11b30585933..019f73fc6bf 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSigner.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSigner.java @@ -4,10 +4,7 @@ package com.yahoo.vespa.athenz.identityprovider.client; import com.yahoo.security.SignatureUtils; import com.yahoo.vespa.athenz.api.AthenzIdentity; import com.yahoo.vespa.athenz.api.AthenzService; -import com.yahoo.vespa.athenz.identityprovider.api.DefaultSignedIdentityDocument; -import com.yahoo.vespa.athenz.identityprovider.api.IdentityDocument; import com.yahoo.vespa.athenz.identityprovider.api.IdentityType; -import com.yahoo.vespa.athenz.identityprovider.api.LegacySignedIdentityDocument; import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument; import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId; @@ -22,7 +19,7 @@ import java.util.Base64; import java.util.Set; import java.util.TreeSet; -import static com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument.LEGACY_DEFAULT_DOCUMENT_VERSION; +import static com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION; import static java.nio.charset.StandardCharsets.UTF_8; /** @@ -32,25 +29,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; */ public class IdentityDocumentSigner { - public String generateSignature(String identityDocumentData, PrivateKey privateKey) { - try { - Signature signer = SignatureUtils.createSigner(privateKey); - signer.initSign(privateKey); - signer.update(identityDocumentData.getBytes(UTF_8)); - byte[] signature = signer.sign(); - return Base64.getEncoder().encodeToString(signature); - } catch (GeneralSecurityException e) { - throw new RuntimeException(e); - } - } - - public String generateLegacySignature(IdentityDocument doc, PrivateKey privateKey) { - return generateSignature(doc.providerUniqueId(), doc.providerService(), doc.configServerHostname(), - doc.instanceHostname(), doc.createdAt(), doc.ipAddresses(), doc.identityType(), privateKey, doc.serviceIdentity()); - } - // Cluster type is ignored due to old Vespa versions not forwarding unknown fields in signed identity document - private String generateSignature(VespaUniqueInstanceId providerUniqueId, + public String generateSignature(VespaUniqueInstanceId providerUniqueId, AthenzService providerService, String configServerHostname, String instanceHostname, @@ -74,32 +54,14 @@ public class IdentityDocumentSigner { } public boolean hasValidSignature(SignedIdentityDocument doc, PublicKey publicKey) { - if (doc instanceof LegacySignedIdentityDocument signedDoc) { - return validateLegacySignature(signedDoc, publicKey); - } else if (doc instanceof DefaultSignedIdentityDocument signedDoc) { - try { - Signature signer = SignatureUtils.createVerifier(publicKey); - signer.initVerify(publicKey); - signer.update(signedDoc.data().getBytes(UTF_8)); - return signer.verify(Base64.getDecoder().decode(doc.signature())); - } catch (GeneralSecurityException e) { - throw new RuntimeException(e); - } - } else { - throw new IllegalArgumentException("Unknown identity document type: " + doc.getClass().getName()); - } - } - - private boolean validateLegacySignature(SignedIdentityDocument doc, PublicKey publicKey) { try { - IdentityDocument iddoc = doc.identityDocument(); Signature signer = SignatureUtils.createVerifier(publicKey); signer.initVerify(publicKey); writeToSigner( - signer, iddoc.providerUniqueId(), iddoc.providerService(), iddoc.configServerHostname(), - iddoc.instanceHostname(), iddoc.createdAt(), iddoc.ipAddresses(), iddoc.identityType()); - if (doc.documentVersion() >= LEGACY_DEFAULT_DOCUMENT_VERSION) { - writeToSigner(signer, iddoc.serviceIdentity()); + signer, doc.providerUniqueId(), doc.providerService(), doc.configServerHostname(), + doc.instanceHostname(), doc.createdAt(), doc.ipAddresses(), doc.identityType()); + if (doc.documentVersion() >= DEFAULT_DOCUMENT_VERSION) { + writeToSigner(signer, doc.serviceIdentity()); } return signer.verify(Base64.getDecoder().decode(doc.signature())); } catch (GeneralSecurityException e) { diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapperTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapperTest.java index 513fb4cdbd3..2a68f6fd231 100644 --- a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapperTest.java +++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapperTest.java @@ -5,11 +5,8 @@ package com.yahoo.vespa.athenz.identityprovider.api; import org.junit.jupiter.api.Test; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Base64; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -18,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class EntityBindingsMapperTest { @Test - public void legacy_persists_unknown_json_members() throws IOException { + public void persists_unknown_json_members() throws IOException { var originalJson = """ { @@ -39,8 +36,7 @@ class EntityBindingsMapperTest { } """; var entity = EntityBindingsMapper.fromString(originalJson); - assertInstanceOf(LegacySignedIdentityDocument.class, entity); - assertEquals(2, entity.identityDocument().unknownAttributes().size(), entity.identityDocument().unknownAttributes().toString()); + assertEquals(2, entity.unknownAttributes().size(), entity.unknownAttributes().toString()); var json = EntityBindingsMapper.toAttestationData(entity); var expectedMemberInJson = "member-in-unknown-object"; @@ -49,39 +45,4 @@ class EntityBindingsMapperTest { assertEquals(EntityBindingsMapper.mapper.readTree(originalJson), EntityBindingsMapper.mapper.readTree(json)); } - @Test - public void reads_unknown_json_members() throws IOException { - var iddoc = """ - { - "provider-unique-id": "0.cluster.instance.app.tenant.us-west-1.test.node", - "provider-service": "domain.service", - "configserver-hostname": "cfg", - "instance-hostname": "host", - "created-at": 12345.0, - "ip-addresses": [], - "identity-type": "node", - "cluster-type": "admin", - "zts-url": "https://zts.url/", - "unknown-string": "string-value", - "unknown-object": { "member-in-unknown-object": 123 } - } - """; - var originalJson = - """ - { - "signature": "sig", - "signing-key-version": 0, - "document-version": 4, - "data": "%s" - } - """.formatted(Base64.getEncoder().encodeToString(iddoc.getBytes(StandardCharsets.UTF_8))); - var entity = EntityBindingsMapper.fromString(originalJson); - assertEquals(2, entity.identityDocument().unknownAttributes().size(), entity.identityDocument().unknownAttributes().toString()); - var json = EntityBindingsMapper.toAttestationData(entity); - - // For the new iddoc format the identity document should be unchanged during serialization/deserialization, - // i.e the signed identity document should be unchanged - assertEquals(EntityBindingsMapper.mapper.readTree(originalJson), EntityBindingsMapper.mapper.readTree(json)); - } - } \ No newline at end of file diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java index acb0905700f..ff85cb79f02 100644 --- a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java +++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java @@ -6,13 +6,10 @@ import com.yahoo.security.KeyUtils; import com.yahoo.vespa.athenz.api.AthenzIdentity; import com.yahoo.vespa.athenz.api.AthenzService; import com.yahoo.vespa.athenz.identityprovider.api.ClusterType; -import com.yahoo.vespa.athenz.identityprovider.api.DefaultSignedIdentityDocument; -import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper; -import com.yahoo.vespa.athenz.identityprovider.api.IdentityDocument; import com.yahoo.vespa.athenz.identityprovider.api.IdentityType; -import com.yahoo.vespa.athenz.identityprovider.api.LegacySignedIdentityDocument; import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument; import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId; +import com.yahoo.vespa.athenz.utils.AthenzIdentities; import org.junit.jupiter.api.Test; import java.security.KeyPair; @@ -21,7 +18,6 @@ import java.util.Arrays; import java.util.HashSet; import static com.yahoo.vespa.athenz.identityprovider.api.IdentityType.TENANT; -import static com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument.LEGACY_DEFAULT_DOCUMENT_VERSION; import static com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -45,54 +41,33 @@ public class IdentityDocumentSignerTest { private static final String ztsUrl = "https://foo"; private static final AthenzIdentity serviceIdentity = new AthenzService("vespa", "node"); - @Test - void legacy_generates_and_validates_signature() { - IdentityDocumentSigner signer = new IdentityDocumentSigner(); - IdentityDocument identityDocument = new IdentityDocument( - id, providerService, configserverHostname, - instanceHostname, createdAt, ipAddresses, identityType, clusterType, ztsUrl, serviceIdentity); - String signature = - signer.generateLegacySignature(identityDocument, keyPair.getPrivate()); - - SignedIdentityDocument signedIdentityDocument = new LegacySignedIdentityDocument( - signature, KEY_VERSION, LEGACY_DEFAULT_DOCUMENT_VERSION, identityDocument); - - assertTrue(signer.hasValidSignature(signedIdentityDocument, keyPair.getPublic())); - } - @Test void generates_and_validates_signature() { IdentityDocumentSigner signer = new IdentityDocumentSigner(); - IdentityDocument identityDocument = new IdentityDocument( - id, providerService, configserverHostname, - instanceHostname, createdAt, ipAddresses, identityType, clusterType, ztsUrl, serviceIdentity); - String data = EntityBindingsMapper.toIdentityDocmentData(identityDocument); String signature = - signer.generateSignature(data, keyPair.getPrivate()); + signer.generateSignature(id, providerService, configserverHostname, instanceHostname, createdAt, + ipAddresses, identityType, keyPair.getPrivate(), serviceIdentity); - SignedIdentityDocument signedIdentityDocument = new DefaultSignedIdentityDocument( - signature, KEY_VERSION, DEFAULT_DOCUMENT_VERSION, data); + SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument( + signature, KEY_VERSION, id, providerService, DEFAULT_DOCUMENT_VERSION, configserverHostname, + instanceHostname, createdAt, ipAddresses, identityType, clusterType, ztsUrl, serviceIdentity); assertTrue(signer.hasValidSignature(signedIdentityDocument, keyPair.getPublic())); } @Test - void legacy_ignores_cluster_type_and_zts_url() { + void ignores_cluster_type_and_zts_url() { IdentityDocumentSigner signer = new IdentityDocumentSigner(); - IdentityDocument identityDocument = new IdentityDocument( - id, providerService, configserverHostname, - instanceHostname, createdAt, ipAddresses, identityType, clusterType, ztsUrl, serviceIdentity); - IdentityDocument withoutIgnoredFields = new IdentityDocument( - id, providerService, configserverHostname, - instanceHostname, createdAt, ipAddresses, identityType, null, null, serviceIdentity); - String signature = - signer.generateLegacySignature(identityDocument, keyPair.getPrivate()); + signer.generateSignature(id, providerService, configserverHostname, instanceHostname, createdAt, + ipAddresses, identityType, keyPair.getPrivate(), serviceIdentity); - var docWithoutIgnoredFields = new LegacySignedIdentityDocument( - signature, KEY_VERSION, LEGACY_DEFAULT_DOCUMENT_VERSION, withoutIgnoredFields); - var docWithIgnoredFields = new LegacySignedIdentityDocument( - signature, KEY_VERSION, LEGACY_DEFAULT_DOCUMENT_VERSION, identityDocument); + var docWithoutIgnoredFields = new SignedIdentityDocument( + signature, KEY_VERSION, id, providerService, DEFAULT_DOCUMENT_VERSION, configserverHostname, + instanceHostname, createdAt, ipAddresses, identityType, null, null, serviceIdentity); + var docWithIgnoredFields = new SignedIdentityDocument( + signature, KEY_VERSION, id, providerService, DEFAULT_DOCUMENT_VERSION, configserverHostname, + instanceHostname, createdAt, ipAddresses, identityType, clusterType, ztsUrl, serviceIdentity); assertTrue(signer.hasValidSignature(docWithoutIgnoredFields, keyPair.getPublic())); assertEquals(docWithIgnoredFields.signature(), docWithoutIgnoredFields.signature()); @@ -101,15 +76,16 @@ public class IdentityDocumentSignerTest { @Test void validates_signature_for_new_and_old_versions() { IdentityDocumentSigner signer = new IdentityDocumentSigner(); - IdentityDocument identityDocument = new IdentityDocument( - id, providerService, configserverHostname, - instanceHostname, createdAt, ipAddresses, identityType, clusterType, ztsUrl, serviceIdentity); String signature = - signer.generateLegacySignature(identityDocument, keyPair.getPrivate()); + signer.generateSignature(id, providerService, configserverHostname, instanceHostname, createdAt, + ipAddresses, identityType, keyPair.getPrivate(), serviceIdentity); - SignedIdentityDocument signedIdentityDocument = new LegacySignedIdentityDocument( - signature, KEY_VERSION, LEGACY_DEFAULT_DOCUMENT_VERSION, identityDocument); + SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument( + signature, KEY_VERSION, id, providerService, DEFAULT_DOCUMENT_VERSION, configserverHostname, + instanceHostname, createdAt, ipAddresses, identityType, clusterType, ztsUrl, serviceIdentity); assertTrue(signer.hasValidSignature(signedIdentityDocument, keyPair.getPublic())); + } + } \ No newline at end of file -- cgit v1.2.3