diff options
author | Bjørn Christian Seime <bjorncs@yahooinc.com> | 2023-02-02 16:11:08 +0100 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@yahooinc.com> | 2023-02-03 12:23:24 +0100 |
commit | e09b191faf77bb95b923bb709b2181a0a3ee2c81 (patch) | |
tree | 42d28de543d81ca48bd0b2c4da53fe89f34d405f | |
parent | 3981141b25ee115f18529e74dacf21e1bf653fed (diff) |
Add cluster type as SAN URI in Athenz instance certificates for Vespa
15 files changed, 86 insertions, 56 deletions
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGenerator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGenerator.java index 5b1a909e109..5042c8cf617 100644 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGenerator.java +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGenerator.java @@ -65,21 +65,14 @@ public class IdentityDocumentGenerator { String configServerHostname = HostName.getLocalhost(); Instant createdAt = Instant.now(); + var clusterType = allocation.membership().cluster().type().name(); String signature = signer.generateSignature( providerUniqueId, providerService, configServerHostname, - node.hostname(), createdAt, ips, identityType, privateKey); - + node.hostname(), createdAt, ips, identityType, clusterType, privateKey); return new SignedIdentityDocument( - signature, - athenzProviderServiceConfig.secretVersion(), - providerUniqueId, - providerService, - SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION, - configServerHostname, - node.hostname(), - createdAt, - ips, - identityType); + signature, athenzProviderServiceConfig.secretVersion(), providerUniqueId, providerService, + SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION, configServerHostname, node.hostname(), + createdAt, ips, identityType, clusterType); } catch (Exception e) { throw new RuntimeException("Exception generating identity document: " + e.getMessage(), e); } diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidator.java index 6b7a4835aee..82f486f1bc0 100644 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidator.java +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidator.java @@ -17,14 +17,16 @@ import com.yahoo.vespa.hosted.provision.Node; import com.yahoo.vespa.hosted.provision.NodeRepository; import java.net.InetAddress; +import java.net.URI; import java.security.PublicKey; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Stream; +import java.util.stream.Collectors; /** * Verifies that the instance's identity document is valid @@ -41,6 +43,7 @@ public class InstanceValidator { public static final String SAN_IPS_ATTRNAME = "sanIP"; public static final String SAN_DNS_ATTRNAME = "sanDNS"; + public static final String SAN_URI_ATTRNAME = "sanURI"; private final AthenzService tenantDockerContainerIdentity; private final IdentityDocumentSigner signer; @@ -163,6 +166,23 @@ public class InstanceValidator { log.log(Level.WARNING, "Invalid InstanceConfirmation, wrong ip in : " + vespaUniqueInstanceId); return false; } + + var urisCommaSeparated = confirmation.attributes.get(SAN_URI_ATTRNAME); + Set<URI> requestedUris; + try { + requestedUris = Optional.ofNullable(urisCommaSeparated).stream() + .flatMap(s -> Arrays.stream(s.split(","))).map(URI::create).collect(Collectors.toSet()); + } catch (IllegalArgumentException e) { + log.log(Level.WARNING, "Invalid SAN URIs: " + urisCommaSeparated); + return false; + } + var clusterType = node.allocation().map(a -> a.membership().cluster().type().name()).orElse(null); + Set<URI> allowedUris = clusterType != null + ? Set.of(URI.create("vespa://cluster-type/%s".formatted(clusterType))) : Set.of(); + if (!allowedUris.containsAll(requestedUris)) { + log.log(Level.WARNING, "Illegal SAN URIs: expected '%s' found '%s'".formatted(allowedUris, requestedUris)); + return false; + } return true; } diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializer.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializer.java index 83a7b850365..2e143bc53cc 100644 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializer.java +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializer.java @@ -47,6 +47,7 @@ public class InstanceSerializer { private static final String IDD_CREATED_AT_FIELD = "created-at"; private static final String IDD_IPADDRESSES_FIELD = "ip-addresses"; private static final String IDD_IDENTITY_TYPE_FIELD = "identity-type"; + private static final String IDD_CLUSTER_TYPE_FIELD = "cluster-type"; private static final ObjectMapper objectMapper = new ObjectMapper(); static { @@ -96,9 +97,12 @@ public class InstanceSerializer { Set<String> ips = new HashSet<>(); requireField(IDD_IPADDRESSES_FIELD, root).traverse((ArrayTraverser) (__, entry) -> ips.add(entry.asString())); IdentityType identityType = IdentityType.fromId(requireField(IDD_IDENTITY_TYPE_FIELD, root).asString()); + var clusterTypeField = root.field(IDD_CLUSTER_TYPE_FIELD); + var clusterType = root.valid() ? clusterTypeField.asString() : null; + return new SignedIdentityDocument(signature, (int)signingKeyVersion, providerUniqueId, athenzService, (int)documentVersion, - configserverHostname, instanceHostname, createdAt, ips, identityType); + configserverHostname, instanceHostname, createdAt, ips, identityType, clusterType); } private static Instant getJsr310Instant(double v) { diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java index 2b5165a815a..2014b74a3e6 100644 --- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java +++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java @@ -199,16 +199,9 @@ public class InstanceValidatorTest { private InstanceConfirmation createRegisterInstanceConfirmation(ApplicationId applicationId, String domain, String service) { VespaUniqueInstanceId vespaUniqueInstanceId = new VespaUniqueInstanceId(0, "default", applicationId.instance().value(), applicationId.application().value(), applicationId.tenant().value(), "us-north-1", "dev", IdentityType.NODE); - SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument(null, - 0, - vespaUniqueInstanceId, - new AthenzService(domain, service), - 0, - "localhost", - "localhost", - Instant.now(), - Collections.emptySet(), - IdentityType.NODE); + SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument( + null, 0, vespaUniqueInstanceId, new AthenzService(domain, service), 0, "localhost", "localhost", + Instant.now(), Collections.emptySet(), IdentityType.NODE, "container"); return createInstanceConfirmation(vespaUniqueInstanceId, domain, service, signedIdentityDocument); } diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java index df75e09b957..4e977324298 100644 --- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java +++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java @@ -46,7 +46,8 @@ public class InstanceSerializerTest { "instancehostname", Instant.now().truncatedTo(ChronoUnit.MICROS), // Truncate to the precision given from EntityBindingsMapper.toAttestationData() Collections.emptySet(), - IdentityType.NODE); + IdentityType.NODE, + "container"); var json = String.format("{\n" + " \"provider\": \"provider_prod_us-north-1\",\n" + 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 b903712254b..2e5d269b720 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 @@ -14,7 +14,6 @@ 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.IdentityDocumentClient; -import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument; import com.yahoo.vespa.athenz.identityprovider.client.CsrGenerator; import com.yahoo.vespa.athenz.identityprovider.client.DefaultIdentityDocumentClient; import com.yahoo.vespa.athenz.tls.AthenzIdentityVerifier; @@ -180,9 +179,9 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { private void registerIdentity(NodeAgentContext context, ContainerPath privateKeyFile, ContainerPath certificateFile, ContainerPath identityDocumentFile) { KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA); - SignedIdentityDocument signedIdentityDocument = identityDocumentClient.getNodeIdentityDocument(context.hostname().value()); + var doc = identityDocumentClient.getNodeIdentityDocument(context.hostname().value()); Pkcs10Csr csr = csrGenerator.generateInstanceCsr( - context.identity(), signedIdentityDocument.providerUniqueId(), signedIdentityDocument.ipAddresses(), keyPair); + context.identity(), doc.providerUniqueId(), doc.ipAddresses(), doc.clusterType(), keyPair); // Set up a hostname verified for zts if this is configured to use the config server (internal zts) apis HostnameVerifier ztsHostNameVerifier = useInternalZts @@ -193,19 +192,19 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { ztsClient.registerInstance( configserverIdentity, context.identity(), - EntityBindingsMapper.toAttestationData(signedIdentityDocument), + EntityBindingsMapper.toAttestationData(doc), csr); - EntityBindingsMapper.writeSignedIdentityDocumentToFile(identityDocumentFile, signedIdentityDocument); + EntityBindingsMapper.writeSignedIdentityDocumentToFile(identityDocumentFile, doc); writePrivateKeyAndCertificate(privateKeyFile, keyPair.getPrivate(), certificateFile, instanceIdentity.certificate()); context.log(logger, "Instance successfully registered and credentials written to file"); } } private void refreshIdentity(NodeAgentContext context, ContainerPath privateKeyFile, ContainerPath certificateFile, ContainerPath identityDocumentFile) { - SignedIdentityDocument identityDocument = EntityBindingsMapper.readSignedIdentityDocumentFromFile(identityDocumentFile); + var doc = EntityBindingsMapper.readSignedIdentityDocumentFromFile(identityDocumentFile); KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA); Pkcs10Csr csr = csrGenerator.generateInstanceCsr( - context.identity(), identityDocument.providerUniqueId(), identityDocument.ipAddresses(), keyPair); + context.identity(), doc.providerUniqueId(), doc.ipAddresses(), doc.clusterType(), keyPair); SSLContext containerIdentitySslContext = new SslContextBuilder().withKeyStore(privateKeyFile, certificateFile) .withTrustStore(ztsTrustStorePath) @@ -221,7 +220,7 @@ public class AthenzCredentialsMaintainer implements CredentialsMaintainer { ztsClient.refreshInstance( configserverIdentity, context.identity(), - identityDocument.providerUniqueId().asDottedString(), + doc.providerUniqueId().asDottedString(), csr); writePrivateKeyAndCertificate(privateKeyFile, keyPair.getPrivate(), certificateFile, instanceIdentity.certificate()); context.log(logger, "Instance successfully refreshed and credentials written to file"); 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 5e887cc4df4..0949192b884 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 @@ -47,7 +47,8 @@ public class EntityBindingsMapper { entity.instanceHostname(), entity.createdAt(), entity.ipAddresses(), - IdentityType.fromId(entity.identityType())); + IdentityType.fromId(entity.identityType()), + entity.clusterType()); } public static SignedIdentityDocumentEntity toSignedIdentityDocumentEntity(SignedIdentityDocument model) { @@ -61,7 +62,8 @@ public class EntityBindingsMapper { model.instanceHostname(), model.createdAt(), model.ipAddresses(), - model.identityType().id()); + model.identityType().id(), + model.clusterType()); } public static SignedIdentityDocument readSignedIdentityDocumentFromFile(Path file) { 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 0f941ea5bf0..c2d8ece84a5 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 @@ -14,7 +14,7 @@ import java.util.Set; public record SignedIdentityDocument(String signature, int signingKeyVersion, VespaUniqueInstanceId providerUniqueId, AthenzService providerService, int documentVersion, String configServerHostname, String instanceHostname, Instant createdAt, Set<String> ipAddresses, - IdentityType identityType) { - public static final int DEFAULT_DOCUMENT_VERSION = 1; + IdentityType identityType, String clusterType) { + public static final int DEFAULT_DOCUMENT_VERSION = 2; } 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 868b41b6689..2fb709615da 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 @@ -20,5 +20,6 @@ public record SignedIdentityDocumentEntity(@JsonProperty("signature") String sig @JsonProperty("instance-hostname") String instanceHostname, @JsonProperty("created-at") Instant createdAt, @JsonProperty("ip-addresses") Set<String> ipAddresses, - @JsonProperty("identity-type") String identityType) { + @JsonProperty("identity-type") String identityType, + @JsonProperty("cluster-type") String clusterType) { } 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 91b95cb6084..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 @@ -79,6 +79,7 @@ class AthenzCredentialsService { tenantIdentity, document.providerUniqueId(), document.ipAddresses(), + document.clusterType(), keyPair); try (ZtsClient ztsClient = new DefaultZtsClient.Builder(ztsEndpoint).withIdentityProvider(nodeIdentityProvider).build()) { @@ -100,6 +101,7 @@ class AthenzCredentialsService { tenantIdentity, document.providerUniqueId(), document.ipAddresses(), + document.clusterType(), newKeyPair); try (ZtsClient ztsClient = new DefaultZtsClient.Builder(ztsEndpoint).withSslContext(sslContext).build()) { 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 0caf3bdfd0b..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 @@ -4,18 +4,18 @@ package com.yahoo.vespa.athenz.identityprovider.client; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import com.yahoo.component.annotation.Inject; import com.yahoo.component.AbstractComponent; +import com.yahoo.component.annotation.Inject; import com.yahoo.container.core.identity.IdentityConfig; import com.yahoo.container.jdisc.athenz.AthenzIdentityProvider; import com.yahoo.container.jdisc.athenz.AthenzIdentityProviderException; import com.yahoo.jdisc.Metric; import com.yahoo.metrics.ContainerMetrics; import com.yahoo.security.KeyStoreBuilder; +import com.yahoo.security.MutableX509KeyManager; import com.yahoo.security.Pkcs10Csr; import com.yahoo.security.SslContextBuilder; import com.yahoo.security.X509CertificateWithKey; -import com.yahoo.security.MutableX509KeyManager; import com.yahoo.vespa.athenz.api.AthenzAccessToken; import com.yahoo.vespa.athenz.api.AthenzDomain; import com.yahoo.vespa.athenz.api.AthenzRole; @@ -48,7 +48,6 @@ import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; import static com.yahoo.security.KeyStoreType.PKCS12; @@ -299,7 +298,9 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen } private X509Certificate requestRoleCertificate(AthenzRole role) { - Pkcs10Csr csr = csrGenerator.generateRoleCsr(identity, role, credentials.getIdentityDocument().providerUniqueId(), credentials.getKeyPair()); + var doc = credentials.getIdentityDocument(); + Pkcs10Csr csr = csrGenerator.generateRoleCsr( + identity, role, doc.providerUniqueId(), doc.clusterType(), credentials.getKeyPair()); try (ZtsClient client = createZtsClient()) { X509Certificate roleCertificate = client.getRoleCertificate(role, csr); updateRoleKeyManager(role, roleCertificate); diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/CsrGenerator.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/CsrGenerator.java index 21ce30fd244..bafeab805bd 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/CsrGenerator.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/CsrGenerator.java @@ -1,12 +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.client; -import com.yahoo.vespa.athenz.api.AthenzIdentity; -import com.yahoo.vespa.athenz.api.AthenzRole; -import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId; import com.yahoo.security.Pkcs10Csr; import com.yahoo.security.Pkcs10CsrBuilder; import com.yahoo.security.SubjectAlternativeName; +import com.yahoo.vespa.athenz.api.AthenzIdentity; +import com.yahoo.vespa.athenz.api.AthenzRole; +import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId; import javax.security.auth.x500.X500Principal; import java.security.KeyPair; @@ -14,8 +14,9 @@ import java.util.Set; import static com.yahoo.security.SignatureAlgorithm.SHA256_WITH_RSA; import static com.yahoo.security.SubjectAlternativeName.Type.DNS; -import static com.yahoo.security.SubjectAlternativeName.Type.IP; import static com.yahoo.security.SubjectAlternativeName.Type.EMAIL; +import static com.yahoo.security.SubjectAlternativeName.Type.IP; +import static com.yahoo.security.SubjectAlternativeName.Type.URI; /** * Generates a {@link Pkcs10Csr} for an instance. @@ -35,6 +36,7 @@ public class CsrGenerator { public Pkcs10Csr generateInstanceCsr(AthenzIdentity instanceIdentity, VespaUniqueInstanceId instanceId, Set<String> ipAddresses, + String clusterType, KeyPair keyPair) { X500Principal subject = new X500Principal(String.format("OU=%s, CN=%s", providerService, instanceIdentity.getFullName())); // Add SAN dnsname <service>.<domain-with-dashes>.<provider-dnsname-suffix> @@ -48,6 +50,7 @@ public class CsrGenerator { instanceIdentity.getDomainName().replace(".", "-"), dnsSuffix)) .addSubjectAlternativeName(DNS, getIdentitySAN(instanceId)); + if (clusterType != null) pkcs10CsrBuilder.addSubjectAlternativeName(URI, "vespa://cluster-type/%s".formatted(clusterType)); ipAddresses.forEach(ip -> pkcs10CsrBuilder.addSubjectAlternativeName(new SubjectAlternativeName(IP, ip))); return pkcs10CsrBuilder.build(); } @@ -55,12 +58,14 @@ public class CsrGenerator { public Pkcs10Csr generateRoleCsr(AthenzIdentity identity, AthenzRole role, VespaUniqueInstanceId instanceId, + String clusterType, KeyPair keyPair) { X500Principal principal = new X500Principal(String.format("OU=%s, cn=%s:role.%s", providerService, role.domain().getName(), role.roleName())); - return Pkcs10CsrBuilder.fromKeypair(principal, keyPair, SHA256_WITH_RSA) + var b = Pkcs10CsrBuilder.fromKeypair(principal, keyPair, SHA256_WITH_RSA) .addSubjectAlternativeName(DNS, getIdentitySAN(instanceId)) - .addSubjectAlternativeName(EMAIL, String.format("%s.%s@%s", identity.getDomainName(), identity.getName(), dnsSuffix)) - .build(); + .addSubjectAlternativeName(EMAIL, String.format("%s.%s@%s", identity.getDomainName(), identity.getName(), dnsSuffix)); + if (clusterType != null) b.addSubjectAlternativeName(URI, "vespa://cluster-type/%s".formatted(clusterType)); + return b.build(); } private String getIdentitySAN(VespaUniqueInstanceId instanceId) { 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 1c1dcb655c0..13bea80dfed 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 @@ -34,11 +34,14 @@ public class IdentityDocumentSigner { Instant createdAt, Set<String> ipAddresses, IdentityType identityType, + String clusterType, PrivateKey privateKey) { try { Signature signer = SignatureUtils.createSigner(privateKey); signer.initSign(privateKey); - writeToSigner(signer, providerUniqueId, providerService, configServerHostname, instanceHostname, createdAt, ipAddresses, identityType); + writeToSigner( + signer, providerUniqueId, providerService, configServerHostname, instanceHostname, createdAt, + ipAddresses, identityType, clusterType); byte[] signature = signer.sign(); return Base64.getEncoder().encodeToString(signature); } catch (GeneralSecurityException e) { @@ -50,7 +53,9 @@ public class IdentityDocumentSigner { try { Signature signer = SignatureUtils.createVerifier(publicKey); signer.initVerify(publicKey); - writeToSigner(signer, doc.providerUniqueId(), doc.providerService(), doc.configServerHostname(), doc.instanceHostname(), doc.createdAt(), doc.ipAddresses(), doc.identityType()); + writeToSigner( + signer, doc.providerUniqueId(), doc.providerService(), doc.configServerHostname(), + doc.instanceHostname(), doc.createdAt(), doc.ipAddresses(), doc.identityType(), doc.clusterType()); return signer.verify(Base64.getDecoder().decode(doc.signature())); } catch (GeneralSecurityException e) { throw new RuntimeException(e); @@ -64,7 +69,8 @@ public class IdentityDocumentSigner { String instanceHostname, Instant createdAt, Set<String> ipAddresses, - IdentityType identityType) throws SignatureException { + IdentityType identityType, + String clusterType) throws SignatureException { signer.update(providerUniqueId.asDottedString().getBytes(UTF_8)); signer.update(providerService.getFullName().getBytes(UTF_8)); signer.update(configServerHostname.getBytes(UTF_8)); @@ -76,5 +82,6 @@ public class IdentityDocumentSigner { signer.update(ipAddress.getBytes(UTF_8)); } signer.update(identityType.id().getBytes(UTF_8)); + if (clusterType != null) signer.update(clusterType.getBytes(UTF_8)); } } 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 5253fee0802..ede006539a1 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 @@ -36,12 +36,14 @@ public class IdentityDocumentSignerTest { String instanceHostname = "instancehostname"; Instant createdAt = Instant.EPOCH; HashSet<String> ipAddresses = new HashSet<>(Arrays.asList("1.2.3.4", "::1")); + String clusterType = "container"; String signature = - signer.generateSignature(id, providerService, configserverHostname, instanceHostname, createdAt, ipAddresses, identityType, keyPair.getPrivate()); + signer.generateSignature(id, providerService, configserverHostname, instanceHostname, createdAt, + ipAddresses, identityType, clusterType, keyPair.getPrivate()); SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument( - signature, KEY_VERSION, id, providerService, - DEFAULT_DOCUMENT_VERSION, configserverHostname, instanceHostname, createdAt, ipAddresses, identityType); + signature, KEY_VERSION, id, providerService, DEFAULT_DOCUMENT_VERSION, configserverHostname, + instanceHostname, createdAt, ipAddresses, identityType, clusterType); assertTrue(signer.hasValidSignature(signedIdentityDocument, keyPair.getPublic())); } diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/InstanceCsrGeneratorTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/InstanceCsrGeneratorTest.java index 3e4f8541aaa..aa4c3e68094 100644 --- a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/InstanceCsrGeneratorTest.java +++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/InstanceCsrGeneratorTest.java @@ -31,7 +31,7 @@ public class InstanceCsrGeneratorTest { VespaUniqueInstanceId vespaUniqueInstanceId = VespaUniqueInstanceId.fromDottedString("0.default.default.foo-app.vespa.us-north-1.prod.node"); KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA); - Pkcs10Csr csr = csrGenerator.generateInstanceCsr(service, vespaUniqueInstanceId, Collections.emptySet(), keyPair); + Pkcs10Csr csr = csrGenerator.generateInstanceCsr(service, vespaUniqueInstanceId, Collections.emptySet(), "container", keyPair); assertEquals(new X500Principal(String.format("OU=%s, CN=%s", PROVIDER_SERVICE, ATHENZ_SERVICE)), csr.getSubject()); } } |