aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@yahooinc.com>2023-02-02 16:11:08 +0100
committerBjørn Christian Seime <bjorncs@yahooinc.com>2023-02-03 12:23:24 +0100
commite09b191faf77bb95b923bb709b2181a0a3ee2c81 (patch)
tree42d28de543d81ca48bd0b2c4da53fe89f34d405f
parent3981141b25ee115f18529e74dacf21e1bf653fed (diff)
Add cluster type as SAN URI in Athenz instance certificates for Vespa
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGenerator.java17
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidator.java22
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializer.java6
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java13
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java3
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/identity/AthenzCredentialsMaintainer.java15
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/EntityBindingsMapper.java6
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/SignedIdentityDocument.java4
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/api/bindings/SignedIdentityDocumentEntity.java3
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java2
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java9
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/CsrGenerator.java19
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSigner.java13
-rw-r--r--vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java8
-rw-r--r--vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/InstanceCsrGeneratorTest.java2
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());
}
}