summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--athenz-identity-provider-service/CMakeLists.txt2
-rw-r--r--athenz-identity-provider-service/OWNERS1
-rw-r--r--athenz-identity-provider-service/README.md5
-rw-r--r--athenz-identity-provider-service/pom.xml186
-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
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AutoGeneratedKeyProvider.java37
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGeneratorTest.java104
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java350
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/TestUtils.java27
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificateTester.java79
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificatesTest.java65
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java243
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java90
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java101
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/InstanceValidatorMock.java27
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/PrincipalFromHeaderFilter.java34
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/SecretStoreMock.java34
-rw-r--r--configserver/CMakeLists.txt1
-rw-r--r--dist/vespa.spec1
-rw-r--r--pom.xml1
37 files changed, 0 insertions, 2956 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ce11196725f..c995007663d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -68,7 +68,6 @@ include_directories(BEFORE ${CMAKE_BINARY_DIR}/configdefinitions/src)
add_subdirectory(airlift-zstd)
add_subdirectory(ann_benchmark)
add_subdirectory(application-model)
-add_subdirectory(athenz-identity-provider-service)
add_subdirectory(client)
add_subdirectory(cloud-tenant-cd)
add_subdirectory(clustercontroller-apps)
diff --git a/athenz-identity-provider-service/CMakeLists.txt b/athenz-identity-provider-service/CMakeLists.txt
deleted file mode 100644
index 75208c49bd3..00000000000
--- a/athenz-identity-provider-service/CMakeLists.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-install_jar(athenz-identity-provider-service-jar-with-dependencies.jar)
diff --git a/athenz-identity-provider-service/OWNERS b/athenz-identity-provider-service/OWNERS
deleted file mode 100644
index 569bf1cc3a1..00000000000
--- a/athenz-identity-provider-service/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-bjorncs
diff --git a/athenz-identity-provider-service/README.md b/athenz-identity-provider-service/README.md
deleted file mode 100644
index b25eb009e1b..00000000000
--- a/athenz-identity-provider-service/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-<!-- Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
-# Athenz Identity Provider Service
-
-An [Athenz Copper Argos](https://github.com/yahoo/athenz/blob/master/docs/copper_argos.md) provider implementation for configserver.
-
diff --git a/athenz-identity-provider-service/pom.xml b/athenz-identity-provider-service/pom.xml
deleted file mode 100644
index f4daa43b8e3..00000000000
--- a/athenz-identity-provider-service/pom.xml
+++ /dev/null
@@ -1,186 +0,0 @@
-<?xml version="1.0"?>
-<!-- Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
-<project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
- http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <artifactId>athenz-identity-provider-service</artifactId>
- <packaging>container-plugin</packaging>
- <parent>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>parent</artifactId>
- <version>8-SNAPSHOT</version>
- <relativePath>../parent/pom.xml</relativePath>
- </parent>
- <dependencies>
- <!-- PROVIDED -->
- <dependency>
- <groupId>com.google.inject</groupId>
- <artifactId>guice</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>component</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>container-apache-http-client-bundle</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>container-dev</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>jdisc_core</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-core</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>node-repository</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>config-provisioning</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>config-model-api</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>vespa-athenz</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>security-utils</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
-
- <!-- TEST -->
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>zkfacade</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter-api</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter-engine</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>orchestrator</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>application</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>testutil</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- </exclusion>
- <exclusion>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest-core</artifactId>
- </exclusion>
- <exclusion>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest-library</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>flags</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>container-test</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>bundle-plugin</artifactId>
- <extensions>true</extensions>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <!-- Illegal reflective access by guice. TODO: try to remove for guice >3.0 -->
- <argLine>
- --add-opens=java.base/java.lang=ALL-UNNAMED
- </argLine>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
-</project>
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;
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AutoGeneratedKeyProvider.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AutoGeneratedKeyProvider.java
deleted file mode 100644
index 67e5caa0c18..00000000000
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AutoGeneratedKeyProvider.java
+++ /dev/null
@@ -1,37 +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.security.KeyAlgorithm;
-import com.yahoo.security.KeyUtils;
-
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
-/**
- * @author bjorncs
- */
-public class AutoGeneratedKeyProvider implements KeyProvider {
-
- private final KeyPair keyPair;
-
- public AutoGeneratedKeyProvider() {
- keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA, 2048);
- }
-
- @Override
- public PrivateKey getPrivateKey(int version) {
- return keyPair.getPrivate();
- }
-
- @Override
- public PublicKey getPublicKey(int version) {
- return keyPair.getPublic();
- }
-
- public KeyPair getKeyPair() {
- return keyPair;
- }
-}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGeneratorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGeneratorTest.java
deleted file mode 100644
index 340be33c2a3..00000000000
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/IdentityDocumentGeneratorTest.java
+++ /dev/null
@@ -1,104 +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.Version;
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.ApplicationName;
-import com.yahoo.config.provision.ClusterMembership;
-import com.yahoo.config.provision.Environment;
-import com.yahoo.config.provision.InstanceName;
-import com.yahoo.config.provision.NodeResources;
-import com.yahoo.config.provision.NodeType;
-import com.yahoo.config.provision.RegionName;
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.config.provision.TenantName;
-import com.yahoo.config.provision.Zone;
-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.ca.restapi.mock.SecretStoreMock;
-import com.yahoo.vespa.hosted.provision.Node;
-import com.yahoo.vespa.hosted.provision.NodeRepository;
-import com.yahoo.vespa.hosted.provision.node.Allocation;
-import com.yahoo.vespa.hosted.provision.node.Generation;
-import com.yahoo.vespa.hosted.provision.node.IP;
-import com.yahoo.vespa.hosted.provision.node.Nodes;
-import com.yahoo.vespa.hosted.provision.testutils.MockNodeFlavors;
-import org.junit.jupiter.api.Test;
-
-import java.util.Optional;
-import java.util.Set;
-
-import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.TestUtils.getAthenzProviderConfig;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-/**
- * @author valerijf
- */
-public class IdentityDocumentGeneratorTest {
-
- private static final Zone ZONE = new Zone(SystemName.cd, Environment.dev, RegionName.from("us-north-1"));
-
- @Test
- void generates_valid_identity_document() {
- String parentHostname = "docker-host";
- String containerHostname = "docker-container";
-
- ApplicationId appid = ApplicationId.from(
- TenantName.from("tenant"), ApplicationName.from("application"), InstanceName.from("default"));
- Allocation allocation = new Allocation(appid,
- ClusterMembership.from("container/default/0/0", Version.fromString("1.2.3"), Optional.empty()),
- new NodeResources(1, 1, 1, 1),
- Generation.initial(),
- false);
- Node parentNode = Node.create("ostkid",
- IP.Config.ofEmptyPool(Set.of("127.0.0.1")),
- parentHostname,
- new MockNodeFlavors().getFlavorOrThrow("default"),
- NodeType.host).build();
- Node containerNode = Node.reserve(Set.of("::1"),
- containerHostname,
- parentHostname,
- new MockNodeFlavors().getFlavorOrThrow("default").resources(),
- NodeType.tenant)
- .allocation(allocation).build();
- NodeRepository nodeRepository = mock(NodeRepository.class);
- Nodes nodes = mock(Nodes.class);
- when(nodeRepository.nodes()).thenReturn(nodes);
-
- when(nodes.node(eq(parentHostname))).thenReturn(Optional.of(parentNode));
- when(nodes.node(eq(containerHostname))).thenReturn(Optional.of(containerNode));
- AutoGeneratedKeyProvider keyProvider = new AutoGeneratedKeyProvider();
- SecretStoreMock secretStore = new SecretStoreMock();
-
- String dnsSuffix = "vespa.dns.suffix";
- AthenzProviderServiceConfig config = getAthenzProviderConfig("domain", "service", dnsSuffix);
- IdentityDocumentGenerator identityDocumentGenerator =
- new IdentityDocumentGenerator(config, nodeRepository, ZONE, keyProvider, secretStore);
- SignedIdentityDocument signedIdentityDocument = identityDocumentGenerator.generateSignedIdentityDocument(containerHostname, IdentityType.TENANT);
-
- // Verify attributes
- assertEquals(containerHostname, signedIdentityDocument.instanceHostname());
-
- String environment = "dev";
- String region = "us-north-1";
-
- VespaUniqueInstanceId expectedProviderUniqueId =
- new VespaUniqueInstanceId(0, "default", "default", "application", "tenant", region, environment, IdentityType.TENANT);
- assertEquals(expectedProviderUniqueId, signedIdentityDocument.providerUniqueId());
-
- // Validate that container ips are present
- assertTrue(signedIdentityDocument.ipAddresses().contains("::1"));
-
- IdentityDocumentSigner signer = new IdentityDocumentSigner();
-
- // Validate signature
- assertTrue(signer.hasValidSignature(signedIdentityDocument, keyProvider.getPublicKey(0)));
- }
-}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java
deleted file mode 100644
index 42d6b92dea1..00000000000
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/InstanceValidatorTest.java
+++ /dev/null
@@ -1,350 +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.Version;
-import com.yahoo.config.model.api.ApplicationInfo;
-import com.yahoo.config.model.api.HostInfo;
-import com.yahoo.config.model.api.Model;
-import com.yahoo.config.model.api.ServiceInfo;
-import com.yahoo.config.model.api.SuperModel;
-import com.yahoo.config.model.api.SuperModelProvider;
-import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.CloudAccount;
-import com.yahoo.config.provision.ClusterMembership;
-import com.yahoo.config.provision.Environment;
-import com.yahoo.config.provision.NodeResources;
-import com.yahoo.config.provision.NodeType;
-import com.yahoo.config.provision.RegionName;
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.config.provision.Zone;
-import com.yahoo.security.KeyAlgorithm;
-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.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.curator.mock.MockCurator;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.InstanceValidator.ValidationException;
-import com.yahoo.vespa.hosted.ca.restapi.mock.SecretStoreMock;
-import com.yahoo.vespa.hosted.provision.Node;
-import com.yahoo.vespa.hosted.provision.NodeList;
-import com.yahoo.vespa.hosted.provision.NodeRepository;
-import com.yahoo.vespa.hosted.provision.node.IP;
-import com.yahoo.vespa.hosted.provision.node.Nodes;
-import com.yahoo.vespa.hosted.provision.testutils.MockNodeFlavors;
-import com.yahoo.vespa.hosted.provision.testutils.MockNodeRepository;
-import org.junit.jupiter.api.Test;
-
-import java.security.PrivateKey;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-import java.util.stream.Stream;
-
-import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.InstanceValidator.SERVICE_PROPERTIES_DOMAIN_KEY;
-import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.InstanceValidator.SERVICE_PROPERTIES_SERVICE_KEY;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-/**
- * @author valerijf
- * @author bjorncs
- * @author mortent
- */
-public class InstanceValidatorTest {
-
- private final ApplicationId applicationId = ApplicationId.from("tenant", "application", "instance");
- private final String domain = "domain";
- private final String service = "service";
-
- private final AthenzService vespaTenantDomain = new AthenzService("vespa.vespa.tenant");
- private final AutoGeneratedKeyProvider keyProvider = new AutoGeneratedKeyProvider();
- private final SecretStoreMock secretStore = new SecretStoreMock();
- private final String sisSecretName = "sis-secret-name";
-
- @Test
- void application_does_not_exist() {
- SuperModelProvider superModelProvider = mockSuperModelProvider();
- InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, null, vespaTenantDomain, secretStore, sisSecretName);
- assertFalse(instanceValidator.isValidInstance(createRegisterInstanceConfirmation(applicationId, domain, service)));
- }
-
- @Test
- void application_does_not_have_domain_set() {
- SuperModelProvider superModelProvider = mockSuperModelProvider(
- mockApplicationInfo(applicationId, 5, Collections.emptyList()));
- InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, new IdentityDocumentSigner(), vespaTenantDomain, secretStore, sisSecretName);
-
- assertFalse(instanceValidator.isValidInstance(createRegisterInstanceConfirmation(applicationId, domain, service)));
- }
-
- @Test
- void application_has_wrong_domain() {
- ServiceInfo serviceInfo = new ServiceInfo("serviceName", "type", Collections.emptyList(),
- Collections.singletonMap(SERVICE_PROPERTIES_DOMAIN_KEY, "not-domain"), "confId", "hostName");
-
- SuperModelProvider superModelProvider = mockSuperModelProvider(
- mockApplicationInfo(applicationId, 5, Collections.singletonList(serviceInfo)));
- InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, null, vespaTenantDomain, secretStore, sisSecretName);
-
- assertFalse(instanceValidator.isValidInstance(createRegisterInstanceConfirmation(applicationId, domain, service)));
- }
-
- @Test
- void application_has_same_domain_and_service() {
- Map<String, String> properties = new HashMap<>();
- properties.put(SERVICE_PROPERTIES_DOMAIN_KEY, domain);
- properties.put(SERVICE_PROPERTIES_SERVICE_KEY, service);
-
- ServiceInfo serviceInfo = new ServiceInfo("serviceName", "type", Collections.emptyList(),
- properties, "confId", "hostName");
-
- SuperModelProvider superModelProvider = mockSuperModelProvider(
- mockApplicationInfo(applicationId, 5, Collections.singletonList(serviceInfo)));
- IdentityDocumentSigner signer = mock(IdentityDocumentSigner.class);
- when(signer.hasValidSignature(any(), any())).thenReturn(true);
- InstanceValidator instanceValidator = new InstanceValidator(mock(KeyProvider.class), superModelProvider, mockNodeRepo(), signer, vespaTenantDomain, secretStore, sisSecretName);
-
- assertTrue(instanceValidator.isValidInstance(createRegisterInstanceConfirmation(applicationId, domain, service)));
- }
-
- @Test
- void rejects_invalid_provider_unique_id_in_csr() {
- SuperModelProvider superModelProvider = mockSuperModelProvider();
- InstanceValidator instanceValidator = new InstanceValidator(null, superModelProvider, null, null, vespaTenantDomain, secretStore, sisSecretName);
- InstanceConfirmation instanceConfirmation = createRegisterInstanceConfirmation(applicationId, domain, service);
- VespaUniqueInstanceId tamperedId = new VespaUniqueInstanceId(0, "default", "instance", "app", "tenant", "us-north-1", "dev", IdentityType.NODE);
- instanceConfirmation.set("sanDNS", tamperedId.asDottedString() + ".instanceid.athenz.dev-us-north-1.vespa.yahoo.cloud");
- assertFalse(instanceValidator.isValidInstance(instanceConfirmation));
- }
-
- @Test
- void rejects_unknown_ips_in_csr() {
- NodeRepository nodeRepository = mockNodeRepo();
- InstanceValidator instanceValidator = new InstanceValidator(null, mockSuperModelProvider(), nodeRepository, null, vespaTenantDomain, secretStore, sisSecretName);
- InstanceConfirmation instanceConfirmation = createRegisterInstanceConfirmation(applicationId, domain, service);
- Set<String> nodeIp = nodeRepository.nodes().list().owner(applicationId).stream().findFirst()
- .map(Node::ipConfig)
- .map(IP.Config::primary)
- .orElseThrow(() -> new RuntimeException("No ipaddress for mocked node"));
-
- List<String> ips = new ArrayList<>(nodeIp);
- ips.add("::ff");
- instanceConfirmation.set("sanIP", String.join(",", ips));
- assertFalse(instanceValidator.isValidInstance(instanceConfirmation));
- }
-
- @Test
- void rejects_invalid_cluster_type_in_csr() {
- var props = Map.of(SERVICE_PROPERTIES_DOMAIN_KEY, domain, SERVICE_PROPERTIES_SERVICE_KEY, service);
- var info = new ServiceInfo("serviceName", "type", List.of(), props, "confId", "hostName");
- var provider = mockSuperModelProvider(mockApplicationInfo(applicationId, 5, List.of(info)));
- var instanceValidator = new InstanceValidator(keyProvider, provider, mockNodeRepo(), new IdentityDocumentSigner(), vespaTenantDomain, secretStore, sisSecretName);
- var instanceConfirmation = createRegisterInstanceConfirmation(applicationId, domain, service);
- instanceConfirmation.set("sanURI", "vespa://cluster-type/content");
- var exception = assertThrows(ValidationException.class, () -> instanceValidator.validateInstance(instanceConfirmation));
- var expectedMsg = "Illegal SAN URIs: expected '[vespa://cluster-type/container]' found '[vespa://cluster-type/content]'";
- assertEquals(expectedMsg, exception.getMessage());
- }
-
- @Test
- void accepts_valid_refresh_requests() {
- NodeRepository nodeRepository = mock(NodeRepository.class);
- Nodes nodes = mock(Nodes.class);
- when(nodeRepository.nodes()).thenReturn(nodes);
- InstanceValidator instanceValidator = new InstanceValidator(null, null, nodeRepository, new IdentityDocumentSigner(), vespaTenantDomain, secretStore, sisSecretName);
-
- List<Node> nodeList = createNodes(10);
- Node node = nodeList.get(0);
- nodeList = allocateNode(nodeList, node, applicationId);
- when(nodes.list()).thenReturn(NodeList.copyOf(nodeList));
- String nodeIp = node.ipConfig().primary().stream().findAny().orElseThrow(() -> new RuntimeException("No ipaddress for mocked node"));
- InstanceConfirmation instanceConfirmation = createRefreshInstanceConfirmation(applicationId, domain, service, List.of(nodeIp));
-
- assertTrue(instanceValidator.isValidRefresh(instanceConfirmation));
- }
-
- @Test
- void rejects_refresh_on_ip_mismatch() {
- NodeRepository nodeRepository = mockNodeRepo();
- InstanceValidator instanceValidator = new InstanceValidator(null, null, nodeRepository, new IdentityDocumentSigner(), vespaTenantDomain, secretStore, sisSecretName);
-
- Set<String> nodeIp = nodeRepository.nodes().list().owner(applicationId).stream().findFirst()
- .map(Node::ipConfig)
- .map(IP.Config::primary)
- .orElseThrow(() -> new RuntimeException("No ipaddress for mocked node"));
-
- List<String> ips = new ArrayList<>(nodeIp);
- ips.add("::ff");
- // Add invalid ip to list of ip addresses
- InstanceConfirmation instanceConfirmation = createRefreshInstanceConfirmation(applicationId, domain, service, ips);
-
- assertFalse(instanceValidator.isValidRefresh(instanceConfirmation));
- }
-
- @Test
- void rejects_refresh_when_node_is_not_allocated() {
- NodeRepository nodeRepository = mock(NodeRepository.class);
- Nodes nodes = mock(Nodes.class);
- when(nodeRepository.nodes()).thenReturn(nodes);
-
- InstanceValidator instanceValidator = new InstanceValidator(null, null, nodeRepository, new IdentityDocumentSigner(), vespaTenantDomain, secretStore, sisSecretName);
-
- List<Node> nodeList = createNodes(10);
-
- when(nodes.list()).thenReturn(NodeList.copyOf(nodeList));
- InstanceConfirmation instanceConfirmation = createRefreshInstanceConfirmation(applicationId, domain, service, List.of("::11"));
-
- assertFalse(instanceValidator.isValidRefresh(instanceConfirmation));
-
- }
-
- @Test
- public void uses_correct_keys_in_public() throws ValidationException {
- var sisKeyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA, 2048);
- secretStore.setSecret(sisSecretName, KeyUtils.toPem(sisKeyPair.getPrivate()));
-
- var props = Map.of(SERVICE_PROPERTIES_DOMAIN_KEY, domain, SERVICE_PROPERTIES_SERVICE_KEY, service);
- var info = new ServiceInfo("serviceName", "type", List.of(), props, "confId", "hostName");
- var provider = mockSuperModelProvider(mockApplicationInfo(applicationId, 5, List.of(info)));
-
- {
- // non Enclave hosts in public should use the SIS key
- var nonEnclaveHosts = createNodes(10, Optional.empty());
- var instanceValidator = new InstanceValidator(keyProvider, provider, mockNodeRepo(nonEnclaveHosts, SystemName.PublicCd), new IdentityDocumentSigner(), vespaTenantDomain, secretStore, sisSecretName);
- var instanceConfirmation = createRegisterInstanceConfirmation(applicationId, domain, service, sisKeyPair.getPrivate());
- instanceValidator.validateInstance(instanceConfirmation);
- }
- {
- // Enclave hosts should use the Vespa provider key
- var enclaveHosts = createNodes(10, Optional.of(CloudAccount.from("123456789012")));
- var instanceValidator = new InstanceValidator(keyProvider, provider, mockNodeRepo(enclaveHosts, SystemName.PublicCd), new IdentityDocumentSigner(), vespaTenantDomain, secretStore, sisSecretName);
- var instanceConfirmation = createRegisterInstanceConfirmation(applicationId, domain, service, keyProvider.getPrivateKey(0));
- instanceValidator.validateInstance(instanceConfirmation);
- }
- }
-
- private NodeRepository mockNodeRepo() {
- return mockNodeRepo(createNodes(10), SystemName.cd);
- }
-
- private NodeRepository mockNodeRepo(List<Node> nodeList, SystemName systemName) {
- NodeRepository nodeRepository = mock(NodeRepository.class);
- Nodes nodes = mock(Nodes.class);
- when(nodeRepository.nodes()).thenReturn(nodes);
- when(nodeRepository.zone()).thenReturn(new Zone(systemName, Environment.dev, RegionName.from("us-north-1")));
- Node node = nodeList.get(0);
- nodeList = allocateNode(nodeList, node, applicationId);
- when(nodes.list()).thenReturn(NodeList.copyOf(nodeList));
- return nodeRepository;
- }
-
- private InstanceConfirmation createRegisterInstanceConfirmation(
- ApplicationId applicationId, String domain, String service) {
- return createRegisterInstanceConfirmation(applicationId, domain, service, keyProvider.getPrivateKey(0));
- }
-
- private InstanceConfirmation createRegisterInstanceConfirmation(
- ApplicationId applicationId, String domain, String service, PrivateKey signingKey) {
- VespaUniqueInstanceId vespaUniqueInstanceId = new VespaUniqueInstanceId(0, "default", applicationId.instance().value(), applicationId.application().value(), applicationId.tenant().value(), "us-north-1", "dev", IdentityType.NODE);
- var domainService = new AthenzService(domain, service);
- var clock = Instant.now();
- var clusterType = ClusterType.CONTAINER;
- var signature = new IdentityDocumentSigner()
- .generateSignature(
- vespaUniqueInstanceId, domainService, "localhost", "localhost", clock, Set.of(),
- IdentityType.NODE, signingKey);
- SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument(
- signature, 0, vespaUniqueInstanceId, domainService, 0, "localhost", "localhost",
- clock, Collections.emptySet(), IdentityType.NODE, clusterType, "https://zts.url");
- return createInstanceConfirmation(vespaUniqueInstanceId, domain, service, signedIdentityDocument);
-
- }
-
- private InstanceConfirmation createRefreshInstanceConfirmation(ApplicationId applicationId, String domain, String service, List<String> ips) {
- VespaUniqueInstanceId vespaUniqueInstanceId = new VespaUniqueInstanceId(0, "default", applicationId.instance().value(), applicationId.application().value(), applicationId.tenant().value(), "us-north-1", "dev", IdentityType.NODE);
- InstanceConfirmation instanceConfirmation = createInstanceConfirmation(vespaUniqueInstanceId, domain, service, null);
- instanceConfirmation.set("sanIP", String.join(",", ips));
- return instanceConfirmation;
- }
-
- private InstanceConfirmation createInstanceConfirmation(VespaUniqueInstanceId vespaUniqueInstanceId, String domain, String service, SignedIdentityDocument identityDocument) {
- InstanceConfirmation instanceConfirmation = new InstanceConfirmation(
- "vespa.vespa.cd.provider_dev_us-north-1",
- domain,
- service,
- Optional.ofNullable(identityDocument)
- .map(EntityBindingsMapper::toSignedIdentityDocumentEntity)
- .orElse(null));
- instanceConfirmation.set("sanDNS", vespaUniqueInstanceId.asDottedString() + ".instanceid.athenz.dev-us-north-1.vespa.yahoo.cloud");
- instanceConfirmation.set("sanURI", "vespa://cluster-type/container");
- return instanceConfirmation;
- }
-
- private SuperModelProvider mockSuperModelProvider(ApplicationInfo... appInfos) {
- SuperModel superModel = new SuperModel(Stream.of(appInfos)
- .collect(Collectors.toMap(
- ApplicationInfo::getApplicationId,
- Function.identity()
- )
- ),
- true);
-
- SuperModelProvider superModelProvider = mock(SuperModelProvider.class);
- when(superModelProvider.getSuperModel()).thenReturn(superModel);
- return superModelProvider;
- }
-
- private ApplicationInfo mockApplicationInfo(ApplicationId appId, int numHosts, List<ServiceInfo> serviceInfo) {
- List<HostInfo> hosts = IntStream.range(0, numHosts)
- .mapToObj(i -> new HostInfo("host-" + i + "." + appId.toShortString() + ".yahoo.com", serviceInfo))
- .toList();
-
- Model model = mock(Model.class);
- when(model.getHosts()).thenReturn(hosts);
-
- return new ApplicationInfo(appId, 0, model);
- }
-
- private List<Node> createNodes(int num) {
- return createNodes(num, Optional.empty());
- }
-
- private List<Node> createNodes(int num, Optional<CloudAccount> cloudAccount) {
- MockNodeFlavors flavors = new MockNodeFlavors();
- List<Node> nodeList = new ArrayList<>();
- for (int i = 0; i < num; i++) {
- Node.Builder builder = Node.create("foo" + i, new IP.Config(Set.of("::1" + i, "::2" + i, "::3" + i), Set.of()),
- "foo" + i, flavors.getFlavorOrThrow("default"), NodeType.tenant);
- cloudAccount.ifPresent(builder::cloudAccount);
- Node node = builder.build();
- nodeList.add(node);
- }
- return nodeList;
- }
-
- private List<Node> allocateNode(List<Node> nodeList, Node node, ApplicationId applicationId) {
- nodeList.removeIf(n -> n.id().equals(node.id()));
- nodeList.add(node.allocate(applicationId,
- ClusterMembership.from("container/default/0/0", Version.fromString("6.123.4"), Optional.empty()),
- new NodeResources(1, 1, 1, 1),
- Instant.now()));
- return nodeList;
- }
-}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/TestUtils.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/TestUtils.java
deleted file mode 100644
index 4110ad2bfa2..00000000000
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/TestUtils.java
+++ /dev/null
@@ -1,27 +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.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
-
-/**
- * @author bjorncs
- */
-public class TestUtils {
-
- public static AthenzProviderServiceConfig getAthenzProviderConfig(String domain,
- String service,
- String dnsSuffix) {
- AthenzProviderServiceConfig.Builder zoneConfig =
- new AthenzProviderServiceConfig.Builder()
- .serviceName(service)
- .secretVersion(0)
- .domain(domain)
- .certDnsSuffix(dnsSuffix)
- .ztsUrl("localhost/zts")
- .secretName("s3cr3t")
- .caCertSecretName(domain + ".ca.cert");
- return new AthenzProviderServiceConfig(
- zoneConfig.athenzCaTrustStore("/dummy/path/to/athenz-ca.jks"));
- }
-
-}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificateTester.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificateTester.java
deleted file mode 100644
index 4012776949e..00000000000
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificateTester.java
+++ /dev/null
@@ -1,79 +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.KeyAlgorithm;
-import com.yahoo.security.KeyUtils;
-import com.yahoo.security.Pkcs10Csr;
-import com.yahoo.security.Pkcs10CsrBuilder;
-import com.yahoo.security.SignatureAlgorithm;
-import com.yahoo.security.SubjectAlternativeName;
-import com.yahoo.security.X509CertificateBuilder;
-
-import javax.security.auth.x500.X500Principal;
-import java.math.BigInteger;
-import java.security.KeyPair;
-import java.security.cert.X509Certificate;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.List;
-
-import static com.yahoo.security.SignatureAlgorithm.SHA256_WITH_ECDSA;
-
-/**
- * Helper class for creating certificates, CSRs etc. for testing purposes.
- *
- * @author mpolden
- */
-public class CertificateTester {
-
- private CertificateTester() {}
-
- public static X509Certificate createCertificate() {
- var keyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 256);
- return createCertificate("subject", keyPair);
- }
-
- public static X509Certificate createCertificate(String cn, KeyPair keyPair) {
- var subject = new X500Principal("CN=" + cn);
- return X509CertificateBuilder.fromKeypair(keyPair,
- subject,
- Instant.EPOCH,
- Instant.EPOCH.plus(Duration.ofMinutes(1)),
- SHA256_WITH_ECDSA,
- BigInteger.ONE)
- .build();
- }
-
- public static Pkcs10Csr createCsr() {
- return createCsr(List.of(), List.of());
- }
-
- public static Pkcs10Csr createCsr(String dnsName) {
- return createCsr(List.of(dnsName), List.of());
- }
-
- public static Pkcs10Csr createCsr(List<String> dnsNames) {
- return createCsr(dnsNames, List.of());
- }
-
- public static Pkcs10Csr createCsr(String cn, List<String> dnsNames) {
- return createCsr(cn, dnsNames, List.of());
- }
-
- public static Pkcs10Csr createCsr(List<String> dnsNames, List<String> ipAddresses) {
- return createCsr("subject", dnsNames, ipAddresses);
- }
- public static Pkcs10Csr createCsr(String cn, List<String> dnsNames, List<String> ipAddresses) {
- X500Principal subject = new X500Principal("CN=" + cn);
- KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 256);
- var builder = Pkcs10CsrBuilder.fromKeypair(subject, keyPair, SignatureAlgorithm.SHA512_WITH_ECDSA);
- for (var dnsName : dnsNames) {
- builder = builder.addSubjectAlternativeName(SubjectAlternativeName.Type.DNS, dnsName);
- }
- for (var ipAddress : ipAddresses) {
- builder = builder.addSubjectAlternativeName(SubjectAlternativeName.Type.IP, ipAddress);
- }
- return builder.build();
- }
-
-}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificatesTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificatesTest.java
deleted file mode 100644
index dd3ddeeb804..00000000000
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificatesTest.java
+++ /dev/null
@@ -1,65 +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.KeyAlgorithm;
-import com.yahoo.security.KeyUtils;
-import com.yahoo.security.SubjectAlternativeName;
-import com.yahoo.test.ManualClock;
-import org.junit.jupiter.api.Test;
-
-import java.security.KeyPair;
-import java.security.cert.X509Certificate;
-import java.time.Duration;
-import java.util.List;
-
-import static java.time.temporal.ChronoUnit.SECONDS;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-/**
- * @author mpolden
- */
-public class CertificatesTest {
-
- private final KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 256);
- private final X509Certificate caCertificate = CertificateTester.createCertificate("CA", keyPair);
-
- @Test
- void expiry() {
- var clock = new ManualClock();
- var certificates = new Certificates(clock);
- var csr = CertificateTester.createCsr();
- var certificate = certificates.create(csr, caCertificate, keyPair.getPrivate());
- var now = clock.instant();
-
- assertEquals(now.minus(Duration.ofHours(1)).truncatedTo(SECONDS), certificate.getNotBefore().toInstant());
- assertEquals(now.plus(Duration.ofDays(30)).truncatedTo(SECONDS), certificate.getNotAfter().toInstant());
- }
-
- @Test
- void add_san_from_csr() throws Exception {
- var certificates = new Certificates(new ManualClock());
- var dnsName = "host.example.com";
- var ip = "192.0.2.42";
- var csr = CertificateTester.createCsr(List.of(dnsName), List.of(ip));
- var certificate = certificates.create(csr, caCertificate, keyPair.getPrivate());
-
- assertNotNull(certificate.getSubjectAlternativeNames());
- assertEquals(2, certificate.getSubjectAlternativeNames().size());
-
- var subjectAlternativeNames = List.copyOf(certificate.getSubjectAlternativeNames());
- assertEquals(List.of(SubjectAlternativeName.Type.DNS.getTag(), dnsName),
- subjectAlternativeNames.get(0));
- assertEquals(List.of(SubjectAlternativeName.Type.IP.getTag(), ip),
- subjectAlternativeNames.get(1));
- }
-
- @Test
- void parse_instance_id() {
- var instanceId = "1.cluster1.default.app1.tenant1.us-north-1.prod.node";
- var instanceIdWithSuffix = instanceId + ".instanceid.athenz.dev-us-north-1.vespa.aws.oath.cloud";
- var csr = CertificateTester.createCsr(List.of("foo", "bar", instanceIdWithSuffix));
- assertEquals(instanceId, Certificates.instanceIdFrom(csr));
- }
-
-}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java
deleted file mode 100644
index bf2115e8759..00000000000
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java
+++ /dev/null
@@ -1,243 +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.application.container.handler.Request;
-import com.yahoo.jdisc.http.server.jetty.RequestUtils;
-import com.yahoo.security.KeyAlgorithm;
-import com.yahoo.security.KeyUtils;
-import com.yahoo.security.Pkcs10Csr;
-import com.yahoo.security.Pkcs10CsrUtils;
-import com.yahoo.security.X509CertificateUtils;
-import com.yahoo.slime.SlimeUtils;
-import com.yahoo.text.StringUtilities;
-import com.yahoo.vespa.athenz.api.AthenzPrincipal;
-import com.yahoo.vespa.athenz.api.AthenzService;
-import com.yahoo.vespa.athenz.client.ErrorHandler;
-import com.yahoo.vespa.athenz.client.zts.DefaultZtsClient;
-import com.yahoo.vespa.hosted.ca.CertificateTester;
-import org.apache.http.client.ResponseHandler;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import javax.net.ssl.SSLContext;
-import java.net.URI;
-import java.nio.charset.StandardCharsets;
-import java.security.Principal;
-import java.security.cert.X509Certificate;
-import java.util.List;
-import java.util.Optional;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
-/**
- * @author mpolden
- */
-public class CertificateAuthorityApiTest extends ContainerTester {
-
- private static final String INSTANCE_ID = "1.cluster1.default.app1.tenant1.us-north-1.prod.node";
- private static final String INSTANCE_ID_WITH_SUFFIX = INSTANCE_ID + ".instanceid.athenz.dev-us-north-1.vespa.aws.oath.cloud";
- private static final String INVALID_INSTANCE_ID = "1.cluster1.default.otherapp.othertenant.us-north-1.prod.node";
- private static final String INVALID_INSTANCE_ID_WITH_SUFFIX = INVALID_INSTANCE_ID + ".instanceid.athenz.dev-us-north-1.vespa.aws.oath.cloud";
-
- private static final String CONTAINER_IDENTITY = "vespa.external.tenant";
- private static final String HOST_IDENTITY = "vespa.external.tenant-host";
-
- @BeforeEach
- public void before() {
- setCaCertificateAndKey();
- }
-
- @Test
- void register_instance() throws Exception {
- // POST instance registration
- var csr = CertificateTester.createCsr(List.of("node1.example.com", INSTANCE_ID_WITH_SUFFIX));
- assertIdentityResponse(new Request("http://localhost:12345/ca/v1/instance/",
- instanceRegistrationJson(csr),
- Request.Method.POST));
-
- // POST instance registration with ZTS client
- var ztsClient = new TestZtsClient(new AthenzPrincipal(new AthenzService(HOST_IDENTITY)), null, URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
- var instanceIdentity = ztsClient.registerInstance(new AthenzService("vespa.external", "provider_prod_us-north-1"),
- new AthenzService(CONTAINER_IDENTITY),
- getAttestationData(),
- csr);
- assertEquals("CN=Vespa CA", instanceIdentity.certificate().getIssuerX500Principal().getName());
- }
-
- private X509Certificate registerInstance() throws Exception {
- // POST instance registration
- var csr = CertificateTester.createCsr(CONTAINER_IDENTITY, List.of("node1.example.com", INSTANCE_ID_WITH_SUFFIX));
- assertIdentityResponse(new Request("http://localhost:12345/ca/v1/instance/",
- instanceRegistrationJson(csr),
- Request.Method.POST));
-
- // POST instance registration with ZTS client
- var ztsClient = new TestZtsClient(new AthenzPrincipal(new AthenzService(HOST_IDENTITY)), null, URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
- var instanceIdentity = ztsClient.registerInstance(new AthenzService("vespa.external", "provider_prod_us-north-1"),
- new AthenzService(CONTAINER_IDENTITY),
- getAttestationData(),
- csr);
- return instanceIdentity.certificate();
- }
-
- @Test
- void refresh_instance() throws Exception {
- // Register instance to get cert
- var certificate = registerInstance();
-
- // POST instance refresh
- var principal = new AthenzPrincipal(new AthenzService(CONTAINER_IDENTITY));
- var csr = CertificateTester.createCsr(principal.getIdentity().getFullName(), List.of("node1.example.com", INSTANCE_ID_WITH_SUFFIX));
- var request = new Request("http://localhost:12345/ca/v1/instance/vespa.external.provider_prod_us-north-1/vespa.external/tenant/" + INSTANCE_ID,
- instanceRefreshJson(csr),
- Request.Method.POST,
- principal);
- request.getAttributes().put(RequestUtils.JDISC_REQUEST_X509CERT, new X509Certificate[]{certificate});
- assertIdentityResponse(request);
-
- // POST instance refresh with ZTS client
- var ztsClient = new TestZtsClient(principal, certificate, URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
- var instanceIdentity = ztsClient.refreshInstance(new AthenzService("vespa.external", "provider_prod_us-north-1"),
- new AthenzService(CONTAINER_IDENTITY),
- INSTANCE_ID,
- csr);
- assertEquals("CN=Vespa CA", instanceIdentity.certificate().getIssuerX500Principal().getName());
- }
-
- @Test
- void invalid_requests() throws Exception {
- // POST instance registration with missing fields
- assertResponse(400, "{\"error-code\":\"BAD_REQUEST\",\"message\":\"POST http://localhost:12345/ca/v1/instance/ failed: Missing required field 'provider'\"}",
- new Request("http://localhost:12345/ca/v1/instance/",
- new byte[0],
- Request.Method.POST));
-
- // POST instance registration without DNS name in CSR
- var csr = CertificateTester.createCsr();
- var request = new Request("http://localhost:12345/ca/v1/instance/",
- instanceRegistrationJson(csr),
- Request.Method.POST);
- assertResponse(400, "{\"error-code\":\"BAD_REQUEST\",\"message\":\"POST http://localhost:12345/ca/v1/instance/ failed: No instance ID found in CSR\"}", request);
-
- // POST instance refresh with missing field
- assertResponse(400, "{\"error-code\":\"BAD_REQUEST\",\"message\":\"POST http://localhost:12345/ca/v1/instance/vespa.external.provider_prod_us-north-1/vespa.external/tenant/1.cluster1.default.app1.tenant1.us-north-1.prod.node failed: Missing required field 'csr'\"}",
- new Request("http://localhost:12345/ca/v1/instance/vespa.external.provider_prod_us-north-1/vespa.external/tenant/" + INSTANCE_ID,
- new byte[0],
- Request.Method.POST));
-
- // POST instance refresh where instanceId does not match CSR dnsName
- var principal = new AthenzPrincipal(new AthenzService(CONTAINER_IDENTITY));
- var cert = CertificateTester.createCertificate(CONTAINER_IDENTITY, KeyUtils.generateKeypair(KeyAlgorithm.EC));
- csr = CertificateTester.createCsr(principal.getIdentity().getFullName(), List.of("node1.example.com", INSTANCE_ID_WITH_SUFFIX));
- request = new Request("http://localhost:12345/ca/v1/instance/vespa.external.provider_prod_us-north-1/vespa.external/tenant/foobar",
- instanceRefreshJson(csr),
- Request.Method.POST,
- principal);
- request.getAttributes().put(RequestUtils.JDISC_REQUEST_X509CERT, new X509Certificate[]{cert});
- assertResponse(
- 400,
- "{\"error-code\":\"BAD_REQUEST\",\"message\":\"POST http://localhost:12345/ca/v1/instance/vespa.external.provider_prod_us-north-1/vespa.external/tenant/foobar failed: Mismatch between instance ID in URL path and instance ID in CSR [instanceId=foobar,instanceIdFromCsr=1.cluster1.default.app1.tenant1.us-north-1.prod.node]\"}",
- request);
-
- // POST instance refresh using zts client where client cert does not contain instanceid
- var certificate = registerInstance();
- var ztsClient = new TestZtsClient(principal, certificate, URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
- try {
- var invalidCsr = CertificateTester.createCsr(principal.getIdentity().getFullName(), List.of("node1.example.com", INVALID_INSTANCE_ID_WITH_SUFFIX));
- var instanceIdentity = ztsClient.refreshInstance(new AthenzService("vespa.external", "provider_prod_us-north-1"),
- new AthenzService(CONTAINER_IDENTITY),
- INSTANCE_ID,
- invalidCsr);
- fail("Refresh instance should have failed");
- } catch (Exception e) {
- String expectedMessage = "Received error from ZTS: code=0, message=\"POST http://localhost:12345/ca/v1/instance/vespa.external.provider_prod_us-north-1/vespa.external/tenant/1.cluster1.default.app1.tenant1.us-north-1.prod.node failed: Mismatch between instance ID in URL path and instance ID in CSR [instanceId=1.cluster1.default.app1.tenant1.us-north-1.prod.node,instanceIdFromCsr=1.cluster1.default.otherapp.othertenant.us-north-1.prod.node]\"";
- assertEquals(expectedMessage, e.getMessage());
- }
- }
-
- private void setCaCertificateAndKey() {
- var keyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 256);
- var caCertificatePem = X509CertificateUtils.toPem(CertificateTester.createCertificate("Vespa CA", keyPair));
- var privateKeyPem = KeyUtils.toPem(keyPair.getPrivate());
- secretStore().setSecret("vespa.external.ca.cert", caCertificatePem)
- .setSecret("secretname", privateKeyPem);
- }
-
- private void assertIdentityResponse(Request request) {
- assertResponse(200, (body) -> {
- var slime = SlimeUtils.jsonToSlime(body);
- var root = slime.get();
- assertEquals("vespa.external.provider_prod_us-north-1", root.field("provider").asString());
- assertEquals("tenant", root.field("service").asString());
- assertEquals(INSTANCE_ID, root.field("instanceId").asString());
- var pemEncodedCertificate = root.field("x509Certificate").asString();
- assertTrue(pemEncodedCertificate.startsWith("-----BEGIN CERTIFICATE-----") &&
- pemEncodedCertificate.endsWith("-----END CERTIFICATE-----\n"),
- "Response contains PEM certificate");
- }, request);
- }
-
- private static byte[] instanceRefreshJson(Pkcs10Csr csr) {
- var csrPem = Pkcs10CsrUtils.toPem(csr);
- var json = "{\"csr\": \"" + csrPem + "\"}";
- return json.getBytes(StandardCharsets.UTF_8);
- }
-
- private static byte[] instanceRegistrationJson(Pkcs10Csr csr) {
- var csrPem = Pkcs10CsrUtils.toPem(csr);
- var json = "{\n" +
- " \"provider\": \"vespa.external.provider_prod_us-north-1\",\n" +
- " \"domain\": \"vespa.external\",\n" +
- " \"service\": \"tenant\",\n" +
- " \"attestationData\": \""+getAttestationData()+"\",\n" +
- " \"csr\": \"" + csrPem + "\"\n" +
- "}";
- return json.getBytes(StandardCharsets.UTF_8);
- }
-
- private static String getAttestationData () {
- var json = "{\n" +
- " \"signature\": \"SIGNATURE\",\n" +
- " \"signing-key-version\": 0,\n" +
- " \"provider-unique-id\": \"0.default.default.application.tenant.us-north-1.dev.tenant\",\n" +
- " \"provider-service\": \"domain.service\",\n" +
- " \"document-version\": 1,\n" +
- " \"configserver-hostname\": \"localhost\",\n" +
- " \"instance-hostname\": \"docker-container\",\n" +
- " \"created-at\": 1572000079.00000,\n" +
- " \"ip-addresses\": [\n" +
- " \"::1\"\n" +
- " ],\n" +
- " \"identity-type\": \"tenant\"\n" +
- "}";
- return StringUtilities.escape(json);
- }
-
- /*
- Zts client that adds principal as header (since setting up ssl in test is cumbersome)
- */
- private static class TestZtsClient extends DefaultZtsClient {
-
- private final Principal principal;
- private final X509Certificate certificate;
-
- public TestZtsClient(Principal principal, X509Certificate certificate, URI ztsUrl, SSLContext sslContext) {
- super(ztsUrl, () -> sslContext, null, ErrorHandler.empty());
- this.principal = principal;
- this.certificate = certificate;
- }
-
- @Override
- protected <T> T execute(HttpUriRequest request, ResponseHandler<T> responseHandler) {
- request.addHeader("PRINCIPAL", principal.getName());
- Optional.ofNullable(certificate).ifPresent(cert -> {
- var pem = X509CertificateUtils.toPem(certificate);
- request.addHeader("CERTIFICATE", StringUtilities.escape(pem));
- });
- return super.execute(request, responseHandler);
- }
- }
-}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java
deleted file mode 100644
index d880fd5220b..00000000000
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/ContainerTester.java
+++ /dev/null
@@ -1,90 +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.application.Networking;
-import com.yahoo.application.container.JDisc;
-import com.yahoo.application.container.handler.Request;
-import com.yahoo.vespa.hosted.ca.restapi.mock.SecretStoreMock;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-
-import java.io.UncheckedIOException;
-import java.nio.charset.CharacterCodingException;
-import java.util.function.Consumer;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * The superclass of REST API tests which require a functional container instance.
- *
- * @author mpolden
- */
-public class ContainerTester {
-
- private JDisc container;
-
- @BeforeEach
- public void startContainer() {
- container = JDisc.fromServicesXml(servicesXml(), Networking.enable);
- }
-
- @AfterEach
- public void stopContainer() {
- container.close();
- }
-
- public SecretStoreMock secretStore() {
- return (SecretStoreMock) container.components().getComponent(SecretStoreMock.class.getName());
- }
-
- public void assertResponse(int expectedStatus, String expectedBody, Request request) {
- assertResponse(expectedStatus, (body) -> assertEquals(expectedBody, body), request);
- }
-
- public void assertResponse(int expectedStatus, Consumer<String> bodyAsserter, Request request) {
- var response = container.handleRequest(request);
- try {
- bodyAsserter.accept(response.getBodyAsString());
- } catch (CharacterCodingException e) {
- throw new UncheckedIOException(e);
- }
- assertEquals(expectedStatus, response.getStatus());
- assertEquals("application/json; charset=UTF-8", response.getHeaders().getFirst("Content-Type"));
- }
-
- private static String servicesXml() {
- return "<container version='1.0'>\n" +
- " <accesslog type=\"disabled\"/>\n" +
- " <config name=\"container.handler.threadpool\">\n" +
- " <maxthreads>10</maxthreads>\n" +
- " </config>\n" +
- " <config name='vespa.hosted.athenz.instanceproviderservice.config.athenz-provider-service'>\n" +
- " <athenzCaTrustStore>/path/to/file</athenzCaTrustStore>\n" +
- " <domain>vespa.external</domain>\n" +
- " <serviceName>servicename</serviceName>\n" +
- " <secretName>secretname</secretName>\n" +
- " <secretVersion>0</secretVersion>\n" +
- " <sisSecretName>secretname</sisSecretName>\n" +
- " <sisSecretVersion>0</sisSecretVersion>\n" +
- " <caCertSecretName>vespa.external.ca.cert</caCertSecretName>\n" +
- " <certDnsSuffix>suffix</certDnsSuffix>\n" +
- " <ztsUrl>https://localhost:123/</ztsUrl>\n" +
- " </config>\n" +
- " <component id='com.yahoo.vespa.hosted.ca.restapi.mock.SecretStoreMock'/>\n" +
- " <component id='com.yahoo.vespa.hosted.ca.restapi.mock.InstanceValidatorMock'/>\n" +
- " <handler id='com.yahoo.vespa.hosted.ca.restapi.CertificateAuthorityApiHandler'>\n" +
- " <binding>http://*/ca/v1/*</binding>\n" +
- " </handler>\n" +
- " <http>\n" +
- " <server id='default' port='12345'/>\n" +
- " <filtering>\n" +
- " <request-chain id=\"my-default-chain\">\n" +
- " <filter id='com.yahoo.vespa.hosted.ca.restapi.mock.PrincipalFromHeaderFilter' />\n" +
- " <binding>http://*/*</binding>\n" +
- " </request-chain>\n" +
- " </filtering>\n" +
- " </http>\n" +
- "</container>";
- }
-
-} \ No newline at end of file
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java
deleted file mode 100644
index 02398b19627..00000000000
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/InstanceSerializerTest.java
+++ /dev/null
@@ -1,101 +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.config.provision.Cloud;
-import com.yahoo.security.Pkcs10CsrUtils;
-import com.yahoo.security.X509CertificateUtils;
-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.EntityBindingsMapper;
-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.CertificateTester;
-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 org.junit.jupiter.api.Test;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.nio.charset.StandardCharsets;
-import java.time.Instant;
-import java.time.temporal.ChronoUnit;
-import java.util.Collections;
-import java.util.Optional;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * @author mpolden
- */
-public class InstanceSerializerTest {
-
- @Test
- void deserialize_instance_registration() {
- var csr = CertificateTester.createCsr();
- var csrPem = Pkcs10CsrUtils.toPem(csr);
- SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument(
- "signature",
- 0,
- new VespaUniqueInstanceId(0, "cluster", "instance", "application", "tenant", "region", "prod", IdentityType.NODE),
- new AthenzService("domain", "service"),
- 0,
- "configserverhostname",
- "instancehostname",
- Instant.now().truncatedTo(ChronoUnit.MICROS), // Truncate to the precision given from EntityBindingsMapper.toAttestationData()
- Collections.emptySet(),
- IdentityType.NODE,
- ClusterType.CONTAINER,
- "https://zts.url");
-
- var json = String.format("{\n" +
- " \"provider\": \"provider_prod_us-north-1\",\n" +
- " \"domain\": \"vespa.external\",\n" +
- " \"service\": \"tenant\",\n" +
- " \"attestationData\":\"%s\",\n" +
- " \"csr\": \"" + csrPem + "\"\n" +
- "}", StringUtilities.escape(EntityBindingsMapper.toAttestationData(signedIdentityDocument)));
- var instanceRegistration = new InstanceRegistration("provider_prod_us-north-1", "vespa.external",
- "tenant", signedIdentityDocument,
- csr);
- var deserialized = InstanceSerializer.registrationFromSlime(SlimeUtils.jsonToSlime(json));
- assertEquals(instanceRegistration, deserialized);
- }
-
- @Test
- void serialize_instance_identity() {
- var certificate = CertificateTester.createCertificate();
- var pem = X509CertificateUtils.toPem(certificate);
- var identity = new InstanceIdentity("provider_prod_us-north-1", "tenant", "node1.example.com",
- Optional.of(certificate));
- var json = "{" +
- "\"provider\":\"provider_prod_us-north-1\"," +
- "\"service\":\"tenant\"," +
- "\"instanceId\":\"node1.example.com\"," +
- "\"x509Certificate\":\"" + pem.replace("\n", "\\n") + "\"" +
- "}";
- assertEquals(json, asJsonString(InstanceSerializer.identityToSlime(identity)));
- }
-
- @Test
- void serialize_instance_refresh() {
- var csr = CertificateTester.createCsr();
- var csrPem = Pkcs10CsrUtils.toPem(csr);
- var json = "{\"csr\": \"" + csrPem + "\"}";
- var instanceRefresh = new InstanceRefresh(csr);
- var deserialized = InstanceSerializer.refreshFromSlime(SlimeUtils.jsonToSlime(json));
- assertEquals(instanceRefresh, deserialized);
- }
-
- private static String asJsonString(Slime slime) {
- try {
- return new String(SlimeUtils.toJsonBytes(slime), StandardCharsets.UTF_8);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/InstanceValidatorMock.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/InstanceValidatorMock.java
deleted file mode 100644
index 163db3e107e..00000000000
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/InstanceValidatorMock.java
+++ /dev/null
@@ -1,27 +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.mock;
-
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.InstanceConfirmation;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.InstanceValidator;
-
-/**
- * @author mortent
- */
-public class InstanceValidatorMock extends InstanceValidator {
-
- public InstanceValidatorMock() {
- super(null, null, null, null, null, null,"");
- }
-
- @Override
- public boolean isValidInstance(InstanceConfirmation instanceConfirmation) {
- return instanceConfirmation.attributes.get(SAN_DNS_ATTRNAME) != null &&
- instanceConfirmation.attributes.get(SAN_IPS_ATTRNAME) != null;
- }
-
- @Override
- public boolean isValidRefresh(InstanceConfirmation confirmation) {
- return confirmation.attributes.get(SAN_DNS_ATTRNAME) != null &&
- confirmation.attributes.get(SAN_IPS_ATTRNAME) != null;
- }
-}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/PrincipalFromHeaderFilter.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/PrincipalFromHeaderFilter.java
deleted file mode 100644
index df98ba75dd2..00000000000
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/PrincipalFromHeaderFilter.java
+++ /dev/null
@@ -1,34 +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.mock;
-
-import com.yahoo.jdisc.handler.ResponseHandler;
-import com.yahoo.jdisc.http.filter.DiscFilterRequest;
-import com.yahoo.jdisc.http.filter.SecurityRequestFilter;
-import com.yahoo.jdisc.http.server.jetty.RequestUtils;
-import com.yahoo.security.X509CertificateUtils;
-import com.yahoo.text.StringUtilities;
-import com.yahoo.vespa.athenz.api.AthenzPrincipal;
-import com.yahoo.vespa.athenz.api.AthenzService;
-
-import java.security.cert.X509Certificate;
-import java.util.Optional;
-
-/**
- * Read principal from http header
- *
- * @author mortent
- */
-public class PrincipalFromHeaderFilter implements SecurityRequestFilter {
-
- @Override
- public void filter(DiscFilterRequest request, ResponseHandler handler) {
- String principal = request.getHeader("PRINCIPAL");
- request.setUserPrincipal(new AthenzPrincipal(new AthenzService(principal)));
-
- Optional<String> certificate = Optional.ofNullable(request.getHeader("CERTIFICATE"));
- certificate.ifPresent(cert -> {
- var x509cert = X509CertificateUtils.fromPem(StringUtilities.unescape(cert));
- request.setAttribute(RequestUtils.JDISC_REQUEST_X509CERT, new X509Certificate[]{x509cert});
- });
- }
-}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/SecretStoreMock.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/SecretStoreMock.java
deleted file mode 100644
index 5a9f4fd0b76..00000000000
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/mock/SecretStoreMock.java
+++ /dev/null
@@ -1,34 +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.mock;
-
-import com.yahoo.component.AbstractComponent;
-import com.yahoo.container.jdisc.secretstore.SecretStore;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @author mpolden
- */
-public class SecretStoreMock extends AbstractComponent implements SecretStore {
-
- private final Map<String, String> secrets = new HashMap<>();
-
- public SecretStoreMock setSecret(String key, String value) {
- secrets.put(key, value);
- return this;
- }
-
- @Override
- public String getSecret(String key) {
- if (!secrets.containsKey(key)) throw new RuntimeException("No such key '" + key + "'");
- return secrets.get(key);
- }
-
- @Override
- public String getSecret(String key, int version) {
- if (!secrets.containsKey(key)) throw new RuntimeException("No such key '" + key + "'");
- return secrets.get(key);
- }
-
-}
diff --git a/configserver/CMakeLists.txt b/configserver/CMakeLists.txt
index f189dc4f2c1..201d419c669 100644
--- a/configserver/CMakeLists.txt
+++ b/configserver/CMakeLists.txt
@@ -12,7 +12,6 @@ install(DIRECTORY DESTINATION conf/configserver)
install(DIRECTORY DESTINATION conf/configserver-app/components)
install(DIRECTORY DESTINATION conf/configserver-app/config-models)
-install_symlink(lib/jars/athenz-identity-provider-service-jar-with-dependencies.jar conf/configserver-app/components/athenz-identity-provider-service.jar)
install_symlink(lib/jars/config-model-fat.jar conf/configserver-app/components/config-model-fat.jar)
install_symlink(lib/jars/configserver-flags-jar-with-dependencies.jar conf/configserver-app/components/configserver-flags.jar)
install_symlink(lib/jars/flags-jar-with-dependencies.jar conf/configserver-app/components/flags.jar)
diff --git a/dist/vespa.spec b/dist/vespa.spec
index 58c2a18d3c1..0dee4e43910 100644
--- a/dist/vespa.spec
+++ b/dist/vespa.spec
@@ -586,7 +586,6 @@ fi
%{_prefix}/include
%dir %{_prefix}/lib
%dir %{_prefix}/lib/jars
-%{_prefix}/lib/jars/athenz-identity-provider-service-jar-with-dependencies.jar
%{_prefix}/lib/jars/cloud-tenant-cd-jar-with-dependencies.jar
%{_prefix}/lib/jars/clustercontroller-apps-jar-with-dependencies.jar
%{_prefix}/lib/jars/clustercontroller-core-jar-with-dependencies.jar
diff --git a/pom.xml b/pom.xml
index e1a7f8dc900..c4b6e200e2e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -27,7 +27,6 @@
<module>airlift-zstd</module>
<module>application</module>
<module>application-model</module>
- <module>athenz-identity-provider-service</module>
<module>bundle-plugin-test</module>
<module>client</module>
<module>cloud-tenant-base</module>