From f582c067ade67091d49f9ae31a2b8668c3d860fe Mon Sep 17 00:00:00 2001 From: Bjørn Christian Seime Date: Mon, 22 Mar 2021 16:48:47 +0100 Subject: Move classes to single package root --- .../instanceproviderservice/CkmsKeyProvider.java | 64 ++++++ .../IdentityDocumentGenerator.java | 90 +++++++++ .../IdentityProviderRequestHandler.java | 3 - .../InstanceConfirmation.java | 100 +++++++++ .../instanceproviderservice/InstanceValidator.java | 221 ++++++++++++++++++++ .../athenz/instanceproviderservice/Utils.java | 24 +++ .../IdentityDocumentGenerator.java | 90 --------- .../impl/CkmsKeyProvider.java | 64 ------ .../athenz/instanceproviderservice/impl/Utils.java | 24 --- .../instanceconfirmation/InstanceConfirmation.java | 101 ---------- .../instanceconfirmation/InstanceValidator.java | 223 --------------------- .../ca/restapi/CertificateAuthorityApiHandler.java | 5 +- 12 files changed, 501 insertions(+), 508 deletions(-) create mode 100644 athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/CkmsKeyProvider.java create mode 100644 athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGenerator.java create mode 100644 athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceConfirmation.java create mode 100644 athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidator.java create mode 100644 athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/Utils.java delete mode 100644 athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java delete mode 100644 athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/CkmsKeyProvider.java delete mode 100644 athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/Utils.java delete mode 100644 athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmation.java delete mode 100644 athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java (limited to 'athenz-identity-provider-service/src/main') diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/CkmsKeyProvider.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/CkmsKeyProvider.java new file mode 100644 index 00000000000..88603dff57d --- /dev/null +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/CkmsKeyProvider.java @@ -0,0 +1,64 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.athenz.instanceproviderservice; + +import com.google.inject.Inject; +import com.yahoo.config.provision.Zone; +import com.yahoo.container.jdisc.secretstore.SecretStore; +import com.yahoo.security.KeyUtils; +import com.yahoo.vespa.hosted.athenz.instanceproviderservice.KeyProvider; +import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig; + +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.HashMap; +import java.util.Map; + +/** + * @author mortent + * @author bjorncs + */ +@SuppressWarnings("unused") // Injected component +public class CkmsKeyProvider implements KeyProvider { + + private final SecretStore secretStore; + private final String secretName; + private final Map secrets; + + @Inject + public CkmsKeyProvider(SecretStore secretStore, + Zone zone, + AthenzProviderServiceConfig config) { + this.secretStore = secretStore; + this.secretName = config.secretName(); + this.secrets = new HashMap<>(); + } + + @Override + public PrivateKey getPrivateKey(int version) { + return getKeyPair(version).getPrivate(); + } + + @Override + public PublicKey getPublicKey(int version) { + return getKeyPair(version).getPublic(); + } + + @Override + public KeyPair getKeyPair(int version) { + synchronized (secrets) { + KeyPair keyPair = secrets.get(version); + if (keyPair == null) { + keyPair = readKeyPair(version); + secrets.put(version, keyPair); + } + return keyPair; + } + } + + private KeyPair readKeyPair(int version) { + PrivateKey privateKey = KeyUtils.fromPemEncodedPrivateKey(secretStore.getSecret(secretName, version)); + PublicKey publicKey = KeyUtils.extractPublicKey(privateKey); + return new KeyPair(publicKey, privateKey); + } +} 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 new file mode 100644 index 00000000000..3ea8eb1f538 --- /dev/null +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGenerator.java @@ -0,0 +1,90 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.athenz.instanceproviderservice; + +import com.google.inject.Inject; +import com.yahoo.config.provision.Zone; +import com.yahoo.net.HostName; +import com.yahoo.vespa.athenz.api.AthenzService; +import com.yahoo.vespa.athenz.identityprovider.api.IdentityType; +import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument; +import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId; +import com.yahoo.vespa.athenz.identityprovider.client.IdentityDocumentSigner; +import com.yahoo.vespa.hosted.athenz.instanceproviderservice.KeyProvider; +import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig; +import com.yahoo.vespa.hosted.provision.Node; +import com.yahoo.vespa.hosted.provision.NodeRepository; +import com.yahoo.vespa.hosted.provision.node.Allocation; + +import java.security.PrivateKey; +import java.time.Instant; +import java.util.HashSet; +import java.util.Set; + +/** + * Generates a signed identity document for a given hostname and type + * + * @author mortent + * @author bjorncs + */ +public class IdentityDocumentGenerator { + + private final IdentityDocumentSigner signer = new IdentityDocumentSigner(); + private final NodeRepository nodeRepository; + private final Zone zone; + private final KeyProvider keyProvider; + private final AthenzProviderServiceConfig athenzProviderServiceConfig; + + @Inject + public IdentityDocumentGenerator(AthenzProviderServiceConfig config, + NodeRepository nodeRepository, + Zone zone, + KeyProvider keyProvider) { + this.athenzProviderServiceConfig = config; + this.nodeRepository = nodeRepository; + this.zone = zone; + this.keyProvider = keyProvider; + } + + public SignedIdentityDocument generateSignedIdentityDocument(String hostname, IdentityType identityType) { + try { + Node node = nodeRepository.nodes().node(hostname).orElseThrow(() -> new RuntimeException("Unable to find node " + hostname)); + Allocation allocation = node.allocation().orElseThrow(() -> new RuntimeException("No allocation for node " + node.hostname())); + VespaUniqueInstanceId providerUniqueId = new VespaUniqueInstanceId( + allocation.membership().index(), + allocation.membership().cluster().id().value(), + allocation.owner().instance().value(), + allocation.owner().application().value(), + allocation.owner().tenant().value(), + zone.region().value(), + zone.environment().value(), + identityType); + + Set ips = new HashSet<>(node.ipConfig().primary()); + + PrivateKey privateKey = keyProvider.getPrivateKey(athenzProviderServiceConfig.secretVersion()); + AthenzService providerService = new AthenzService(athenzProviderServiceConfig.domain(), athenzProviderServiceConfig.serviceName()); + + String configServerHostname = HostName.getLocalhost(); + Instant createdAt = Instant.now(); + String signature = signer.generateSignature( + providerUniqueId, providerService, configServerHostname, + node.hostname(), createdAt, ips, identityType, privateKey); + + return new SignedIdentityDocument( + signature, + SignedIdentityDocument.DEFAULT_KEY_VERSION, + providerUniqueId, + providerService, + SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION, + configServerHostname, + node.hostname(), + createdAt, + ips, + identityType); + } 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/IdentityProviderRequestHandler.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityProviderRequestHandler.java index ef98893c0f4..8593401b887 100644 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityProviderRequestHandler.java +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityProviderRequestHandler.java @@ -13,9 +13,6 @@ import com.yahoo.restapi.RestApiRequestHandler; import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper; import com.yahoo.vespa.athenz.identityprovider.api.IdentityType; import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity; -import com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument.IdentityDocumentGenerator; -import com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation.InstanceConfirmation; -import com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation.InstanceValidator; import java.util.logging.Level; diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceConfirmation.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceConfirmation.java new file mode 100644 index 00000000000..65829865625 --- /dev/null +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceConfirmation.java @@ -0,0 +1,100 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.athenz.instanceproviderservice; + +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.yahoo.restapi.RestApi; +import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * InstanceConfirmation object as per Athenz InstanceConfirmation API. + * + * @author bjorncs + */ +public class InstanceConfirmation implements RestApi.JacksonRequestEntity { + + @JsonProperty("provider") public final String provider; + @JsonProperty("domain") public final String domain; + @JsonProperty("service") public final String service; + + @JsonProperty("attestationData") @JsonSerialize(using = SignedIdentitySerializer.class) + public final SignedIdentityDocumentEntity signedIdentityDocument; + @JsonUnwrapped public final Map attributes = new HashMap<>(); // optional attributes that Athenz may provide + + @JsonCreator + public InstanceConfirmation(@JsonProperty("provider") String provider, + @JsonProperty("domain") String domain, + @JsonProperty("service") String service, + @JsonProperty("attestationData") @JsonDeserialize(using = SignedIdentityDeserializer.class) + SignedIdentityDocumentEntity signedIdentityDocument) { + this.provider = provider; + this.domain = domain; + this.service = service; + this.signedIdentityDocument = signedIdentityDocument; + } + + @JsonAnySetter + public void set(String name, String value) { + attributes.put(name, value); + } + + @Override + public String toString() { + return "InstanceConfirmation{" + + "provider='" + provider + '\'' + + ", domain='" + domain + '\'' + + ", service='" + service + '\'' + + ", signedIdentityDocument='" + signedIdentityDocument + '\'' + + ", attributes=" + attributes + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + InstanceConfirmation that = (InstanceConfirmation) o; + return Objects.equals(provider, that.provider) && + Objects.equals(domain, that.domain) && + Objects.equals(service, that.service) && + Objects.equals(signedIdentityDocument, that.signedIdentityDocument) && + Objects.equals(attributes, that.attributes); + } + + @Override + public int hashCode() { + return Objects.hash(provider, domain, service, signedIdentityDocument, attributes); + } + + public static class SignedIdentityDeserializer extends JsonDeserializer { + @Override + public SignedIdentityDocumentEntity deserialize( + JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + String value = jsonParser.getValueAsString(); + return Utils.getMapper().readValue(value, SignedIdentityDocumentEntity.class); + } + } + + public static class SignedIdentitySerializer extends JsonSerializer { + @Override + public void serialize( + SignedIdentityDocumentEntity document, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(Utils.getMapper().writeValueAsString(document)); + } + } +} 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 new file mode 100644 index 00000000000..3dcb5a13d6d --- /dev/null +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidator.java @@ -0,0 +1,221 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.athenz.instanceproviderservice; + +import com.google.common.net.InetAddresses; +import com.google.inject.Inject; +import com.yahoo.config.model.api.ApplicationInfo; +import com.yahoo.config.model.api.ServiceInfo; +import com.yahoo.config.model.api.SuperModelProvider; +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.vespa.athenz.api.AthenzService; +import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper; +import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument; +import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId; +import com.yahoo.vespa.athenz.identityprovider.client.IdentityDocumentSigner; +import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig; +import com.yahoo.vespa.hosted.provision.Node; +import com.yahoo.vespa.hosted.provision.NodeRepository; + +import java.net.InetAddress; +import java.security.PublicKey; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Verifies that the instance's identity document is valid + * + * @author bjorncs + * @author mortent + */ +public class InstanceValidator { + + private static final Logger log = Logger.getLogger(InstanceValidator.class.getName()); + static final String SERVICE_PROPERTIES_DOMAIN_KEY = "identity.domain"; + static final String SERVICE_PROPERTIES_SERVICE_KEY = "identity.service"; + static final String INSTANCE_ID_DELIMITER = ".instanceid.athenz."; + + public static final String SAN_IPS_ATTRNAME = "sanIP"; + public static final String SAN_DNS_ATTRNAME = "sanDNS"; + + private final AthenzService tenantDockerContainerIdentity; + private final IdentityDocumentSigner signer; + private final KeyProvider keyProvider; + private final SuperModelProvider superModelProvider; + private final NodeRepository nodeRepository; + + @Inject + public InstanceValidator(KeyProvider keyProvider, + SuperModelProvider superModelProvider, + NodeRepository nodeRepository, + AthenzProviderServiceConfig config) { + this(keyProvider, superModelProvider, nodeRepository, new IdentityDocumentSigner(), new AthenzService(config.tenantService())); + } + + public InstanceValidator(KeyProvider keyProvider, + SuperModelProvider superModelProvider, + NodeRepository nodeRepository, + IdentityDocumentSigner identityDocumentSigner, + AthenzService tenantIdentity){ + this.keyProvider = keyProvider; + this.superModelProvider = superModelProvider; + this.nodeRepository = nodeRepository; + this.signer = identityDocumentSigner; + this.tenantDockerContainerIdentity = tenantIdentity; + } + + public boolean isValidInstance(InstanceConfirmation instanceConfirmation) { + SignedIdentityDocument signedIdentityDocument = EntityBindingsMapper.toSignedIdentityDocument(instanceConfirmation.signedIdentityDocument); + VespaUniqueInstanceId providerUniqueId = signedIdentityDocument.providerUniqueId(); + ApplicationId applicationId = ApplicationId.from( + providerUniqueId.tenant(), providerUniqueId.application(), providerUniqueId.instance()); + + VespaUniqueInstanceId csrProviderUniqueId = getVespaUniqueInstanceId(instanceConfirmation); + if(! providerUniqueId.equals(csrProviderUniqueId)) { + log.log(Level.WARNING, String.format("Instance %s has invalid provider unique ID in CSR (%s)", providerUniqueId, csrProviderUniqueId)); + return false; + } + + if (! isSameIdentityAsInServicesXml(applicationId, instanceConfirmation.domain, instanceConfirmation.service)) { + return false; + } + + log.log(Level.FINE, () -> String.format("Validating instance %s.", providerUniqueId)); + + PublicKey publicKey = keyProvider.getPublicKey(signedIdentityDocument.signingKeyVersion()); + if (signer.hasValidSignature(signedIdentityDocument, publicKey)) { + log.log(Level.FINE, () -> String.format("Instance %s is valid.", providerUniqueId)); + return true; + } + log.log(Level.SEVERE, () -> String.format("Instance %s has invalid signature.", providerUniqueId)); + return false; + } + + // TODO Add actual validation. Cannot reuse isValidInstance as identity document is not part of the refresh request. + // We'll have to perform some validation on the instance id and other fields of the attribute map. + // Separate between tenant and node certificate as well. + public boolean isValidRefresh(InstanceConfirmation confirmation) { + log.log(Level.FINE, () -> String.format("Accepting refresh for instance with identity '%s', provider '%s', instanceId '%s'.", + new AthenzService(confirmation.domain, confirmation.service).getFullName(), + confirmation.provider, + confirmation.attributes.get(SAN_DNS_ATTRNAME))); + try { + return validateAttributes(confirmation, getVespaUniqueInstanceId(confirmation)); + } catch (Exception e) { + log.log(Level.WARNING, "Encountered exception while refreshing certificate for confirmation: " + confirmation, e); + return false; + } + } + + private VespaUniqueInstanceId getVespaUniqueInstanceId(InstanceConfirmation instanceConfirmation) { + // Find a list of SAN DNS + List sanDNS = Optional.ofNullable(instanceConfirmation.attributes.get(SAN_DNS_ATTRNAME)) + .map(s -> s.split(",")) + .map(Arrays::asList) + .map(List::stream) + .orElse(Stream.empty()) + .collect(Collectors.toList()); + + return sanDNS.stream() + .filter(dns -> dns.contains(INSTANCE_ID_DELIMITER)) + .findFirst() + .map(s -> s.replaceAll(INSTANCE_ID_DELIMITER + ".*", "")) + .map(VespaUniqueInstanceId::fromDottedString) + .orElse(null); + } + + private boolean validateAttributes(InstanceConfirmation confirmation, VespaUniqueInstanceId vespaUniqueInstanceId) { + if(vespaUniqueInstanceId == null) { + log.log(Level.WARNING, "Unable to find unique instance ID in refresh request: " + confirmation.toString()); + return false; + } + + // Find node matching vespa unique id + Node node = nodeRepository.nodes().list().stream() + .filter(n -> n.allocation().isPresent()) + .filter(n -> nodeMatchesVespaUniqueId(n, vespaUniqueInstanceId)) + .findFirst() // Should be only one + .orElse(null); + if(node == null) { + log.log(Level.WARNING, "Invalid InstanceConfirmation, No nodes matching uniqueId: " + vespaUniqueInstanceId); + return false; + } + + // Find list of ipaddresses + List ips = Optional.ofNullable(confirmation.attributes.get(SAN_IPS_ATTRNAME)) + .map(s -> s.split(",")) + .map(Arrays::asList) + .map(List::stream) + .orElse(Stream.empty()) + .map(InetAddresses::forString) + .collect(Collectors.toList()); + + List nodeIpAddresses = node.ipConfig().primary().stream() + .map(InetAddresses::forString) + .collect(Collectors.toList()); + + // Validate that ipaddresses in request are valid for node + + if(! nodeIpAddresses.containsAll(ips)) { + log.log(Level.WARNING, "Invalid InstanceConfirmation, wrong ip in : " + vespaUniqueInstanceId); + return false; + } + return true; + } + + private boolean nodeMatchesVespaUniqueId(Node node, VespaUniqueInstanceId vespaUniqueInstanceId) { + return node.allocation().map(allocation -> + allocation.membership().index() == vespaUniqueInstanceId.clusterIndex() && + allocation.membership().cluster().id().value().equals(vespaUniqueInstanceId.clusterId()) && + allocation.owner().instance().value().equals(vespaUniqueInstanceId.instance()) && + allocation.owner().application().value().equals(vespaUniqueInstanceId.application()) && + allocation.owner().tenant().value().equals(vespaUniqueInstanceId.tenant())) + .orElse(false); + } + + // If/when we don't care about logging exactly whats wrong, this can be simplified + // TODO Use identity type to determine if this check should be performed + private boolean isSameIdentityAsInServicesXml(ApplicationId applicationId, String domain, String service) { + + Optional applicationInfo = superModelProvider.getSuperModel().getApplicationInfo(applicationId); + + if (!applicationInfo.isPresent()) { + log.info(String.format("Could not find application info for %s, existing applications: %s", + applicationId.serializedForm(), + superModelProvider.getSuperModel().getAllApplicationInfos())); + return false; + } + + if (tenantDockerContainerIdentity.equals(new AthenzService(domain, service))) { + return true; + } + + Optional matchingServiceInfo = applicationInfo.get() + .getModel() + .getHosts() + .stream() + .flatMap(hostInfo -> hostInfo.getServices().stream()) + .filter(serviceInfo -> serviceInfo.getProperty(SERVICE_PROPERTIES_DOMAIN_KEY).isPresent()) + .filter(serviceInfo -> serviceInfo.getProperty(SERVICE_PROPERTIES_SERVICE_KEY).isPresent()) + .findFirst(); + + if (!matchingServiceInfo.isPresent()) { + log.info(String.format("Application %s has not specified domain/service", applicationId.serializedForm())); + return false; + } + + String domainInConfig = matchingServiceInfo.get().getProperty(SERVICE_PROPERTIES_DOMAIN_KEY).get(); + String serviceInConfig = matchingServiceInfo.get().getProperty(SERVICE_PROPERTIES_SERVICE_KEY).get(); + if (!domainInConfig.equals(domain) || !serviceInConfig.equals(service)) { + log.warning(String.format("domain '%s' or service '%s' does not match the one in config for application %s", + domain, service, applicationId.serializedForm())); + return false; + } + + return true; + } +} diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/Utils.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/Utils.java new file mode 100644 index 00000000000..4ee64044da8 --- /dev/null +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/Utils.java @@ -0,0 +1,24 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.athenz.instanceproviderservice; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +/** + * @author bjorncs + */ +public class Utils { + + private static final ObjectMapper mapper = createObjectMapper(); + + public static ObjectMapper getMapper() { + return mapper; + } + + private static ObjectMapper createObjectMapper() { + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + return mapper; + } + +} diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java deleted file mode 100644 index b2ae42cc294..00000000000 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument; - -import com.google.inject.Inject; -import com.yahoo.config.provision.Zone; -import com.yahoo.net.HostName; -import com.yahoo.vespa.athenz.api.AthenzService; -import com.yahoo.vespa.athenz.identityprovider.api.IdentityType; -import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument; -import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId; -import com.yahoo.vespa.athenz.identityprovider.client.IdentityDocumentSigner; -import com.yahoo.vespa.hosted.athenz.instanceproviderservice.KeyProvider; -import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig; -import com.yahoo.vespa.hosted.provision.Node; -import com.yahoo.vespa.hosted.provision.NodeRepository; -import com.yahoo.vespa.hosted.provision.node.Allocation; - -import java.security.PrivateKey; -import java.time.Instant; -import java.util.HashSet; -import java.util.Set; - -/** - * Generates a signed identity document for a given hostname and type - * - * @author mortent - * @author bjorncs - */ -public class IdentityDocumentGenerator { - - private final IdentityDocumentSigner signer = new IdentityDocumentSigner(); - private final NodeRepository nodeRepository; - private final Zone zone; - private final KeyProvider keyProvider; - private final AthenzProviderServiceConfig athenzProviderServiceConfig; - - @Inject - public IdentityDocumentGenerator(AthenzProviderServiceConfig config, - NodeRepository nodeRepository, - Zone zone, - KeyProvider keyProvider) { - this.athenzProviderServiceConfig = config; - this.nodeRepository = nodeRepository; - this.zone = zone; - this.keyProvider = keyProvider; - } - - public SignedIdentityDocument generateSignedIdentityDocument(String hostname, IdentityType identityType) { - try { - Node node = nodeRepository.nodes().node(hostname).orElseThrow(() -> new RuntimeException("Unable to find node " + hostname)); - Allocation allocation = node.allocation().orElseThrow(() -> new RuntimeException("No allocation for node " + node.hostname())); - VespaUniqueInstanceId providerUniqueId = new VespaUniqueInstanceId( - allocation.membership().index(), - allocation.membership().cluster().id().value(), - allocation.owner().instance().value(), - allocation.owner().application().value(), - allocation.owner().tenant().value(), - zone.region().value(), - zone.environment().value(), - identityType); - - Set ips = new HashSet<>(node.ipConfig().primary()); - - PrivateKey privateKey = keyProvider.getPrivateKey(athenzProviderServiceConfig.secretVersion()); - AthenzService providerService = new AthenzService(athenzProviderServiceConfig.domain(), athenzProviderServiceConfig.serviceName()); - - String configServerHostname = HostName.getLocalhost(); - Instant createdAt = Instant.now(); - String signature = signer.generateSignature( - providerUniqueId, providerService, configServerHostname, - node.hostname(), createdAt, ips, identityType, privateKey); - - return new SignedIdentityDocument( - signature, - SignedIdentityDocument.DEFAULT_KEY_VERSION, - providerUniqueId, - providerService, - SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION, - configServerHostname, - node.hostname(), - createdAt, - ips, - identityType); - } 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/impl/CkmsKeyProvider.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/CkmsKeyProvider.java deleted file mode 100644 index bc044f12b15..00000000000 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/CkmsKeyProvider.java +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl; - -import com.google.inject.Inject; -import com.yahoo.config.provision.Zone; -import com.yahoo.container.jdisc.secretstore.SecretStore; -import com.yahoo.security.KeyUtils; -import com.yahoo.vespa.hosted.athenz.instanceproviderservice.KeyProvider; -import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig; - -import java.security.KeyPair; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.util.HashMap; -import java.util.Map; - -/** - * @author mortent - * @author bjorncs - */ -@SuppressWarnings("unused") // Injected component -public class CkmsKeyProvider implements KeyProvider { - - private final SecretStore secretStore; - private final String secretName; - private final Map secrets; - - @Inject - public CkmsKeyProvider(SecretStore secretStore, - Zone zone, - AthenzProviderServiceConfig config) { - this.secretStore = secretStore; - this.secretName = config.secretName(); - this.secrets = new HashMap<>(); - } - - @Override - public PrivateKey getPrivateKey(int version) { - return getKeyPair(version).getPrivate(); - } - - @Override - public PublicKey getPublicKey(int version) { - return getKeyPair(version).getPublic(); - } - - @Override - public KeyPair getKeyPair(int version) { - synchronized (secrets) { - KeyPair keyPair = secrets.get(version); - if (keyPair == null) { - keyPair = readKeyPair(version); - secrets.put(version, keyPair); - } - return keyPair; - } - } - - private KeyPair readKeyPair(int version) { - PrivateKey privateKey = KeyUtils.fromPemEncodedPrivateKey(secretStore.getSecret(secretName, version)); - PublicKey publicKey = KeyUtils.extractPublicKey(privateKey); - return new KeyPair(publicKey, privateKey); - } -} diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/Utils.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/Utils.java deleted file mode 100644 index f52493375f1..00000000000 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/Utils.java +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; - -/** - * @author bjorncs - */ -public class Utils { - - private static final ObjectMapper mapper = createObjectMapper(); - - public static ObjectMapper getMapper() { - return mapper; - } - - private static ObjectMapper createObjectMapper() { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new JavaTimeModule()); - return mapper; - } - -} diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmation.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmation.java deleted file mode 100644 index 0c6b5e67a80..00000000000 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmation.java +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation; - -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonUnwrapped; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.yahoo.restapi.RestApi; -import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity; -import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -/** - * InstanceConfirmation object as per Athenz InstanceConfirmation API. - * - * @author bjorncs - */ -public class InstanceConfirmation implements RestApi.JacksonRequestEntity { - - @JsonProperty("provider") public final String provider; - @JsonProperty("domain") public final String domain; - @JsonProperty("service") public final String service; - - @JsonProperty("attestationData") @JsonSerialize(using = SignedIdentitySerializer.class) - public final SignedIdentityDocumentEntity signedIdentityDocument; - @JsonUnwrapped public final Map attributes = new HashMap<>(); // optional attributes that Athenz may provide - - @JsonCreator - public InstanceConfirmation(@JsonProperty("provider") String provider, - @JsonProperty("domain") String domain, - @JsonProperty("service") String service, - @JsonProperty("attestationData") @JsonDeserialize(using = SignedIdentityDeserializer.class) - SignedIdentityDocumentEntity signedIdentityDocument) { - this.provider = provider; - this.domain = domain; - this.service = service; - this.signedIdentityDocument = signedIdentityDocument; - } - - @JsonAnySetter - public void set(String name, String value) { - attributes.put(name, value); - } - - @Override - public String toString() { - return "InstanceConfirmation{" + - "provider='" + provider + '\'' + - ", domain='" + domain + '\'' + - ", service='" + service + '\'' + - ", signedIdentityDocument='" + signedIdentityDocument + '\'' + - ", attributes=" + attributes + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - InstanceConfirmation that = (InstanceConfirmation) o; - return Objects.equals(provider, that.provider) && - Objects.equals(domain, that.domain) && - Objects.equals(service, that.service) && - Objects.equals(signedIdentityDocument, that.signedIdentityDocument) && - Objects.equals(attributes, that.attributes); - } - - @Override - public int hashCode() { - return Objects.hash(provider, domain, service, signedIdentityDocument, attributes); - } - - public static class SignedIdentityDeserializer extends JsonDeserializer { - @Override - public SignedIdentityDocumentEntity deserialize( - JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { - String value = jsonParser.getValueAsString(); - return Utils.getMapper().readValue(value, SignedIdentityDocumentEntity.class); - } - } - - public static class SignedIdentitySerializer extends JsonSerializer { - @Override - public void serialize( - SignedIdentityDocumentEntity document, JsonGenerator gen, SerializerProvider serializers) throws IOException { - gen.writeString(Utils.getMapper().writeValueAsString(document)); - } - } -} diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java deleted file mode 100644 index 9c5abb791cf..00000000000 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation; - -import com.google.common.net.InetAddresses; -import com.google.inject.Inject; -import com.yahoo.config.model.api.ApplicationInfo; -import com.yahoo.config.model.api.ServiceInfo; -import com.yahoo.config.model.api.SuperModelProvider; -import com.yahoo.config.provision.ApplicationId; - -import java.util.logging.Level; -import com.yahoo.vespa.athenz.api.AthenzService; -import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper; -import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument; -import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId; -import com.yahoo.vespa.athenz.identityprovider.client.IdentityDocumentSigner; -import com.yahoo.vespa.hosted.athenz.instanceproviderservice.KeyProvider; -import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig; -import com.yahoo.vespa.hosted.provision.Node; -import com.yahoo.vespa.hosted.provision.NodeRepository; - -import java.net.InetAddress; -import java.security.PublicKey; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * Verifies that the instance's identity document is valid - * - * @author bjorncs - * @author mortent - */ -public class InstanceValidator { - - private static final Logger log = Logger.getLogger(InstanceValidator.class.getName()); - static final String SERVICE_PROPERTIES_DOMAIN_KEY = "identity.domain"; - static final String SERVICE_PROPERTIES_SERVICE_KEY = "identity.service"; - static final String INSTANCE_ID_DELIMITER = ".instanceid.athenz."; - - public static final String SAN_IPS_ATTRNAME = "sanIP"; - public static final String SAN_DNS_ATTRNAME = "sanDNS"; - - private final AthenzService tenantDockerContainerIdentity; - private final IdentityDocumentSigner signer; - private final KeyProvider keyProvider; - private final SuperModelProvider superModelProvider; - private final NodeRepository nodeRepository; - - @Inject - public InstanceValidator(KeyProvider keyProvider, - SuperModelProvider superModelProvider, - NodeRepository nodeRepository, - AthenzProviderServiceConfig config) { - this(keyProvider, superModelProvider, nodeRepository, new IdentityDocumentSigner(), new AthenzService(config.tenantService())); - } - - public InstanceValidator(KeyProvider keyProvider, - SuperModelProvider superModelProvider, - NodeRepository nodeRepository, - IdentityDocumentSigner identityDocumentSigner, - AthenzService tenantIdentity){ - this.keyProvider = keyProvider; - this.superModelProvider = superModelProvider; - this.nodeRepository = nodeRepository; - this.signer = identityDocumentSigner; - this.tenantDockerContainerIdentity = tenantIdentity; - } - - public boolean isValidInstance(InstanceConfirmation instanceConfirmation) { - SignedIdentityDocument signedIdentityDocument = EntityBindingsMapper.toSignedIdentityDocument(instanceConfirmation.signedIdentityDocument); - VespaUniqueInstanceId providerUniqueId = signedIdentityDocument.providerUniqueId(); - ApplicationId applicationId = ApplicationId.from( - providerUniqueId.tenant(), providerUniqueId.application(), providerUniqueId.instance()); - - VespaUniqueInstanceId csrProviderUniqueId = getVespaUniqueInstanceId(instanceConfirmation); - if(! providerUniqueId.equals(csrProviderUniqueId)) { - log.log(Level.WARNING, String.format("Instance %s has invalid provider unique ID in CSR (%s)", providerUniqueId, csrProviderUniqueId)); - return false; - } - - if (! isSameIdentityAsInServicesXml(applicationId, instanceConfirmation.domain, instanceConfirmation.service)) { - return false; - } - - log.log(Level.FINE, () -> String.format("Validating instance %s.", providerUniqueId)); - - PublicKey publicKey = keyProvider.getPublicKey(signedIdentityDocument.signingKeyVersion()); - if (signer.hasValidSignature(signedIdentityDocument, publicKey)) { - log.log(Level.FINE, () -> String.format("Instance %s is valid.", providerUniqueId)); - return true; - } - log.log(Level.SEVERE, () -> String.format("Instance %s has invalid signature.", providerUniqueId)); - return false; - } - - // TODO Add actual validation. Cannot reuse isValidInstance as identity document is not part of the refresh request. - // We'll have to perform some validation on the instance id and other fields of the attribute map. - // Separate between tenant and node certificate as well. - public boolean isValidRefresh(InstanceConfirmation confirmation) { - log.log(Level.FINE, () -> String.format("Accepting refresh for instance with identity '%s', provider '%s', instanceId '%s'.", - new AthenzService(confirmation.domain, confirmation.service).getFullName(), - confirmation.provider, - confirmation.attributes.get(SAN_DNS_ATTRNAME))); - try { - return validateAttributes(confirmation, getVespaUniqueInstanceId(confirmation)); - } catch (Exception e) { - log.log(Level.WARNING, "Encountered exception while refreshing certificate for confirmation: " + confirmation, e); - return false; - } - } - - private VespaUniqueInstanceId getVespaUniqueInstanceId(InstanceConfirmation instanceConfirmation) { - // Find a list of SAN DNS - List sanDNS = Optional.ofNullable(instanceConfirmation.attributes.get(SAN_DNS_ATTRNAME)) - .map(s -> s.split(",")) - .map(Arrays::asList) - .map(List::stream) - .orElse(Stream.empty()) - .collect(Collectors.toList()); - - return sanDNS.stream() - .filter(dns -> dns.contains(INSTANCE_ID_DELIMITER)) - .findFirst() - .map(s -> s.replaceAll(INSTANCE_ID_DELIMITER + ".*", "")) - .map(VespaUniqueInstanceId::fromDottedString) - .orElse(null); - } - - private boolean validateAttributes(InstanceConfirmation confirmation, VespaUniqueInstanceId vespaUniqueInstanceId) { - if(vespaUniqueInstanceId == null) { - log.log(Level.WARNING, "Unable to find unique instance ID in refresh request: " + confirmation.toString()); - return false; - } - - // Find node matching vespa unique id - Node node = nodeRepository.nodes().list().stream() - .filter(n -> n.allocation().isPresent()) - .filter(n -> nodeMatchesVespaUniqueId(n, vespaUniqueInstanceId)) - .findFirst() // Should be only one - .orElse(null); - if(node == null) { - log.log(Level.WARNING, "Invalid InstanceConfirmation, No nodes matching uniqueId: " + vespaUniqueInstanceId); - return false; - } - - // Find list of ipaddresses - List ips = Optional.ofNullable(confirmation.attributes.get(SAN_IPS_ATTRNAME)) - .map(s -> s.split(",")) - .map(Arrays::asList) - .map(List::stream) - .orElse(Stream.empty()) - .map(InetAddresses::forString) - .collect(Collectors.toList()); - - List nodeIpAddresses = node.ipConfig().primary().stream() - .map(InetAddresses::forString) - .collect(Collectors.toList()); - - // Validate that ipaddresses in request are valid for node - - if(! nodeIpAddresses.containsAll(ips)) { - log.log(Level.WARNING, "Invalid InstanceConfirmation, wrong ip in : " + vespaUniqueInstanceId); - return false; - } - return true; - } - - private boolean nodeMatchesVespaUniqueId(Node node, VespaUniqueInstanceId vespaUniqueInstanceId) { - return node.allocation().map(allocation -> - allocation.membership().index() == vespaUniqueInstanceId.clusterIndex() && - allocation.membership().cluster().id().value().equals(vespaUniqueInstanceId.clusterId()) && - allocation.owner().instance().value().equals(vespaUniqueInstanceId.instance()) && - allocation.owner().application().value().equals(vespaUniqueInstanceId.application()) && - allocation.owner().tenant().value().equals(vespaUniqueInstanceId.tenant())) - .orElse(false); - } - - // If/when we don't care about logging exactly whats wrong, this can be simplified - // TODO Use identity type to determine if this check should be performed - private boolean isSameIdentityAsInServicesXml(ApplicationId applicationId, String domain, String service) { - - Optional applicationInfo = superModelProvider.getSuperModel().getApplicationInfo(applicationId); - - if (!applicationInfo.isPresent()) { - log.info(String.format("Could not find application info for %s, existing applications: %s", - applicationId.serializedForm(), - superModelProvider.getSuperModel().getAllApplicationInfos())); - return false; - } - - if (tenantDockerContainerIdentity.equals(new AthenzService(domain, service))) { - return true; - } - - Optional matchingServiceInfo = applicationInfo.get() - .getModel() - .getHosts() - .stream() - .flatMap(hostInfo -> hostInfo.getServices().stream()) - .filter(serviceInfo -> serviceInfo.getProperty(SERVICE_PROPERTIES_DOMAIN_KEY).isPresent()) - .filter(serviceInfo -> serviceInfo.getProperty(SERVICE_PROPERTIES_SERVICE_KEY).isPresent()) - .findFirst(); - - if (!matchingServiceInfo.isPresent()) { - log.info(String.format("Application %s has not specified domain/service", applicationId.serializedForm())); - return false; - } - - String domainInConfig = matchingServiceInfo.get().getProperty(SERVICE_PROPERTIES_DOMAIN_KEY).get(); - String serviceInConfig = matchingServiceInfo.get().getProperty(SERVICE_PROPERTIES_SERVICE_KEY).get(); - if (!domainInConfig.equals(domain) || !serviceInConfig.equals(service)) { - log.warning(String.format("domain '%s' or service '%s' does not match the one in config for application %s", - domain, service, applicationId.serializedForm())); - return false; - } - - return true; - } -} diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java index 0325971038f..c9850b70afd 100644 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java @@ -19,8 +19,8 @@ import com.yahoo.vespa.athenz.api.AthenzService; import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper; import com.yahoo.slime.SlimeUtils; import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig; -import com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation.InstanceConfirmation; -import com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation.InstanceValidator; +import com.yahoo.vespa.hosted.athenz.instanceproviderservice.InstanceConfirmation; +import com.yahoo.vespa.hosted.athenz.instanceproviderservice.InstanceValidator; import com.yahoo.vespa.hosted.ca.Certificates; import com.yahoo.vespa.hosted.ca.instance.InstanceIdentity; import com.yahoo.vespa.hosted.ca.instance.InstanceRefresh; @@ -37,7 +37,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.Function; -import java.util.logging.Level; import java.util.stream.Stream; /** -- cgit v1.2.3