aboutsummaryrefslogtreecommitdiffstats
path: root/athenz-identity-provider-service/src/main
diff options
context:
space:
mode:
authorMorten Tokle <mortent@yahooinc.com>2023-03-02 14:39:35 +0100
committerAndreas Eriksen <andreer@yahooinc.com>2023-03-02 15:07:40 +0100
commit910579c0d018226f94786208bb0a9d3bbe4b4a27 (patch)
tree1159c1202c140a8d6080881a952d4814805b5c52 /athenz-identity-provider-service/src/main
parentbbec3fdb956f1af1a9beaabf15c1426726cebb92 (diff)
Remove module athenz-identity-provider-service
Diffstat (limited to 'athenz-identity-provider-service/src/main')
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/CertificateExpiryMetricUpdater.java59
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/CkmsKeyProvider.java64
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ConfigserverSslContextFactoryProvider.java182
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGenerator.java106
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityProviderRequestHandler.java99
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceConfirmation.java99
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidator.java293
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/KeyProvider.java19
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/Utils.java24
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/package-info.java8
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/Certificates.java95
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceIdentity.java64
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceRefresh.java40
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceRegistration.java83
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java198
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializer.java126
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/package-info.java8
17 files changed, 0 insertions, 1567 deletions
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/CertificateExpiryMetricUpdater.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/CertificateExpiryMetricUpdater.java
deleted file mode 100644
index f3568caac04..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/CertificateExpiryMetricUpdater.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright Yahoo. 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.yahoo.component.annotation.Inject;
-import com.yahoo.component.AbstractComponent;
-import com.yahoo.jdisc.Metric;
-
-import java.time.Duration;
-import java.time.Instant;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * @author freva
- */
-public class CertificateExpiryMetricUpdater extends AbstractComponent {
-
- private static final Duration METRIC_REFRESH_PERIOD = Duration.ofMinutes(5);
- private static final String ATHENZ_CONFIGSERVER_CERT_METRIC_NAME = "athenz-configserver-cert.expiry.seconds";
-
- private final Logger logger = Logger.getLogger(CertificateExpiryMetricUpdater.class.getName());
- private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
- private final Metric metric;
- private final ConfigserverSslContextFactoryProvider provider;
-
- @Inject
- public CertificateExpiryMetricUpdater(Metric metric,
- ConfigserverSslContextFactoryProvider provider) {
- this.metric = metric;
- this.provider = provider;
-
- scheduler.scheduleAtFixedRate(this::updateMetrics,
- 30/*initial delay*/,
- METRIC_REFRESH_PERIOD.getSeconds(),
- TimeUnit.SECONDS);
- }
-
- @Override
- public void deconstruct() {
- try {
- scheduler.shutdownNow();
- scheduler.awaitTermination(30, TimeUnit.SECONDS);
- } catch (InterruptedException e) {
- throw new RuntimeException("Failed to shutdown certificate expiry metrics updater on time", e);
- }
- }
-
- private void updateMetrics() {
- try {
- Duration keyStoreExpiry = Duration.between(Instant.now(), provider.getCertificateNotAfter());
- metric.set(ATHENZ_CONFIGSERVER_CERT_METRIC_NAME, keyStoreExpiry.getSeconds(), null);
- } catch (Exception e) {
- logger.log(Level.WARNING, "Failed to update key store expiry metric: " + e.getMessage(), e);
- }
- }
-}
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
deleted file mode 100644
index c659c454420..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/CkmsKeyProvider.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright Yahoo. 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.yahoo.component.annotation.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<Integer, KeyPair> 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/ConfigserverSslContextFactoryProvider.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ConfigserverSslContextFactoryProvider.java
deleted file mode 100644
index 61a4a0fe41f..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ConfigserverSslContextFactoryProvider.java
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright Yahoo. 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.yahoo.component.annotation.Inject;
-import com.yahoo.jdisc.http.ssl.impl.TlsContextBasedProvider;
-import com.yahoo.security.KeyStoreBuilder;
-import com.yahoo.security.KeyStoreType;
-import com.yahoo.security.KeyUtils;
-import com.yahoo.security.SslContextBuilder;
-import com.yahoo.security.tls.DefaultTlsContext;
-import com.yahoo.security.MutableX509KeyManager;
-import com.yahoo.security.tls.PeerAuthentication;
-import com.yahoo.security.tls.TlsContext;
-import com.yahoo.vespa.athenz.api.AthenzService;
-import com.yahoo.vespa.athenz.client.zts.DefaultZtsClient;
-import com.yahoo.vespa.athenz.client.zts.Identity;
-import com.yahoo.vespa.athenz.client.zts.ZtsClient;
-import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider;
-import com.yahoo.vespa.athenz.utils.SiaUtils;
-import com.yahoo.vespa.defaults.Defaults;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
-
-import javax.net.ssl.SSLContext;
-import java.net.URI;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.security.KeyPair;
-import java.security.KeyStore;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.cert.X509Certificate;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.Optional;
-import java.util.UUID;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * Configures the JDisc https connector with the configserver's Athenz provider certificate and private key.
- *
- * @author bjorncs
- */
-public class ConfigserverSslContextFactoryProvider extends TlsContextBasedProvider {
-
- private static final String CERTIFICATE_ALIAS = "athenz";
- private static final Duration EXPIRATION_MARGIN = Duration.ofHours(6);
- private static final Path VESPA_SIA_DIRECTORY = Paths.get(Defaults.getDefaults().underVespaHome("var/vespa/sia"));
-
- private static final Logger log = Logger.getLogger(ConfigserverSslContextFactoryProvider.class.getName());
-
- private final TlsContext tlsContext;
- private final MutableX509KeyManager keyManager = new MutableX509KeyManager();
- private final ScheduledExecutorService scheduler =
- Executors.newSingleThreadScheduledExecutor(runnable -> new Thread(runnable, "configserver-ssl-context-factory-provider"));
- private final ZtsClient ztsClient;
- private final KeyProvider keyProvider;
- private final AthenzProviderServiceConfig athenzProviderServiceConfig;
- private final AthenzService configserverIdentity;
-
- @Inject
- public ConfigserverSslContextFactoryProvider(ServiceIdentityProvider bootstrapIdentity,
- KeyProvider keyProvider,
- AthenzProviderServiceConfig config) {
- this.athenzProviderServiceConfig = config;
- this.ztsClient = new DefaultZtsClient.Builder(URI.create(athenzProviderServiceConfig.ztsUrl()))
- .withIdentityProvider(bootstrapIdentity).build();
- this.keyProvider = keyProvider;
- this.configserverIdentity = new AthenzService(athenzProviderServiceConfig.domain(), athenzProviderServiceConfig.serviceName());
-
- Duration updatePeriod = Duration.ofDays(config.updatePeriodDays());
- Path trustStoreFile = Paths.get(config.athenzCaTrustStore());
- this.tlsContext = createTlsContext(keyProvider, keyManager, trustStoreFile, updatePeriod, configserverIdentity, ztsClient, athenzProviderServiceConfig);
- scheduler.scheduleAtFixedRate(new KeystoreUpdater(keyManager),
- updatePeriod.toDays()/*initial delay*/,
- updatePeriod.toDays(),
- TimeUnit.DAYS);
- }
-
- @Override
- protected TlsContext getTlsContext(String containerId, int port) {
- return tlsContext;
- }
-
- Instant getCertificateNotAfter() {
- return keyManager.currentManager().getCertificateChain(CERTIFICATE_ALIAS)[0].getNotAfter().toInstant();
- }
-
- @Override
- public void deconstruct() {
- try {
- scheduler.shutdownNow();
- scheduler.awaitTermination(30, TimeUnit.SECONDS);
- ztsClient.close();
- super.deconstruct();
- } catch (InterruptedException e) {
- throw new RuntimeException("Failed to shutdown Athenz certificate updater on time", e);
- }
- }
-
- private static TlsContext createTlsContext(KeyProvider keyProvider,
- MutableX509KeyManager keyManager,
- Path trustStoreFile,
- Duration updatePeriod,
- AthenzService configserverIdentity,
- ZtsClient ztsClient,
- AthenzProviderServiceConfig zoneConfig) {
- KeyStore keyStore =
- tryReadKeystoreFile(configserverIdentity, updatePeriod)
- .orElseGet(() -> updateKeystore(configserverIdentity, generateKeystorePassword(), keyProvider, ztsClient, zoneConfig));
- keyManager.updateKeystore(keyStore, new char[0]);
- SSLContext sslContext = new SslContextBuilder()
- .withTrustStore(trustStoreFile, KeyStoreType.JKS)
- .withKeyManager(keyManager)
- .build();
- return new DefaultTlsContext(sslContext, PeerAuthentication.WANT);
- }
-
- private static Optional<KeyStore> tryReadKeystoreFile(AthenzService configserverIdentity, Duration updatePeriod) {
- Optional<X509Certificate> certificate = SiaUtils.readCertificateFile(VESPA_SIA_DIRECTORY, configserverIdentity);
- if (!certificate.isPresent()) return Optional.empty();
- Optional<PrivateKey> privateKey = SiaUtils.readPrivateKeyFile(VESPA_SIA_DIRECTORY, configserverIdentity);
- if (!privateKey.isPresent()) return Optional.empty();
- Instant minimumExpiration = Instant.now().plus(updatePeriod).plus(EXPIRATION_MARGIN);
- boolean isExpired = certificate.get().getNotAfter().toInstant().isBefore(minimumExpiration);
- if (isExpired) return Optional.empty();
- KeyStore keyStore = KeyStoreBuilder.withType(KeyStoreType.JKS)
- .withKeyEntry(CERTIFICATE_ALIAS, privateKey.get(), certificate.get())
- .build();
- return Optional.of(keyStore);
- }
-
- private static KeyStore updateKeystore(AthenzService configserverIdentity,
- char[] keystorePwd,
- KeyProvider keyProvider,
- ZtsClient ztsClient,
- AthenzProviderServiceConfig zoneConfig) {
- PrivateKey privateKey = keyProvider.getPrivateKey(zoneConfig.secretVersion());
- PublicKey publicKey = KeyUtils.extractPublicKey(privateKey);
- Identity serviceIdentity = ztsClient.getServiceIdentity(configserverIdentity,
- Integer.toString(zoneConfig.secretVersion()),
- new KeyPair(publicKey, privateKey),
- zoneConfig.certDnsSuffix());
- X509Certificate certificate = serviceIdentity.certificate();
- SiaUtils.writeCertificateFile(VESPA_SIA_DIRECTORY, configserverIdentity, certificate);
- SiaUtils.writePrivateKeyFile(VESPA_SIA_DIRECTORY, configserverIdentity, privateKey);
- Instant expirationTime = certificate.getNotAfter().toInstant();
- Duration expiry = Duration.between(certificate.getNotBefore().toInstant(), expirationTime);
- log.log(Level.INFO, String.format("Got Athenz x509 certificate with expiry %s (expires %s)", expiry, expirationTime));
- return KeyStoreBuilder.withType(KeyStoreType.JKS)
- .withKeyEntry(CERTIFICATE_ALIAS, privateKey, keystorePwd, certificate)
- .build();
- }
-
- private static char[] generateKeystorePassword() {
- return UUID.randomUUID().toString().toCharArray();
- }
-
- private class KeystoreUpdater implements Runnable {
- final MutableX509KeyManager keyManager;
-
- KeystoreUpdater(MutableX509KeyManager keyManager) {
- this.keyManager = keyManager;
- }
-
- @Override
- public void run() {
- try {
- log.log(Level.INFO, "Updating configserver provider certificate from ZTS");
- char[] keystorePwd = generateKeystorePassword();
- KeyStore keyStore = updateKeystore(configserverIdentity, keystorePwd, keyProvider, ztsClient, athenzProviderServiceConfig);
- keyManager.updateKeystore(keyStore, keystorePwd);
- log.log(Level.INFO, "Certificate successfully updated");
- } catch (Throwable t) {
- log.log(Level.SEVERE, "Failed to update certificate from ZTS: " + t.getMessage(), t);
- }
- }
- }
-}
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
deleted file mode 100644
index 5138bee1ff6..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGenerator.java
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright Yahoo. 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.yahoo.component.annotation.Inject;
-import com.yahoo.config.provision.Zone;
-import com.yahoo.container.jdisc.secretstore.SecretStore;
-import com.yahoo.net.HostName;
-import com.yahoo.security.KeyUtils;
-
-import com.yahoo.vespa.athenz.api.AthenzService;
-import com.yahoo.vespa.athenz.identityprovider.api.ClusterType;
-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.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 SecretStore secretStore;
- private final AthenzProviderServiceConfig athenzProviderServiceConfig;
-
- @Inject
- public IdentityDocumentGenerator(AthenzProviderServiceConfig config,
- NodeRepository nodeRepository,
- Zone zone,
- KeyProvider keyProvider,
- SecretStore secretStore) {
- this.athenzProviderServiceConfig = config;
- this.nodeRepository = nodeRepository;
- this.zone = zone;
- this.keyProvider = keyProvider;
- this.secretStore = secretStore;
- }
-
- 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<String> ips = new HashSet<>(node.ipConfig().primary());
-
- PrivateKey privateKey = privateKey(node);
- AthenzService providerService = new AthenzService(athenzProviderServiceConfig.domain(), athenzProviderServiceConfig.serviceName());
-
- String configServerHostname = HostName.getLocalhost();
- Instant createdAt = Instant.now();
- var clusterType = ClusterType.from(allocation.membership().cluster().type().name());
- String signature = signer.generateSignature(
- providerUniqueId, providerService, configServerHostname,
- node.hostname(), createdAt, ips, identityType, privateKey);
- return new SignedIdentityDocument(
- signature, athenzProviderServiceConfig.secretVersion(), providerUniqueId, providerService,
- SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION, configServerHostname, node.hostname(),
- createdAt, ips, identityType, clusterType, ztsUrl(node));
- } catch (Exception e) {
- throw new RuntimeException("Exception generating identity document: " + e.getMessage(), e);
- }
- }
-
- private PrivateKey privateKey(Node node) {
- // return sisSecret for public non-enclave hosts. secret otherwise
- if (zone.system().isPublic() && !node.cloudAccount().isEnclave(zone)) {
- String keyPem = secretStore.getSecret(athenzProviderServiceConfig.sisSecretName(), athenzProviderServiceConfig.sisSecretVersion());
- return KeyUtils.fromPemEncodedPrivateKey(keyPem);
- } else {
- return keyProvider.getPrivateKey(athenzProviderServiceConfig.secretVersion());
- }
- }
- private String ztsUrl(Node node) {
- // return sisUrl for public non-enclave hosts, ztsUrl otherwise
- if (zone.system().isPublic() && !node.cloudAccount().isEnclave(zone)) {
- return athenzProviderServiceConfig.sisUrl();
- } else {
- return athenzProviderServiceConfig.ztsUrl();
- }
- }
-}
-
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
deleted file mode 100644
index c1dd70d7656..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityProviderRequestHandler.java
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright Yahoo. 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.databind.SerializationFeature;
-import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
-import com.yahoo.component.annotation.Inject;
-import com.yahoo.container.jdisc.ThreadedHttpRequestHandler;
-import com.yahoo.restapi.RestApi;
-import com.yahoo.restapi.RestApiException;
-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 java.util.logging.Level;
-
-/**
- * Handler implementing the Athenz Identity Provider API (Copper Argos).
- *
- * @author bjorncs
- */
-public class IdentityProviderRequestHandler extends RestApiRequestHandler<IdentityProviderRequestHandler> {
-
- private final IdentityDocumentGenerator documentGenerator;
- private final InstanceValidator instanceValidator;
-
- @Inject
- public IdentityProviderRequestHandler(ThreadedHttpRequestHandler.Context context,
- IdentityDocumentGenerator documentGenerator,
- InstanceValidator instanceValidator) {
- super(context, IdentityProviderRequestHandler::createRestApi);
- this.documentGenerator = documentGenerator;
- this.instanceValidator = instanceValidator;
- }
-
- private static RestApi createRestApi(IdentityProviderRequestHandler self) {
- return RestApi.builder()
- .addRoute(RestApi.route("/athenz/v1/provider/identity-document/node/{host}")
- .get(self::getNodeIdentityDocument))
- .addRoute(RestApi.route("/athenz/v1/provider/identity-document/tenant/{host}")
- .get(self::getTenantIdentityDocument))
- .addRoute(RestApi.route("/athenz/v1/provider/instance")
- .post(InstanceConfirmation.class, self::confirmInstance))
- .addRoute(RestApi.route("/athenz/v1/provider/refresh")
- .post(InstanceConfirmation.class, self::confirmInstanceRefresh))
- .registerJacksonRequestEntity(InstanceConfirmation.class)
- .registerJacksonResponseEntity(InstanceConfirmation.class)
- .registerJacksonResponseEntity(SignedIdentityDocumentEntity.class)
- // Overriding object mapper to change serialization of timestamps
- .setObjectMapper(new ObjectMapper()
- .registerModule(new JavaTimeModule())
- .registerModule(new Jdk8Module())
- .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true))
- .build();
- }
-
- private SignedIdentityDocumentEntity getNodeIdentityDocument(RestApi.RequestContext context) {
- String host = context.pathParameters().getString("host").orElse(null);
- return getIdentityDocument(host, IdentityType.NODE);
- }
-
- private SignedIdentityDocumentEntity getTenantIdentityDocument(RestApi.RequestContext context) {
- String host = context.pathParameters().getString("host").orElse(null);
- return getIdentityDocument(host, IdentityType.TENANT);
- }
-
- private InstanceConfirmation confirmInstance(RestApi.RequestContext context, InstanceConfirmation instanceConfirmation) {
- log.log(Level.FINE, () -> instanceConfirmation.toString());
- if (!instanceValidator.isValidInstance(instanceConfirmation)) {
- log.log(Level.SEVERE, "Invalid instance: " + instanceConfirmation);
- throw new RestApiException.Forbidden("Instance is invalid");
- }
- return instanceConfirmation;
- }
-
- private InstanceConfirmation confirmInstanceRefresh(RestApi.RequestContext context, InstanceConfirmation instanceConfirmation) {
- log.log(Level.FINE, () -> instanceConfirmation.toString());
- if (!instanceValidator.isValidRefresh(instanceConfirmation)) {
- log.log(Level.SEVERE, "Invalid instance refresh: " + instanceConfirmation);
- throw new RestApiException.Forbidden("Instance is invalid");
- }
- return instanceConfirmation;
- }
-
- private SignedIdentityDocumentEntity getIdentityDocument(String hostname, IdentityType identityType) {
- if (hostname == null) {
- throw new RestApiException.BadRequest("The 'hostname' query parameter is missing");
- }
- try {
- return EntityBindingsMapper.toSignedIdentityDocumentEntity(documentGenerator.generateSignedIdentityDocument(hostname, identityType));
- } catch (Exception e) {
- String message = String.format("Unable to generate identity document for '%s': %s", hostname, e.getMessage());
- log.log(Level.SEVERE, message, e);
- throw new RestApiException.InternalServerError(message, e);
- }
- }
-}
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
deleted file mode 100644
index 6c09a35ee3d..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceConfirmation.java
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright Yahoo. 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.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 {
-
- @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<String, String> 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<SignedIdentityDocumentEntity> {
- @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<SignedIdentityDocumentEntity> {
- @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
deleted file mode 100644
index a349bddc76b..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidator.java
+++ /dev/null
@@ -1,293 +0,0 @@
-// Copyright Yahoo. 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.yahoo.component.annotation.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.config.provision.Zone;
-import com.yahoo.container.jdisc.secretstore.SecretStore;
-import com.yahoo.security.KeyUtils;
-import com.yahoo.vespa.athenz.api.AthenzService;
-import com.yahoo.vespa.athenz.identityprovider.api.ClusterType;
-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.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.function.Supplier;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.stream.Collectors;
-
-/**
- * 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";
- public static final String SAN_URI_ATTRNAME = "sanURI";
-
- private final AthenzService tenantDockerContainerIdentity;
- private final IdentityDocumentSigner signer;
- private final KeyProvider keyProvider;
- private final SuperModelProvider superModelProvider;
- private final NodeRepository nodeRepository;
- private final SecretStore secretStore;
- private final String sisSecretName;
-
- @Inject
- public InstanceValidator(KeyProvider keyProvider,
- SuperModelProvider superModelProvider,
- NodeRepository nodeRepository,
- AthenzProviderServiceConfig config,
- SecretStore secretStore) {
- this(keyProvider, superModelProvider, nodeRepository, new IdentityDocumentSigner(), new AthenzService(config.tenantService()), secretStore, config.sisSecretName());
- }
-
- public InstanceValidator(KeyProvider keyProvider,
- SuperModelProvider superModelProvider,
- NodeRepository nodeRepository,
- IdentityDocumentSigner identityDocumentSigner,
- AthenzService tenantIdentity,
- SecretStore secretStore,
- String sisSecretName) {
- this.keyProvider = keyProvider;
- this.superModelProvider = superModelProvider;
- this.nodeRepository = nodeRepository;
- this.signer = identityDocumentSigner;
- this.tenantDockerContainerIdentity = tenantIdentity;
- this.secretStore = secretStore;
- this.sisSecretName = sisSecretName;
- }
-
- public boolean isValidInstance(InstanceConfirmation instanceConfirmation) {
- try {
- validateInstance(instanceConfirmation);
- return true;
- } catch (ValidationException e) {
- log.log(e.logLevel(), e.messageSupplier());
- return false;
- }
- }
-
- public void validateInstance(InstanceConfirmation req) throws ValidationException {
- SignedIdentityDocument signedIdentityDocument = EntityBindingsMapper.toSignedIdentityDocument(req.signedIdentityDocument);
- VespaUniqueInstanceId providerUniqueId = signedIdentityDocument.providerUniqueId();
- ApplicationId applicationId = ApplicationId.from(
- providerUniqueId.tenant(), providerUniqueId.application(), providerUniqueId.instance());
-
- VespaUniqueInstanceId csrProviderUniqueId = getVespaUniqueInstanceId(req);
- if(! providerUniqueId.equals(csrProviderUniqueId)) {
- var msg = String.format("Instance %s has invalid provider unique ID in CSR (%s)", providerUniqueId, csrProviderUniqueId);
- throw new ValidationException(Level.WARNING, () -> msg);
- }
-
- if (! isSameIdentityAsInServicesXml(applicationId, req.domain, req.service)) {
- Supplier<String> msg = () -> "Invalid identity '%s.%s' in services.xml".formatted(req.domain, req.service);
- throw new ValidationException(Level.FINE, msg);
- }
-
- log.log(Level.FINE, () -> String.format("Validating instance %s.", providerUniqueId));
-
- // Find node matching vespa unique id
- Node node = getNode(providerUniqueId);
-
- PublicKey publicKey = publicKey(node, signedIdentityDocument.signingKeyVersion());
- if (! signer.hasValidSignature(signedIdentityDocument, publicKey)) {
- var msg = String.format("Instance %s has invalid signature.", providerUniqueId);
- throw new ValidationException(Level.SEVERE, () -> msg);
- }
-
- validateAttributes(node, req, providerUniqueId);
- log.log(Level.FINE, () -> String.format("Instance %s is valid.", providerUniqueId));
- }
-
- private PublicKey publicKey(Node node, int version) {
- // return sisSecret for public non-enclave hosts.
- Zone zone = nodeRepository.zone();
- if (zone.system().isPublic() && !node.cloudAccount().isEnclave(zone)) {
- String keyPem = secretStore.getSecret(sisSecretName, version);
- return KeyUtils.extractPublicKey(KeyUtils.fromPemEncodedPrivateKey(keyPem));
- } else {
- return keyProvider.getPublicKey(version);
- }
- }
-
- // 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 {
- VespaUniqueInstanceId vespaUniqueInstanceId = getVespaUniqueInstanceId(confirmation);
- validateAttributes(getNode(vespaUniqueInstanceId), confirmation, vespaUniqueInstanceId);
- return true;
- } catch (ValidationException e) {
- log.log(e.logLevel(), e.messageSupplier());
- return false;
- } 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<String> sanDNS = Optional.ofNullable(instanceConfirmation.attributes.get(SAN_DNS_ATTRNAME))
- .map(s -> s.split(","))
- .map(Arrays::asList).stream().flatMap(Collection::stream).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 void validateAttributes(Node node, InstanceConfirmation confirmation, VespaUniqueInstanceId vespaUniqueInstanceId)
- throws ValidationException {
- if(vespaUniqueInstanceId == null) {
- var msg = "Unable to find unique instance ID in refresh request: " + confirmation.toString();
- throw new ValidationException(Level.WARNING, () -> msg);
- }
-
- // Find list of ipaddresses
- List<InetAddress> ips = Optional.ofNullable(confirmation.attributes.get(SAN_IPS_ATTRNAME))
- .map(s -> s.split(","))
- .map(Arrays::asList).stream().flatMap(Collection::stream)
- .map(InetAddresses::forString)
- .toList();
-
- List<InetAddress> nodeIpAddresses = node.ipConfig().primary().stream()
- .map(InetAddresses::forString)
- .toList();
-
- // Validate that ipaddresses in request are valid for node
-
- if(! nodeIpAddresses.containsAll(ips)) {
- var msg = "Invalid InstanceConfirmation, wrong ip in : " + vespaUniqueInstanceId;
- throw new ValidationException(Level.WARNING, () -> msg);
- }
-
- 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) {
- throw new ValidationException(Level.WARNING, () -> "Invalid SAN URIs: " + urisCommaSeparated, e);
- }
- var clusterType = node.allocation().map(a -> a.membership().cluster().type()).orElse(null);
- Set<URI> allowedUris = clusterType != null
- ? Set.of(ClusterType.from(clusterType.name()).asCertificateSanUri()) : Set.of();
- if (!allowedUris.containsAll(requestedUris)) {
- Supplier<String> msg = () -> "Illegal SAN URIs: expected '%s' found '%s'".formatted(allowedUris, requestedUris);
- throw new ValidationException(Level.WARNING, msg);
- }
- }
-
- private Node getNode(VespaUniqueInstanceId vespaUniqueInstanceId) throws ValidationException {
- // 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) {
- var msg = "Invalid InstanceConfirmation, No nodes matching uniqueId: " + vespaUniqueInstanceId;
- throw new ValidationException(Level.WARNING, () -> msg);
- }
- return node;
- }
-
- 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> applicationInfo = superModelProvider.getSuperModel().getApplicationInfo(applicationId);
-
- if (applicationInfo.isEmpty()) {
- 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<ServiceInfo> 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.isEmpty()) {
- 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;
- }
-
- public static class ValidationException extends Exception {
- private final Level logLevel;
- private final Supplier<String> msg;
-
- public ValidationException(Level logLevel, Supplier<String> msg) { this(logLevel, msg, null); }
- public ValidationException(Level logLevel, Supplier<String> msg, Throwable cause) { super(cause); this.logLevel = logLevel; this.msg = msg; }
-
- @Override public String getMessage() { return msg.get(); }
- public Level logLevel() { return logLevel; }
- public Supplier<String> messageSupplier() { return msg; }
- }
-}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/KeyProvider.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/KeyProvider.java
deleted file mode 100644
index 324f927fd73..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/KeyProvider.java
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice;
-
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
-/**
- * @author bjorncs
- */
-public interface KeyProvider {
- PrivateKey getPrivateKey(int version);
-
- PublicKey getPublicKey(int version);
-
- default KeyPair getKeyPair(int version) {
- return new KeyPair(getPublicKey(version), getPrivateKey(version));
- }
-}
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
deleted file mode 100644
index 5c4942f37cb..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/Utils.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright Yahoo. 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/package-info.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/package-info.java
deleted file mode 100644
index 0cb5c9d4f82..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/package-info.java
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-/**
- * @author bjorncs
- */
-@ExportPackage
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice;
-
-import com.yahoo.osgi.annotation.ExportPackage;
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/Certificates.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/Certificates.java
deleted file mode 100644
index df904bf8010..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/Certificates.java
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.ca;
-
-import com.yahoo.security.Pkcs10Csr;
-import com.yahoo.security.SubjectAlternativeName;
-import com.yahoo.security.X509CertificateBuilder;
-import com.yahoo.security.X509CertificateUtils;
-import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId;
-
-import java.security.PrivateKey;
-import java.security.cert.X509Certificate;
-import java.time.Clock;
-import java.time.Duration;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-import static com.yahoo.security.SignatureAlgorithm.SHA256_WITH_ECDSA;
-import static com.yahoo.security.SubjectAlternativeName.Type.DNS;
-
-/**
- * Helper class for creating {@link X509Certificate}s.
- *
- * @author mpolden
- */
-public class Certificates {
-
- private static final Duration CERTIFICATE_TTL = Duration.ofDays(30);
- private static final String INSTANCE_ID_DELIMITER = ".instanceid.athenz.";
-
- private final Clock clock;
-
- public Certificates(Clock clock) {
- this.clock = Objects.requireNonNull(clock, "clock must be non-null");
- }
-
- /** Create a new certificate from csr signed by the given CA private key */
- public X509Certificate create(Pkcs10Csr csr, X509Certificate caCertificate, PrivateKey caPrivateKey) {
- var x500principal = caCertificate.getSubjectX500Principal();
- var now = clock.instant();
- var notBefore = now.minus(Duration.ofHours(1));
- var notAfter = now.plus(CERTIFICATE_TTL);
- var builder = X509CertificateBuilder.fromCsr(csr,
- x500principal,
- notBefore,
- notAfter,
- caPrivateKey,
- SHA256_WITH_ECDSA,
- X509CertificateBuilder.generateRandomSerialNumber());
- for (var san : csr.getSubjectAlternativeNames()) {
- builder = builder.addSubjectAlternativeName(san.decode());
- }
- return builder.build();
- }
-
- /** Returns instance ID parsed from the Subject Alternative Names in given csr */
- public static String instanceIdFrom(Pkcs10Csr csr) {
- return getInstanceIdFromSAN(csr.getSubjectAlternativeNames())
- .orElseThrow(() -> new IllegalArgumentException("No instance ID found in CSR"));
- }
-
- public static Optional<String> instanceIdFrom(X509Certificate certificate) {
- return getInstanceIdFromSAN(X509CertificateUtils.getSubjectAlternativeNames(certificate));
- }
-
- private static Optional<String> getInstanceIdFromSAN(List<SubjectAlternativeName> subjectAlternativeNames) {
- return subjectAlternativeNames.stream()
- .filter(san -> san.getType() == DNS)
- .map(SubjectAlternativeName::getValue)
- .map(Certificates::parseInstanceId)
- .flatMap(Optional::stream)
- .map(VespaUniqueInstanceId::asDottedString)
- .findFirst();
- }
-
- private static Optional<VespaUniqueInstanceId> parseInstanceId(String dnsName) {
- var delimiterStart = dnsName.indexOf(INSTANCE_ID_DELIMITER);
- if (delimiterStart == -1) return Optional.empty();
- dnsName = dnsName.substring(0, delimiterStart);
- try {
- return Optional.of(VespaUniqueInstanceId.fromDottedString(dnsName));
- } catch (IllegalArgumentException e) {
- return Optional.empty();
- }
- }
-
- public static String getSubjectAlternativeNames(Pkcs10Csr csr, SubjectAlternativeName.Type sanType) {
- return csr.getSubjectAlternativeNames().stream()
- .map(SubjectAlternativeName::decode)
- .filter(san -> san.getType() == sanType)
- .map(SubjectAlternativeName::getValue)
- .collect(Collectors.joining(","));
- }
-}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceIdentity.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceIdentity.java
deleted file mode 100644
index f33ec4fbd6d..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceIdentity.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.ca.instance;
-
-import java.security.cert.X509Certificate;
-import java.util.Objects;
-import java.util.Optional;
-
-/**
- * A signed instance identity object that includes a client certificate. This is the result of a successful
- * {@link InstanceRegistration} and is the same type as InstanceIdentity in the ZTS API.
- *
- * @author mpolden
- */
-public class InstanceIdentity {
-
- private final String provider;
- private final String service;
- private final String instanceId;
- private final Optional<X509Certificate> x509Certificate;
-
- public InstanceIdentity(String provider, String service, String instanceId, Optional<X509Certificate> x509Certificate) {
- this.provider = Objects.requireNonNull(provider, "provider must be non-null");
- this.service = Objects.requireNonNull(service, "service must be non-null");
- this.instanceId = Objects.requireNonNull(instanceId, "instanceId must be non-null");
- this.x509Certificate = Objects.requireNonNull(x509Certificate, "x509Certificate must be non-null");
- }
-
- /** Same as {@link InstanceRegistration#domain()} */
- public String provider() {
- return provider;
- }
-
- /** Same as {@link InstanceRegistration#service()} ()} */
- public String service() {
- return service;
- }
-
- /** A unique identifier of the instance to which the certificate is issued */
- public String instanceId() {
- return instanceId;
- }
-
- /** The issued certificate */
- public Optional<X509Certificate> x509Certificate() {
- return x509Certificate;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- InstanceIdentity that = (InstanceIdentity) o;
- return provider.equals(that.provider) &&
- service.equals(that.service) &&
- instanceId.equals(that.instanceId) &&
- x509Certificate.equals(that.x509Certificate);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(provider, service, instanceId, x509Certificate);
- }
-
-}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceRefresh.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceRefresh.java
deleted file mode 100644
index d63ee7f979f..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceRefresh.java
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.ca.instance;
-
-import com.yahoo.security.Pkcs10Csr;
-
-import java.util.Objects;
-
-/**
- * Information for refreshing a instance in the system. This is the same type as InstanceRefreshInformation type in
- * the ZTS API.
- *
- * @author mpolden
- */
-public class InstanceRefresh {
-
- private final Pkcs10Csr csr;
-
- public InstanceRefresh(Pkcs10Csr csr) {
- this.csr = Objects.requireNonNull(csr, "csr must be non-null");
- }
-
- /** The Certificate Signed Request describing the wanted certificate */
- public Pkcs10Csr csr() {
- return csr;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- InstanceRefresh that = (InstanceRefresh) o;
- return csr.equals(that.csr);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(csr);
- }
-
-}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceRegistration.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceRegistration.java
deleted file mode 100644
index 231954976bf..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/instance/InstanceRegistration.java
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.ca.instance;
-
-import com.yahoo.security.Pkcs10Csr;
-import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument;
-
-import java.util.Objects;
-
-/**
- * Information for registering a new instance in the system. This is the same type as InstanceRegisterInformation type
- * in the ZTS API.
- *
- * @author mpolden
- */
-public class InstanceRegistration {
-
- private final String provider;
- private final String domain;
- private final String service;
- private final SignedIdentityDocument attestationData;
- private final Pkcs10Csr csr;
-
- public InstanceRegistration(String provider, String domain, String service, SignedIdentityDocument attestationData, Pkcs10Csr csr) {
- this.provider = Objects.requireNonNull(provider, "provider must be non-null");
- this.domain = Objects.requireNonNull(domain, "domain must be non-null");
- this.service = Objects.requireNonNull(service, "service must be non-null");
- this.attestationData = Objects.requireNonNull(attestationData, "attestationData must be non-null");
- this.csr = Objects.requireNonNull(csr, "csr must be non-null");
- }
-
- /** The provider which issued the attestation data contained in this */
- public String provider() {
- return provider;
- }
-
- /** Athenz domain of the instance */
- public String domain() {
- return domain;
- }
-
- /** Athenz service of the instance */
- public String service() {
- return service;
- }
-
- /** Host document describing this instance (received from config server) */
- public SignedIdentityDocument attestationData() {
- return attestationData;
- }
-
- /** The Certificate Signed Request describing the wanted certificate */
- public Pkcs10Csr csr() {
- return csr;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- InstanceRegistration that = (InstanceRegistration) o;
- return provider.equals(that.provider) &&
- domain.equals(that.domain) &&
- service.equals(that.service) &&
- attestationData.equals(that.attestationData) &&
- csr.equals(that.csr);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(provider, domain, service, attestationData, csr);
- }
-
- @Override
- public String toString() {
- return "InstanceRegistration{" +
- "provider='" + provider + '\'' +
- ", domain='" + domain + '\'' +
- ", service='" + service + '\'' +
- ", attestationData='" + attestationData.toString() + '\'' +
- ", csr=" + csr.toString() +
- '}';
- }
-}
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
deleted file mode 100644
index 231f22ac56b..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.ca.restapi;
-
-import com.yahoo.component.annotation.Inject;
-import com.yahoo.container.jdisc.HttpRequest;
-import com.yahoo.container.jdisc.HttpResponse;
-import com.yahoo.container.jdisc.ThreadedHttpRequestHandler;
-import com.yahoo.container.jdisc.secretstore.SecretStore;
-import com.yahoo.jdisc.http.server.jetty.RequestUtils;
-import com.yahoo.restapi.ErrorResponse;
-import com.yahoo.restapi.Path;
-import com.yahoo.restapi.SlimeJsonResponse;
-import com.yahoo.security.KeyUtils;
-import com.yahoo.security.SubjectAlternativeName;
-import com.yahoo.security.X509CertificateUtils;
-import com.yahoo.slime.Slime;
-import com.yahoo.slime.SlimeUtils;
-import com.yahoo.vespa.athenz.api.AthenzService;
-import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.InstanceConfirmation;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.InstanceValidator;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
-import com.yahoo.vespa.hosted.ca.Certificates;
-import com.yahoo.vespa.hosted.ca.instance.InstanceIdentity;
-import com.yahoo.vespa.hosted.ca.instance.InstanceRefresh;
-import com.yahoo.yolean.Exceptions;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.security.PrivateKey;
-import java.security.cert.X509Certificate;
-import java.time.Clock;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.function.Function;
-import java.util.logging.Level;
-
-/**
- * REST API for issuing and refreshing node certificates in a hosted Vespa system.
- *
- * The API implements the following subset of methods from the Athenz ZTS REST API:
- *
- * - Instance registration
- * - Instance refresh
- *
- * @author mpolden
- */
-public class CertificateAuthorityApiHandler extends ThreadedHttpRequestHandler {
-
- private final SecretStore secretStore;
- private final Certificates certificates;
- private final String caPrivateKeySecretName;
- private final String caCertificateSecretName;
- private final InstanceValidator instanceValidator;
-
- @Inject
- public CertificateAuthorityApiHandler(Context ctx, SecretStore secretStore, AthenzProviderServiceConfig athenzProviderServiceConfig, InstanceValidator instanceValidator) {
- this(ctx, secretStore, new Certificates(Clock.systemUTC()), athenzProviderServiceConfig, instanceValidator);
- }
-
- CertificateAuthorityApiHandler(Context ctx, SecretStore secretStore, Certificates certificates, AthenzProviderServiceConfig athenzProviderServiceConfig, InstanceValidator instanceValidator) {
- super(ctx);
- this.secretStore = secretStore;
- this.certificates = certificates;
- this.caPrivateKeySecretName = athenzProviderServiceConfig.sisSecretName();
- this.caCertificateSecretName = athenzProviderServiceConfig.caCertSecretName();
- this.instanceValidator = instanceValidator;
- }
-
- @Override
- public HttpResponse handle(HttpRequest request) {
- try {
- switch (request.getMethod()) {
- case POST: return handlePost(request);
- default: return ErrorResponse.methodNotAllowed("Method " + request.getMethod() + " is unsupported");
- }
- } catch (IllegalArgumentException e) {
- return ErrorResponse.badRequest(request.getMethod() + " " + request.getUri() + " failed: " + Exceptions.toMessageString(e));
- } catch (RuntimeException e) {
- log.log(Level.WARNING, "Unexpected error handling " + request.getMethod() + " " + request.getUri(), e);
- return ErrorResponse.internalServerError(Exceptions.toMessageString(e));
- }
- }
-
- private HttpResponse handlePost(HttpRequest request) {
- Path path = new Path(request.getUri());
- if (path.matches("/ca/v1/instance/")) return registerInstance(request);
- if (path.matches("/ca/v1/instance/{provider}/{domain}/{service}/{instanceId}")) return refreshInstance(request, path.get("provider"), path.get("service"), path.get("instanceId"));
- return ErrorResponse.notFoundError("Nothing at " + path);
- }
-
- private HttpResponse registerInstance(HttpRequest request) {
- var instanceRegistration = deserializeRequest(request, InstanceSerializer::registrationFromSlime);
-
- InstanceConfirmation confirmation = new InstanceConfirmation(instanceRegistration.provider(), instanceRegistration.domain(), instanceRegistration.service(), EntityBindingsMapper.toSignedIdentityDocumentEntity(instanceRegistration.attestationData()));
- confirmation.set(InstanceValidator.SAN_IPS_ATTRNAME, Certificates.getSubjectAlternativeNames(instanceRegistration.csr(), SubjectAlternativeName.Type.IP));
- confirmation.set(InstanceValidator.SAN_DNS_ATTRNAME, Certificates.getSubjectAlternativeNames(instanceRegistration.csr(), SubjectAlternativeName.Type.DNS));
- if (!instanceValidator.isValidInstance(confirmation)) {
- log.log(Level.INFO, "Invalid instance registration for " + instanceRegistration.toString());
- return ErrorResponse.forbidden("Unable to launch service: " +instanceRegistration.service());
- }
- var certificate = certificates.create(instanceRegistration.csr(), caCertificate(), caPrivateKey());
- var instanceId = Certificates.instanceIdFrom(instanceRegistration.csr());
- var identity = new InstanceIdentity(instanceRegistration.provider(), instanceRegistration.service(), instanceId,
- Optional.of(certificate));
- return new SlimeJsonResponse(InstanceSerializer.identityToSlime(identity));
- }
-
- private HttpResponse refreshInstance(HttpRequest request, String provider, String service, String instanceId) {
- var instanceRefresh = deserializeRequest(request, InstanceSerializer::refreshFromSlime);
- var instanceIdFromCsr = Certificates.instanceIdFrom(instanceRefresh.csr());
-
- var athenzService = getRequestAthenzService(request);
-
- if (!instanceIdFromCsr.equals(instanceId)) {
- throw new IllegalArgumentException("Mismatch between instance ID in URL path and instance ID in CSR " +
- "[instanceId=" + instanceId + ",instanceIdFromCsr=" + instanceIdFromCsr +
- "]");
- }
-
- // Verify that the csr instance id matches one of the certificates in the chain
- refreshesSameInstanceId(instanceIdFromCsr, request);
-
-
- // Validate that there is no privilege escalation (can only refresh same service)
- refreshesSameService(instanceRefresh, athenzService);
-
- InstanceConfirmation instanceConfirmation = new InstanceConfirmation(provider, athenzService.getDomain().getName(), athenzService.getName(), null);
- instanceConfirmation.set(InstanceValidator.SAN_IPS_ATTRNAME, Certificates.getSubjectAlternativeNames(instanceRefresh.csr(), SubjectAlternativeName.Type.IP));
- instanceConfirmation.set(InstanceValidator.SAN_DNS_ATTRNAME, Certificates.getSubjectAlternativeNames(instanceRefresh.csr(), SubjectAlternativeName.Type.DNS));
- if(!instanceValidator.isValidRefresh(instanceConfirmation)) {
- return ErrorResponse.forbidden("Unable to refresh cert: " + instanceRefresh.csr().getSubject().toString());
- }
-
- var certificate = certificates.create(instanceRefresh.csr(), caCertificate(), caPrivateKey());
- var identity = new InstanceIdentity(provider, service, instanceIdFromCsr, Optional.of(certificate));
- return new SlimeJsonResponse(InstanceSerializer.identityToSlime(identity));
- }
-
- public void refreshesSameInstanceId(String csrInstanceId, HttpRequest request) {
- String certificateInstanceId = getRequestCertificateChain(request).stream()
- .map(Certificates::instanceIdFrom)
- .filter(Optional::isPresent)
- .map(Optional::get)
- .findAny().orElseThrow(() -> new IllegalArgumentException("No client certificate with instance id in request."));
-
- if(! Objects.equals(certificateInstanceId, csrInstanceId)) {
- throw new IllegalArgumentException("Mismatch between instance ID in client certificate and instance ID in CSR " +
- "[instanceId=" + certificateInstanceId + ",instanceIdFromCsr=" + csrInstanceId +
- "]");
- }
- }
-
- private void refreshesSameService(InstanceRefresh instanceRefresh, AthenzService athenzService) {
- List<String> commonNames = X509CertificateUtils.getCommonNames(instanceRefresh.csr().getSubject());
- if(commonNames.size() != 1 && !Objects.equals(commonNames.get(0), athenzService.getFullName())) {
- throw new IllegalArgumentException(String.format("Invalid request, trying to refresh service %s using service %s.", instanceRefresh.csr().getSubject().getName(), athenzService.getFullName()));
- }
- }
-
- /** Returns CA certificate from secret store */
- private X509Certificate caCertificate() {
- return X509CertificateUtils.fromPem(secretStore.getSecret(caCertificateSecretName));
- }
-
- private List<X509Certificate> getRequestCertificateChain(HttpRequest request) {
- return Optional.ofNullable(request.getJDiscRequest().context().get(RequestUtils.JDISC_REQUEST_X509CERT))
- .map(X509Certificate[].class::cast)
- .map(Arrays::asList)
- .orElse(Collections.emptyList());
- }
-
- private AthenzService getRequestAthenzService(HttpRequest request) {
- return getRequestCertificateChain(request).stream()
- .findFirst()
- .flatMap(X509CertificateUtils::getSubjectCommonName)
- .map(AthenzService::new)
- .orElseThrow(() -> new RuntimeException("No certificate found"));
- }
-
- /** Returns CA private key from secret store */
- private PrivateKey caPrivateKey() {
- return KeyUtils.fromPemEncodedPrivateKey(secretStore.getSecret(caPrivateKeySecretName));
- }
-
- private static <T> T deserializeRequest(HttpRequest request, Function<Slime, T> serializer) {
- try {
- var slime = SlimeUtils.jsonToSlime(request.getData().readAllBytes());
- return serializer.apply(slime);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
-}
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
deleted file mode 100644
index 8c575a6403b..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializer.java
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.ca.restapi;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
-import com.yahoo.security.Pkcs10CsrUtils;
-import com.yahoo.security.X509CertificateUtils;
-import com.yahoo.slime.ArrayTraverser;
-import com.yahoo.slime.Cursor;
-import com.yahoo.slime.Slime;
-import com.yahoo.slime.SlimeUtils;
-import com.yahoo.text.StringUtilities;
-import com.yahoo.vespa.athenz.api.AthenzService;
-import com.yahoo.vespa.athenz.identityprovider.api.ClusterType;
-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.hosted.ca.instance.InstanceIdentity;
-import com.yahoo.vespa.hosted.ca.instance.InstanceRefresh;
-import com.yahoo.vespa.hosted.ca.instance.InstanceRegistration;
-
-import java.io.IOException;
-import java.time.Instant;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * @author mpolden
- */
-public class InstanceSerializer {
-
- private static final String PROVIDER_FIELD = "provider";
- private static final String DOMAIN_FIELD = "domain";
- private static final String SERVICE_FIELD = "service";
- private static final String ATTESTATION_DATA_FIELD = "attestationData";
- private static final String CSR_FIELD = "csr";
- private static final String NAME_FIELD = "service";
- private static final String INSTANCE_ID_FIELD = "instanceId";
- private static final String X509_CERTIFICATE_FIELD = "x509Certificate";
-
- private static final String IDD_SIGNATURE_FIELD = "signature";
- private static final String IDD_SIGNING_KEY_VERSION_FIELD = "signing-key-version";
- private static final String IDD_PROVIDER_UNIQUE_ID_FIELD = "provider-unique-id";
- private static final String IDD_PROVIDER_SERVICE_FIELD = "provider-service";
- private static final String IDD_DOCUMENT_VERSION_FIELD = "document-version";
- private static final String IDD_CONFIGSERVER_HOSTNAME_FIELD = "configserver-hostname";
- private static final String IDD_INSTANCE_HOSTNAME_FIELD = "instance-hostname";
- 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 String IDD_ZTS_URL_FIELD = "zts-url";
-
- private static final ObjectMapper objectMapper = new ObjectMapper();
- static {
- objectMapper.registerModule(new JavaTimeModule());
- }
-
- private InstanceSerializer() {}
-
- public static InstanceRegistration registrationFromSlime(Slime slime) {
- Cursor root = slime.get();
- return new InstanceRegistration(requireField(PROVIDER_FIELD, root).asString(),
- requireField(DOMAIN_FIELD, root).asString(),
- requireField(SERVICE_FIELD, root).asString(),
- attestationDataToIdentityDocument(StringUtilities.unescape(requireField(ATTESTATION_DATA_FIELD, root).asString())),
- Pkcs10CsrUtils.fromPem(requireField(CSR_FIELD, root).asString()));
- }
-
- public static InstanceRefresh refreshFromSlime(Slime slime) {
- Cursor root = slime.get();
- return new InstanceRefresh(Pkcs10CsrUtils.fromPem(requireField(CSR_FIELD, root).asString()));
- }
-
- public static Slime identityToSlime(InstanceIdentity identity) {
- Slime slime = new Slime();
- Cursor root = slime.setObject();
- root.setString(PROVIDER_FIELD, identity.provider());
- root.setString(NAME_FIELD, identity.service());
- root.setString(INSTANCE_ID_FIELD, identity.instanceId());
- identity.x509Certificate()
- .map(X509CertificateUtils::toPem)
- .ifPresent(pem -> root.setString(X509_CERTIFICATE_FIELD, pem));
- return slime;
- }
-
- public static SignedIdentityDocument attestationDataToIdentityDocument(String attestationData) {
- Slime slime = SlimeUtils.jsonToSlime(attestationData);
- Cursor root = slime.get();
- String signature = requireField(IDD_SIGNATURE_FIELD, root).asString();
- long signingKeyVersion = requireField(IDD_SIGNING_KEY_VERSION_FIELD, root).asLong();
- VespaUniqueInstanceId providerUniqueId = VespaUniqueInstanceId.fromDottedString(requireField(IDD_PROVIDER_UNIQUE_ID_FIELD, root).asString());
- AthenzService athenzService = new AthenzService(requireField(IDD_PROVIDER_SERVICE_FIELD, root).asString());
- long documentVersion = requireField(IDD_DOCUMENT_VERSION_FIELD, root).asLong();
- String configserverHostname = requireField(IDD_CONFIGSERVER_HOSTNAME_FIELD, root).asString();
- String instanceHostname = requireField(IDD_INSTANCE_HOSTNAME_FIELD, root).asString();
- double createdAtTimestamp = requireField(IDD_CREATED_AT_FIELD, root).asDouble();
- Instant createdAt = getJsr310Instant(createdAtTimestamp);
- 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 = clusterTypeField.valid() ? ClusterType.from(clusterTypeField.asString()) : null;
- var ztsUrlField = root.field(IDD_ZTS_URL_FIELD);
- var ztsUrl = ztsUrlField.valid() ? ztsUrlField.asString() : "";
-
-
- return new SignedIdentityDocument(signature, (int)signingKeyVersion, providerUniqueId, athenzService, (int)documentVersion,
- configserverHostname, instanceHostname, createdAt, ips, identityType, clusterType, ztsUrl);
- }
-
- private static Instant getJsr310Instant(double v) {
- try {
- return objectMapper.readValue(Double.toString(v), Instant.class);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static Cursor requireField(String fieldName, Cursor root) {
- var field = root.field(fieldName);
- if (!field.valid()) throw new IllegalArgumentException("Missing required field '" + fieldName + "'");
- return field;
- }
-
-}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/package-info.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/package-info.java
deleted file mode 100644
index 118f4b08c2a..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/package-info.java
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-/**
- * @author mpolden
- */
-@ExportPackage
-package com.yahoo.vespa.hosted.ca.restapi;
-
-import com.yahoo.osgi.annotation.ExportPackage;