summaryrefslogtreecommitdiffstats
path: root/athenz-identity-provider-service/src
diff options
context:
space:
mode:
Diffstat (limited to 'athenz-identity-provider-service/src')
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderService.java207
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java117
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/KeyProvider.java (renamed from athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/KeyProvider.java)2
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSerializedPayload.java68
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSigner.java150
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSignerResource.java52
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CsrSerializedPayload.java62
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocument.java (renamed from athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/model/IdentityDocument.java)2
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java (renamed from athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentGenerator.java)25
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentResource.java48
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/ProviderUniqueId.java (renamed from athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/model/ProviderUniqueId.java)4
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/SignedIdentityDocument.java (renamed from athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/model/SignedIdentityDocument.java)4
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/AthenzCertificateClient.java3
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/CertificateClient.java14
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/FileBackedKeyProvider.java44
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentServlet.java51
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/InstanceConfirmationServlet.java65
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/SecretStoreKeyProvider.java15
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/StatusServlet.java21
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/Utils.java8
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmation.java (renamed from athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/model/InstanceConfirmation.java)3
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmationResource.java41
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java (renamed from athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/InstanceValidator.java)12
-rw-r--r--athenz-identity-provider-service/src/main/resources/configdefinitions/athenz-provider-service.def6
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderServiceTest.java270
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AutoGeneratedKeyProvider.java40
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/TestUtils.java31
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSignerTest.java134
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CsrSerializedPayloadTest.java32
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java (renamed from athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentGeneratorTest.java)30
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java (renamed from athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/InstanceValidatorTest.java)19
31 files changed, 844 insertions, 736 deletions
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderService.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderService.java
deleted file mode 100644
index 26a88896fb9..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderService.java
+++ /dev/null
@@ -1,207 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice;
-
-import com.google.inject.Inject;
-import com.yahoo.component.AbstractComponent;
-import com.yahoo.config.model.api.SuperModelProvider;
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.config.provision.Zone;
-import com.yahoo.jdisc.http.SecretStore;
-import com.yahoo.log.LogLevel;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.AthenzCertificateClient;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.CertificateClient;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.IdentityDocumentGenerator;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.IdentityDocumentServlet;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.InstanceConfirmationServlet;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.InstanceValidator;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.KeyProvider;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.SecretStoreKeyProvider;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.StatusServlet;
-import com.yahoo.vespa.hosted.provision.NodeRepository;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.servlet.ServletHandler;
-import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-
-import java.security.KeyStore;
-import java.security.PrivateKey;
-import java.security.cert.Certificate;
-import java.security.cert.X509Certificate;
-import java.time.Duration;
-import java.time.temporal.TemporalAmount;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Logger;
-
-/**
- * A component acting as both SIA for configserver and provides a lightweight Jetty instance hosting the InstanceConfirmation API
- *
- * @author bjorncs
- */
-public class AthenzInstanceProviderService extends AbstractComponent {
-
- private static final Logger log = Logger.getLogger(AthenzInstanceProviderService.class.getName());
-
- private final ScheduledExecutorService scheduler;
- private final Server jetty;
-
- @Inject
- public AthenzInstanceProviderService(AthenzProviderServiceConfig config, SuperModelProvider superModelProvider,
- NodeRepository nodeRepository, Zone zone, SecretStore secretStore) {
- this(config, new SecretStoreKeyProvider(secretStore, getZoneConfig(config, zone).secretName()), Executors.newSingleThreadScheduledExecutor(),
- superModelProvider, nodeRepository, zone, new AthenzCertificateClient(config, getZoneConfig(config, zone)), createSslContextFactory());
- }
-
- private AthenzInstanceProviderService(AthenzProviderServiceConfig config,
- KeyProvider keyProvider,
- ScheduledExecutorService scheduler,
- SuperModelProvider superModelProvider,
- NodeRepository nodeRepository,
- Zone zone,
- CertificateClient certificateClient,
- SslContextFactory sslContextFactory) {
- this(config, scheduler, zone, sslContextFactory,
- new InstanceValidator(keyProvider, superModelProvider),
- new IdentityDocumentGenerator(config, getZoneConfig(config, zone), nodeRepository, zone, keyProvider),
- new AthenzCertificateUpdater(
- certificateClient, sslContextFactory, keyProvider, config, getZoneConfig(config, zone)));
- }
-
- AthenzInstanceProviderService(AthenzProviderServiceConfig config,
- ScheduledExecutorService scheduler,
- Zone zone,
- SslContextFactory sslContextFactory,
- InstanceValidator instanceValidator,
- IdentityDocumentGenerator identityDocumentGenerator,
- AthenzCertificateUpdater reloader) {
- // TODO: Enable for all systems. Currently enabled for CD system only
- if (SystemName.cd.equals(zone.system())) {
- this.scheduler = scheduler;
- this.jetty = createJettyServer(config, sslContextFactory, instanceValidator, identityDocumentGenerator);
-
- // TODO Configurable update frequency
- scheduler.scheduleAtFixedRate(reloader, 0, 1, TimeUnit.DAYS);
- try {
- jetty.start();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- } else {
- this.scheduler = null;
- this.jetty = null;
- }
- }
-
- private static Server createJettyServer(AthenzProviderServiceConfig config,
- SslContextFactory sslContextFactory,
- InstanceValidator instanceValidator,
- IdentityDocumentGenerator identityDocumentGenerator) {
- Server server = new Server();
- ServerConnector connector = new ServerConnector(server, sslContextFactory);
- connector.setPort(config.port());
- server.addConnector(connector);
-
- ServletHandler handler = new ServletHandler();
- InstanceConfirmationServlet instanceConfirmationServlet = new InstanceConfirmationServlet(instanceValidator);
- handler.addServletWithMapping(new ServletHolder(instanceConfirmationServlet), config.apiPath() + "/instance");
-
- IdentityDocumentServlet identityDocumentServlet = new IdentityDocumentServlet(identityDocumentGenerator);
- handler.addServletWithMapping(new ServletHolder(identityDocumentServlet), config.apiPath() + "/identity-document");
-
- handler.addServletWithMapping(StatusServlet.class, "/status.html");
- server.setHandler(handler);
- return server;
-
- }
-
- private static AthenzProviderServiceConfig.Zones getZoneConfig(AthenzProviderServiceConfig config, Zone zone) {
- String key = zone.environment().value() + "." + zone.region().value();
- return config.zones(key);
- }
-
- static SslContextFactory createSslContextFactory() {
- try {
- SslContextFactory sslContextFactory = new SslContextFactory();
- sslContextFactory.setWantClientAuth(true);
- sslContextFactory.setProtocol("TLS");
- sslContextFactory.setKeyManagerFactoryAlgorithm("SunX509");
- return sslContextFactory;
- } catch (Exception e) {
- throw new IllegalArgumentException("Failed to create SSL context factory: " + e.getMessage(), e);
- }
- }
-
- static class AthenzCertificateUpdater implements Runnable {
-
- // TODO Make expiry a configuration parameter
- private static final TemporalAmount EXPIRY_TIME = Duration.ofDays(30);
- private static final Logger log = Logger.getLogger(AthenzCertificateUpdater.class.getName());
-
- private final CertificateClient certificateClient;
- private final SslContextFactory sslContextFactory;
- private final KeyProvider keyProvider;
- private final AthenzProviderServiceConfig config;
- private final AthenzProviderServiceConfig.Zones zoneConfig;
-
- AthenzCertificateUpdater(CertificateClient certificateClient,
- SslContextFactory sslContextFactory,
- KeyProvider keyProvider,
- AthenzProviderServiceConfig config,
- AthenzProviderServiceConfig.Zones zoneConfig) {
- this.certificateClient = certificateClient;
- this.sslContextFactory = sslContextFactory;
- this.keyProvider = keyProvider;
- this.config = config;
- this.zoneConfig = zoneConfig;
- }
-
- @Override
- public void run() {
- try {
- log.log(LogLevel.INFO, "Updating Athenz certificate through ZTS");
- PrivateKey privateKey = keyProvider.getPrivateKey(zoneConfig.secretVersion());
- X509Certificate certificate = certificateClient.updateCertificate(privateKey, EXPIRY_TIME);
-
- String dummyPassword = "athenz";
- KeyStore keyStore = KeyStore.getInstance("JKS");
- keyStore.load(null);
- keyStore.setKeyEntry("athenz",
- privateKey,
- dummyPassword.toCharArray(),
- new Certificate[]{certificate});
-
- sslContextFactory.reload(sslContextFactory -> {
- sslContextFactory.setKeyStore(keyStore);
- sslContextFactory.setKeyStorePassword(dummyPassword);
- });
- log.log(LogLevel.INFO, "Athenz certificate reload successfully completed");
- } catch (Throwable e) {
- log.log(LogLevel.ERROR, "Failed to update certificate from ZTS: " + e.getMessage(), e);
- }
- }
- }
-
- @Override
- public void deconstruct() {
- try {
- // TODO: Fix deconstruct when setup properly in all zones
- log.log(LogLevel.INFO, "Deconstructing Athenz provider service");
- if(scheduler != null)
- scheduler.shutdown();
- if(jetty != null)
- jetty.stop();
- if (scheduler != null && !scheduler.awaitTermination(1, TimeUnit.MINUTES)) {
- log.log(LogLevel.ERROR, "Failed to stop certificate updater");
- }
- } catch (InterruptedException e) {
- log.log(LogLevel.ERROR, "Failed to stop certificate updater: " + e.getMessage(), e);
- } catch (Exception e) {
- log.log(LogLevel.ERROR, "Failed to stop Jetty: " + e.getMessage(), e);
- } finally {
- super.deconstruct();
- }
- }
-}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java
new file mode 100644
index 00000000000..7910650ed5e
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java
@@ -0,0 +1,117 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice;
+
+import com.google.inject.Inject;
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.config.provision.Zone;
+import com.yahoo.jdisc.http.ssl.SslKeyStoreConfigurator;
+import com.yahoo.jdisc.http.ssl.SslKeyStoreContext;
+import com.yahoo.log.LogLevel;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.AthenzCertificateClient;
+
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+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.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Logger;
+
+import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils.getZoneConfig;
+
+/**
+ * @author bjorncs
+ */
+// TODO Cache certificate on disk
+@SuppressWarnings("unused") // Component injected into Jetty connector factory
+public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements SslKeyStoreConfigurator {
+ private static final Logger log = Logger.getLogger(AthenzSslKeyStoreConfigurator.class.getName());
+ // TODO Make expiry and update frequency configurable parameters
+ private static final Duration CERTIFICATE_EXPIRY_TIME = Duration.ofDays(30);
+ private static final Duration CERTIFICATE_UPDATE_PERIOD = Duration.ofDays(7);
+
+ private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
+ private final AthenzCertificateClient certificateClient;
+ private final KeyProvider keyProvider;
+ private final AthenzProviderServiceConfig.Zones zoneConfig;
+ private final AtomicBoolean alreadyConfigured = new AtomicBoolean();
+ private final Zone zone;
+
+ @Inject
+ public AthenzSslKeyStoreConfigurator(KeyProvider keyProvider,
+ AthenzProviderServiceConfig config,
+ Zone zone) {
+ AthenzProviderServiceConfig.Zones zoneConfig = getZoneConfig(config, zone);
+ this.certificateClient = new AthenzCertificateClient(config, zoneConfig);
+ this.keyProvider = keyProvider;
+ this.zoneConfig = zoneConfig;
+ this.zone = zone;
+ }
+
+ @Override
+ public void configure(SslKeyStoreContext sslKeyStoreContext) {
+ // TODO Remove this when main is ready
+ if (zone.system() != SystemName.cd) {
+ return;
+ }
+ if (alreadyConfigured.getAndSet(true)) { // For debugging purpose of SslKeyStoreConfigurator interface
+ throw new IllegalStateException("Already configured. configure() can only be called once.");
+ }
+ AthenzCertificateUpdater updater = new AthenzCertificateUpdater(sslKeyStoreContext);
+ scheduler.scheduleAtFixedRate(updater, /*initialDelay*/0, CERTIFICATE_UPDATE_PERIOD.toMinutes(), TimeUnit.MINUTES);
+ }
+
+ @Override
+ public void deconstruct() {
+ try {
+ scheduler.shutdownNow();
+ scheduler.awaitTermination(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Failed to shutdown Athenz certificate updater on time", e);
+ }
+ }
+
+ private class AthenzCertificateUpdater implements Runnable {
+
+ private final SslKeyStoreContext sslKeyStoreContext;
+
+ AthenzCertificateUpdater(SslKeyStoreContext sslKeyStoreContext) {
+ this.sslKeyStoreContext = sslKeyStoreContext;
+ }
+
+ @Override
+ public void run() {
+ try {
+ log.log(LogLevel.INFO, "Updating Athenz certificate from ZTS");
+ PrivateKey privateKey = keyProvider.getPrivateKey(zoneConfig.secretVersion());
+ X509Certificate certificate = certificateClient.updateCertificate(privateKey, CERTIFICATE_EXPIRY_TIME);
+ verifyActualExperiy(certificate);
+
+ String dummyPassword = "athenz";
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null);
+ keyStore.setKeyEntry("athenz", privateKey, dummyPassword.toCharArray(), new Certificate[]{certificate});
+ sslKeyStoreContext.updateKeyStore(keyStore, dummyPassword);
+ log.log(LogLevel.INFO, "Athenz certificate reload successfully completed");
+ } catch (Throwable e) {
+ log.log(LogLevel.ERROR, "Failed to update certificate from ZTS: " + e.getMessage(), e);
+ }
+ }
+
+ private void verifyActualExperiy(X509Certificate certificate) {
+ Instant notAfter = certificate.getNotAfter().toInstant();
+ Instant notBefore = certificate.getNotBefore().toInstant();
+ if (!notBefore.plus(CERTIFICATE_EXPIRY_TIME).equals(notAfter)) {
+ Duration actualExpiry = Duration.between(notBefore, notAfter);
+ log.log(LogLevel.WARNING,
+ String.format("Expected expiry %s, got %s", CERTIFICATE_EXPIRY_TIME, actualExpiry));
+ }
+ }
+ }
+}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/KeyProvider.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/KeyProvider.java
index 5a1d7e3c1ff..a72a2fcbc6c 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/KeyProvider.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/KeyProvider.java
@@ -1,5 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl;
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice;
import java.security.PrivateKey;
import java.security.PublicKey;
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSerializedPayload.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSerializedPayload.java
new file mode 100644
index 00000000000..25733bf0075
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSerializedPayload.java
@@ -0,0 +1,68 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.ca;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
+import org.bouncycastle.util.io.pem.PemObject;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+/**
+ * Contains PEM formatted signed certificate
+ *
+ * @author freva
+ */
+public class CertificateSerializedPayload {
+
+ @JsonProperty("certificate") @JsonSerialize(using = CertificateSerializer.class)
+ public final X509Certificate certificate;
+
+ @JsonCreator
+ public CertificateSerializedPayload(@JsonProperty("certificate") X509Certificate certificate) {
+ this.certificate = certificate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ CertificateSerializedPayload that = (CertificateSerializedPayload) o;
+
+ return certificate.equals(that.certificate);
+ }
+
+ @Override
+ public int hashCode() {
+ return certificate.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "CertificateSerializedPayload{" +
+ "certificate='" + certificate + '\'' +
+ '}';
+ }
+
+ public static class CertificateSerializer extends JsonSerializer<X509Certificate> {
+ @Override
+ public void serialize(
+ X509Certificate certificate, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+ try (StringWriter stringWriter = new StringWriter(); JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) {
+ pemWriter.writeObject(new PemObject("CERTIFICATE", certificate.getEncoded()));
+ pemWriter.flush();
+ gen.writeString(stringWriter.toString());
+ } catch (CertificateEncodingException e) {
+ throw new RuntimeException("Failed to encode X509Certificate", e);
+ }
+ }
+ }
+}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSigner.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSigner.java
new file mode 100644
index 00000000000..2dc3f24664c
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSigner.java
@@ -0,0 +1,150 @@
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.ca;
+
+import com.google.common.collect.ImmutableList;
+import com.google.inject.Inject;
+import com.yahoo.config.provision.Zone;
+import com.yahoo.log.LogLevel;
+import com.yahoo.net.HostName;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.KeyProvider;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERUTF8String;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x500.style.BCStyle;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest;
+
+import java.math.BigInteger;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.time.Clock;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils.getZoneConfig;
+
+
+/**
+ * Signs Certificate Signing Reqest from tenant nodes. This certificate will be used
+ * by nodes to authenticate themselves when performing operations against the config
+ * server, such as updating node-repository or orchestrator.
+ *
+ * @author freva
+ */
+public class CertificateSigner {
+
+ private static final Logger log = Logger.getLogger(CertificateSigner.class.getName());
+
+ static final String SIGNER_ALGORITHM = "SHA256withRSA";
+ static final Duration CERTIFICATE_EXPIRATION = Duration.ofDays(30);
+ private static final List<ASN1ObjectIdentifier> ILLEGAL_EXTENSIONS = ImmutableList.of(
+ Extension.basicConstraints, Extension.subjectAlternativeName);
+
+ private final JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter();
+ private final Provider provider = new BouncyCastleProvider();
+
+ private final PrivateKey caPrivateKey;
+ private final X500Name issuer;
+ private final Clock clock;
+
+ @Inject
+ public CertificateSigner(KeyProvider keyProvider, AthenzProviderServiceConfig config, Zone zone) {
+ this(getPrivateKey(keyProvider, config, zone), HostName.getLocalhost(), Clock.systemUTC());
+ }
+
+ CertificateSigner(PrivateKey caPrivateKey, String configServerHostname, Clock clock) {
+ this.caPrivateKey = caPrivateKey;
+ this.issuer = new X500Name("CN=" + configServerHostname);
+ this.clock = clock;
+ }
+
+ /**
+ * Signs the CSR if:
+ * <ul>
+ * <li>Common Name matches {@code remoteHostname}</li>
+ * <li>CSR does not contain any any of the extensions in {@code ILLEGAL_EXTENSIONS}</li>
+ * </ul>
+ */
+ X509Certificate generateX509Certificate(PKCS10CertificationRequest certReq, String remoteHostname) {
+ verifyCertificateCommonName(certReq.getSubject(), remoteHostname);
+ verifyCertificateExtensions(certReq);
+
+ Date notBefore = Date.from(clock.instant());
+ Date notAfter = Date.from(clock.instant().plus(CERTIFICATE_EXPIRATION));
+
+ try {
+ PublicKey publicKey = new JcaPKCS10CertificationRequest(certReq).getPublicKey();
+ X509v3CertificateBuilder caBuilder = new JcaX509v3CertificateBuilder(
+ issuer, BigInteger.valueOf(clock.millis()), notBefore, notAfter, certReq.getSubject(), publicKey)
+
+ // Set Basic Constraints to false
+ .addExtension(Extension.basicConstraints, true, new BasicConstraints(false));
+
+ ContentSigner caSigner = new JcaContentSignerBuilder(SIGNER_ALGORITHM).build(caPrivateKey);
+
+ return certificateConverter
+ .setProvider(provider)
+ .getCertificate(caBuilder.build(caSigner));
+ } catch (Exception ex) {
+ log.log(LogLevel.ERROR, "Failed to generate X509 Certificate", ex);
+ throw new RuntimeException("Failed to generate X509 Certificate");
+ }
+ }
+
+ static void verifyCertificateCommonName(X500Name subject, String commonName) {
+ List<AttributeTypeAndValue> attributesAndValues = Arrays.stream(subject.getRDNs())
+ .flatMap(rdn -> rdn.isMultiValued() ?
+ Stream.of(rdn.getTypesAndValues()) : Stream.of(rdn.getFirst()))
+ .filter(attr -> attr.getType() == BCStyle.CN)
+ .collect(Collectors.toList());
+
+ if (attributesAndValues.size() != 1) {
+ throw new IllegalArgumentException("Only 1 common name should be set");
+ }
+
+ String actualCommonName = DERUTF8String.getInstance(attributesAndValues.get(0).getValue()).getString();
+ if (! actualCommonName.equals(commonName)) {
+ throw new IllegalArgumentException("Expected common name to be " + commonName + ", but was " + actualCommonName);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ static void verifyCertificateExtensions(PKCS10CertificationRequest request) {
+ List<String> illegalExt = Arrays
+ .stream(request.getAttributes(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest))
+ .map(attribute -> Extensions.getInstance(attribute.getAttrValues().getObjectAt(0)))
+ .flatMap(ext -> Collections.list((Enumeration<ASN1ObjectIdentifier>) ext.oids()).stream())
+ .filter(ILLEGAL_EXTENSIONS::contains)
+ .map(ASN1ObjectIdentifier::getId)
+ .collect(Collectors.toList());
+
+ if (! illegalExt.isEmpty()) {
+ throw new IllegalArgumentException("CSR contains illegal extensions: " + String.join(", ", illegalExt));
+ }
+ }
+
+ private static PrivateKey getPrivateKey(KeyProvider keyProvider, AthenzProviderServiceConfig config, Zone zone) {
+ AthenzProviderServiceConfig.Zones zoneConfig = getZoneConfig(config, zone);
+ return keyProvider.getPrivateKey(zoneConfig.secretVersion());
+ }
+}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSignerResource.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSignerResource.java
new file mode 100644
index 00000000000..417acf0e9b5
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSignerResource.java
@@ -0,0 +1,52 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.ca;
+
+import com.google.inject.Inject;
+import com.yahoo.container.jaxrs.annotation.Component;
+import com.yahoo.log.LogLevel;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import java.security.cert.X509Certificate;
+import java.util.logging.Logger;
+
+/**
+ * @author bjorncs
+ * @author freva
+ */
+@Path("/sign")
+public class CertificateSignerResource {
+
+ private static final Logger log = Logger.getLogger(CertificateSignerResource.class.getName());
+
+ private final CertificateSigner certificateSigner;
+
+ @Inject
+ public CertificateSignerResource(@Component CertificateSigner certificateSigner) {
+ this.certificateSigner = certificateSigner;
+ }
+
+ @POST
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public CertificateSerializedPayload generateCertificate(CsrSerializedPayload csrPayload,
+ @Context HttpServletRequest req) {
+ try {
+ String remoteHostname = req.getRemoteHost();
+ PKCS10CertificationRequest csr = csrPayload.csr;
+ log.log(LogLevel.DEBUG, "Certification request from " + remoteHostname + ": " + csr);
+ X509Certificate certificate = certificateSigner.generateX509Certificate(csr, remoteHostname);
+ return new CertificateSerializedPayload(certificate);
+ } catch (RuntimeException e) {
+ log.log(LogLevel.ERROR, e.getMessage(), e);
+ throw new InternalServerErrorException(e.getMessage(), e);
+ }
+ }
+}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CsrSerializedPayload.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CsrSerializedPayload.java
new file mode 100644
index 00000000000..f56214513aa
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CsrSerializedPayload.java
@@ -0,0 +1,62 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.ca;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+/**
+ * Contains PEM formatted Certificate Signing Request (CSR)
+ *
+ * @author freva
+ */
+public class CsrSerializedPayload {
+
+ @JsonProperty("csr") public final PKCS10CertificationRequest csr;
+
+ @JsonCreator
+ public CsrSerializedPayload(@JsonProperty("csr") @JsonDeserialize(using = CertificateRequestDeserializer.class)
+ PKCS10CertificationRequest csr) {
+ this.csr = csr;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ CsrSerializedPayload that = (CsrSerializedPayload) o;
+
+ return csr.equals(that.csr);
+ }
+
+ @Override
+ public int hashCode() {
+ return csr.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "CsrSerializedPayload{" +
+ "csr='" + csr + '\'' +
+ '}';
+ }
+
+ public static class CertificateRequestDeserializer extends JsonDeserializer<PKCS10CertificationRequest> {
+ @Override
+ public PKCS10CertificationRequest deserialize(
+ JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
+ try (PEMParser pemParser = new PEMParser(new StringReader(jsonParser.getValueAsString()))) {
+ return (PKCS10CertificationRequest) pemParser.readObject();
+ }
+ }
+ }
+}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/model/IdentityDocument.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocument.java
index 41ce5d969a7..bae8f6f03b6 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/model/IdentityDocument.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocument.java
@@ -1,5 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model;
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument;
import com.fasterxml.jackson.annotation.JsonProperty;
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentGenerator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java
index 55acf0b796c..4dd6881c07e 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentGenerator.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java
@@ -1,11 +1,11 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl;
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument;
+import com.google.inject.Inject;
import com.yahoo.config.provision.Zone;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.KeyProvider;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.IdentityDocument;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.ProviderUniqueId;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.SignedIdentityDocument;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.Allocation;
@@ -29,8 +29,12 @@ public class IdentityDocumentGenerator {
private final String providerDomain;
private final int signingSecretVersion;
- public IdentityDocumentGenerator(AthenzProviderServiceConfig config, AthenzProviderServiceConfig.Zones zoneConfig,
- NodeRepository nodeRepository, Zone zone, KeyProvider keyProvider) {
+ @Inject
+ public IdentityDocumentGenerator(AthenzProviderServiceConfig config,
+ NodeRepository nodeRepository,
+ Zone zone,
+ KeyProvider keyProvider) {
+ AthenzProviderServiceConfig.Zones zoneConfig = Utils.getZoneConfig(config, zone);
this.nodeRepository = nodeRepository;
this.zone = zone;
this.keyProvider = keyProvider;
@@ -41,7 +45,7 @@ public class IdentityDocumentGenerator {
this.signingSecretVersion = zoneConfig.secretVersion();
}
- public String generateSignedIdentityDocument(String hostname) {
+ public SignedIdentityDocument generateSignedIdentityDocument(String hostname) {
Node node = nodeRepository.getNode(hostname).orElseThrow(() -> new RuntimeException("Unable to find node " + hostname));
try {
IdentityDocument identityDocument = generateIdDocument(node);
@@ -51,13 +55,12 @@ public class IdentityDocumentGenerator {
Base64.getEncoder().encodeToString(identityDocumentString.getBytes());
Signature sigGenerator = Signature.getInstance("SHA512withRSA");
- // TODO: Get the correct version 0 ok for now
PrivateKey privateKey = keyProvider.getPrivateKey(signingSecretVersion);
sigGenerator.initSign(privateKey);
sigGenerator.update(encodedIdentityDocument.getBytes());
String signature = Base64.getEncoder().encodeToString(sigGenerator.sign());
- SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument(
+ return new SignedIdentityDocument(
encodedIdentityDocument,
signature,
SignedIdentityDocument.DEFAULT_KEY_VERSION,
@@ -65,9 +68,7 @@ public class IdentityDocumentGenerator {
toZoneDnsSuffix(zone, dnsSuffix),
providerDomain + "." + providerService,
ztsUrl,
- SignedIdentityDocument.DEFAILT_DOCUMENT_VERSION
- );
- return Utils.getMapper().writeValueAsString(signedIdentityDocument);
+ SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION);
} catch (Exception e) {
throw new RuntimeException("Exception generating identity document: " + e.getMessage(), e);
}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentResource.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentResource.java
new file mode 100644
index 00000000000..b3e5aee97b3
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentResource.java
@@ -0,0 +1,48 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument;
+
+import com.google.inject.Inject;
+import com.yahoo.container.jaxrs.annotation.Component;
+import com.yahoo.log.LogLevel;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.GET;
+import javax.ws.rs.InternalServerErrorException;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import java.util.logging.Logger;
+
+/**
+ * @author bjorncs
+ */
+@Path("/identity-document")
+public class IdentityDocumentResource {
+
+ private static final Logger log = Logger.getLogger(IdentityDocumentResource.class.getName());
+
+ private final IdentityDocumentGenerator identityDocumentGenerator;
+
+ @Inject
+ public IdentityDocumentResource(@Component IdentityDocumentGenerator identityDocumentGenerator) {
+ this.identityDocumentGenerator = identityDocumentGenerator;
+ }
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public SignedIdentityDocument getIdentityDocument(@QueryParam("hostname") String hostname) {
+ // TODO Use TLS client authentication instead of blindly trusting hostname
+ if (hostname == null) {
+ throw new BadRequestException("The 'hostname' query parameter is missing");
+ }
+ try {
+ return identityDocumentGenerator.generateSignedIdentityDocument(hostname);
+ } catch (Exception e) {
+ String message = String.format("Unable to generate identity doument for '%s': %s", hostname, e.getMessage());
+ log.log(LogLevel.ERROR, message, e);
+ throw new InternalServerErrorException(message, e);
+ }
+ }
+
+}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/model/ProviderUniqueId.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/ProviderUniqueId.java
index 810c75ef0c5..1de2292d2d0 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/model/ProviderUniqueId.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/ProviderUniqueId.java
@@ -1,5 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model;
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -76,4 +76,4 @@ public class ProviderUniqueId {
public int hashCode() {
return Objects.hash(tenant, application, environment, region, instance, clusterId, clusterIndex);
}
-} \ No newline at end of file
+}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/model/SignedIdentityDocument.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/SignedIdentityDocument.java
index 37f94d48a95..2545401f3ec 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/model/SignedIdentityDocument.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/SignedIdentityDocument.java
@@ -1,5 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model;
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
@@ -17,7 +17,7 @@ import java.util.Objects;
public class SignedIdentityDocument {
public static final int DEFAULT_KEY_VERSION = 0;
- public static final int DEFAILT_DOCUMENT_VERSION = 1;
+ public static final int DEFAULT_DOCUMENT_VERSION = 1;
@JsonProperty("identity-document")public final String rawIdentityDocument;
@JsonIgnore public final IdentityDocument identityDocument;
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/AthenzCertificateClient.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/AthenzCertificateClient.java
index dab1581f580..c6aee673f9c 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/AthenzCertificateClient.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/AthenzCertificateClient.java
@@ -17,7 +17,7 @@ import java.util.concurrent.TimeUnit;
/**
* @author bjorncs
*/
-public class AthenzCertificateClient implements CertificateClient {
+public class AthenzCertificateClient {
private final AthenzProviderServiceConfig config;
private final AthenzPrincipalAuthority authority;
@@ -29,7 +29,6 @@ public class AthenzCertificateClient implements CertificateClient {
this.zoneConfig = zoneConfig;
}
- @Override
public X509Certificate updateCertificate(PrivateKey privateKey, TemporalAmount expiryTime) {
SimpleServiceIdentityProvider identityProvider = new SimpleServiceIdentityProvider(
authority, zoneConfig.domain(), zoneConfig.serviceName(),
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/CertificateClient.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/CertificateClient.java
deleted file mode 100644
index 6465873e092..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/CertificateClient.java
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl;
-
-import java.security.PrivateKey;
-import java.security.cert.X509Certificate;
-import java.time.temporal.TemporalAmount;
-
-/**
- * @author bjorncs
- */
-@FunctionalInterface
-public interface CertificateClient {
- X509Certificate updateCertificate(PrivateKey privateKey, TemporalAmount expiryTime);
-}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/FileBackedKeyProvider.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/FileBackedKeyProvider.java
deleted file mode 100644
index 40a2a1dbcc9..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/FileBackedKeyProvider.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl;
-
-import com.yahoo.athenz.auth.util.Crypto;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.nio.file.Files;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
-/**
- * @author bjorncs
- */
-public class FileBackedKeyProvider implements KeyProvider {
-
- private final String keyPathPrefix;
-
- public FileBackedKeyProvider(String keyPathPrefix) {
- this.keyPathPrefix = keyPathPrefix;
- }
-
- @Override
- public PrivateKey getPrivateKey(int version) {
- return Crypto.loadPrivateKey(readPemStringFromFile(new File(keyPathPrefix + ".priv." + version)));
- }
-
- @Override
- public PublicKey getPublicKey(int version) {
- return Crypto.loadPublicKey(readPemStringFromFile(new File(keyPathPrefix + ".pub." + version)));
- }
-
- private static String readPemStringFromFile(File file) {
- try {
- if (!file.exists() || !file.isFile()) {
- throw new IllegalArgumentException("Key missing: " + file.getAbsolutePath());
- }
- return new String(Files.readAllBytes(file.toPath()));
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentServlet.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentServlet.java
deleted file mode 100644
index a66fdf9d82f..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentServlet.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl;
-
-import com.yahoo.log.LogLevel;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.logging.Logger;
-
-/**
- * @author bjorncs
- */
-public class IdentityDocumentServlet extends HttpServlet {
-
- private static final Logger log = Logger.getLogger(IdentityDocumentServlet.class.getName());
-
- private final IdentityDocumentGenerator identityDocumentGenerator;
-
- public IdentityDocumentServlet(IdentityDocumentGenerator identityDocumentGenerator) {
- this.identityDocumentGenerator = identityDocumentGenerator;
- }
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- // TODO verify tls client cert
- String hostname = req.getParameter("hostname");
- if (hostname == null) {
- String message = "The 'hostname' parameter is missing";
- log.log(LogLevel.ERROR, message);
- resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
- return;
- }
- try {
- log.log(LogLevel.INFO, "Generating identity document for " + hostname);
- String signedIdentityDocument = identityDocumentGenerator.generateSignedIdentityDocument(hostname);
- resp.setContentType("application/json");
- PrintWriter writer = resp.getWriter();
- writer.print(signedIdentityDocument);
- writer.flush();
- } catch (Exception e) {
- String message = String.format("Unable to generate identity doument [%s]", e.getMessage());
- log.log(LogLevel.ERROR, message);
- resp.sendError(HttpServletResponse.SC_NOT_FOUND, message);
- }
- }
-
-}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/InstanceConfirmationServlet.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/InstanceConfirmationServlet.java
deleted file mode 100644
index 766b95b443b..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/InstanceConfirmationServlet.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl;
-
-import com.fasterxml.jackson.core.JsonParseException;
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.yahoo.log.LogLevel;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.InstanceConfirmation;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.Reader;
-import java.util.logging.Logger;
-import java.util.stream.Collectors;
-
-/**
- * A Servlet implementing the Athenz Service Provider InstanceConfirmation API
- *
- * @author bjorncs
- */
-public class InstanceConfirmationServlet extends HttpServlet {
-
- private static final Logger log = Logger.getLogger(InstanceConfirmationServlet.class.getName());
-
- private final InstanceValidator instanceValidator;
-
- public InstanceConfirmationServlet(InstanceValidator instanceValidator) {
- this.instanceValidator = instanceValidator;
- }
-
- @Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- // TODO Validate that request originates from ZTS
- try {
- String confirmationContent = toString(req.getReader());
- log.log(LogLevel.DEBUG, () -> "Confirmation content: " + confirmationContent);
- InstanceConfirmation instanceConfirmation =
- Utils.getMapper().readValue(confirmationContent, InstanceConfirmation.class);
- log.log(LogLevel.DEBUG, () -> "Parsed confirmation content: " + instanceConfirmation.toString());
- if (!instanceValidator.isValidInstance(instanceConfirmation)) {
- String message = "Invalid instance: " + instanceConfirmation;
- log.log(LogLevel.ERROR, message);
- resp.sendError(HttpServletResponse.SC_FORBIDDEN, message);
- } else {
- resp.setStatus(HttpServletResponse.SC_OK);
- resp.setContentType("application/json");
- resp.getWriter().write(Utils.getMapper().writeValueAsString(instanceConfirmation));
- }
- } catch (JsonParseException | JsonMappingException e) {
- String message = "InstanceConfirmation is not valid JSON";
- log.log(LogLevel.ERROR, message, e);
- resp.sendError(HttpServletResponse.SC_BAD_REQUEST, message);
- }
- }
-
- private static String toString(Reader reader) throws IOException {
- try (BufferedReader bufferedReader = new BufferedReader(reader)) {
- return bufferedReader.lines().collect(Collectors.joining("\n"));
- }
- }
-
-}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/SecretStoreKeyProvider.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/SecretStoreKeyProvider.java
index 93abda1f9ea..e66131b6cf7 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/SecretStoreKeyProvider.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/SecretStoreKeyProvider.java
@@ -1,8 +1,12 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl;
+import com.google.inject.Inject;
import com.yahoo.athenz.auth.util.Crypto;
+import com.yahoo.config.provision.Zone;
import com.yahoo.jdisc.http.SecretStore;
+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;
@@ -10,19 +14,24 @@ import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;
+import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils.getZoneConfig;
+
/**
* @author mortent
*/
+@SuppressWarnings("unused") // Injected component
public class SecretStoreKeyProvider implements KeyProvider {
private final SecretStore secretStore;
private final String secretName;
private final Map<Integer, KeyPair> secrets;
-
- public SecretStoreKeyProvider(SecretStore secretStore, String secretName) {
+ @Inject
+ public SecretStoreKeyProvider(SecretStore secretStore,
+ Zone zone,
+ AthenzProviderServiceConfig config) {
this.secretStore = secretStore;
- this.secretName = secretName;
+ this.secretName = getZoneConfig(config, zone).secretName();
this.secrets = new HashMap<>();
}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/StatusServlet.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/StatusServlet.java
deleted file mode 100644
index fd5ba5843aa..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/StatusServlet.java
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-
-/**
- * A simple status servlet that should return status code 200 as long as the provider service servlet is up.
- *
- * @author bjorncs
- */
-public class StatusServlet extends HttpServlet {
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- resp.setStatus(HttpServletResponse.SC_OK);
- }
-}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/Utils.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/Utils.java
index d81ec183fd4..ad54aa341bf 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/Utils.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/Utils.java
@@ -3,6 +3,8 @@ package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.yahoo.config.provision.Zone;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
/**
* @author bjorncs
@@ -20,4 +22,10 @@ public class Utils {
mapper.registerModule(new JavaTimeModule());
return mapper;
}
+
+ public static AthenzProviderServiceConfig.Zones getZoneConfig(AthenzProviderServiceConfig config, Zone zone) {
+ String key = zone.environment().value() + "." + zone.region().value();
+ return config.zones(key);
+ }
+
}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/model/InstanceConfirmation.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmation.java
index ade42968e58..7b2725a8d95 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/model/InstanceConfirmation.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmation.java
@@ -1,5 +1,5 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model;
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonCreator;
@@ -14,6 +14,7 @@ import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument.SignedIdentityDocument;
import java.io.IOException;
import java.util.HashMap;
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmationResource.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmationResource.java
new file mode 100644
index 00000000000..5c93bf423d3
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceConfirmationResource.java
@@ -0,0 +1,41 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation;
+
+import com.google.inject.Inject;
+import com.yahoo.container.jaxrs.annotation.Component;
+import com.yahoo.log.LogLevel;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.ForbiddenException;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import java.util.logging.Logger;
+
+/**
+ * @author bjorncs
+ */
+@Path("/instance")
+public class InstanceConfirmationResource {
+
+ private static final Logger log = Logger.getLogger(InstanceConfirmationResource.class.getName());
+
+ private final InstanceValidator instanceValidator;
+
+ @Inject
+ public InstanceConfirmationResource(@Component InstanceValidator instanceValidator) {
+ this.instanceValidator = instanceValidator;
+ }
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public InstanceConfirmation confirmInstance(InstanceConfirmation instanceConfirmation) {
+ if (!instanceValidator.isValidInstance(instanceConfirmation)) {
+ log.log(LogLevel.ERROR, "Invalid instance: " + instanceConfirmation);
+ throw new ForbiddenException("Instance is invalid");
+ }
+ return instanceConfirmation;
+ }
+}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/InstanceValidator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java
index 427f35c41d8..69c5d961b7e 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/InstanceValidator.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java
@@ -1,14 +1,15 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl;
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation;
+import com.google.inject.Inject;
import com.yahoo.config.model.api.ApplicationInfo;
import com.yahoo.config.model.api.ServiceInfo;
import com.yahoo.config.model.api.SuperModelProvider;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.log.LogLevel;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.InstanceConfirmation;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.ProviderUniqueId;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.SignedIdentityDocument;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.KeyProvider;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument.ProviderUniqueId;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument.SignedIdentityDocument;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
@@ -33,6 +34,7 @@ public class InstanceValidator {
private final KeyProvider keyProvider;
private final SuperModelProvider superModelProvider;
+ @Inject
public InstanceValidator(KeyProvider keyProvider, SuperModelProvider superModelProvider) {
this.keyProvider = keyProvider;
this.superModelProvider = superModelProvider;
@@ -64,7 +66,7 @@ public class InstanceValidator {
return isSignatureValid(publicKey, signedIdentityDocument.rawIdentityDocument, signedIdentityDocument.signature);
}
- static boolean isSignatureValid(PublicKey publicKey, String rawIdentityDocument, String signature) {
+ public static boolean isSignatureValid(PublicKey publicKey, String rawIdentityDocument, String signature) {
try {
Signature signatureVerifier = Signature.getInstance("SHA512withRSA");
signatureVerifier.initVerify(publicKey);
diff --git a/athenz-identity-provider-service/src/main/resources/configdefinitions/athenz-provider-service.def b/athenz-identity-provider-service/src/main/resources/configdefinitions/athenz-provider-service.def
index 4aad9a4eae2..13cc78b0bd0 100644
--- a/athenz-identity-provider-service/src/main/resources/configdefinitions/athenz-provider-service.def
+++ b/athenz-identity-provider-service/src/main/resources/configdefinitions/athenz-provider-service.def
@@ -13,12 +13,6 @@ zones{}.secretName string
# Secret version
zones{}.secretVersion int
-# HTTPS port for Athenz Provider Service endpoint
-port int default=8443
-
-# InstanceConfirmation API path
-apiPath string default="/athenz/v1/provider"
-
# Athenz principal authority header name
athenzPrincipalHeaderName string default="Athenz-Principal-Auth"
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderServiceTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderServiceTest.java
deleted file mode 100644
index bf0746aee7e..00000000000
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderServiceTest.java
+++ /dev/null
@@ -1,270 +0,0 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice;
-
-import athenz.shade.zts.jersey.repackaged.com.google.common.collect.ImmutableMap;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.yahoo.config.provision.Environment;
-import com.yahoo.config.provision.RegionName;
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.config.provision.Zone;
-import com.yahoo.log.LogLevel;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.AthenzInstanceProviderService.AthenzCertificateUpdater;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.CertificateClient;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.IdentityDocumentGenerator;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.InstanceValidator;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.KeyProvider;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.IdentityDocument;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.InstanceConfirmation;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.ProviderUniqueId;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.SignedIdentityDocument;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.conn.ssl.NoopHostnameVerifier;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.ssl.SSLContextBuilder;
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.asn1.x509.BasicConstraints;
-import org.bouncycastle.cert.CertIOException;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
-import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.operator.ContentSigner;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.junit.Test;
-
-import javax.net.ssl.SSLContext;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.KeyManagementException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.time.Instant;
-import java.time.temporal.TemporalAmount;
-import java.util.Base64;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.logging.Logger;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-/**
- * @author bjorncs
- */
-public class AthenzInstanceProviderServiceTest {
-
- private static final Logger log = Logger.getLogger(AthenzInstanceProviderServiceTest.class.getName());
- private static final int PORT = 12345;
- private static final Zone ZONE = new Zone(SystemName.cd, Environment.dev, RegionName.from("us-north-1"));
-
- @Test
- public void provider_service_hosts_endpoint_secured_with_tls() throws Exception {
- String domain = "domain";
- String service = "service";
-
- AutoGeneratedKeyProvider keyProvider = new AutoGeneratedKeyProvider();
- PrivateKey privateKey = keyProvider.getPrivateKey(0);
- AthenzProviderServiceConfig config = getAthenzProviderConfig(domain, service, "vespa.dns.suffix", ZONE);
- SslContextFactory sslContextFactory = AthenzInstanceProviderService.createSslContextFactory();
- AthenzCertificateUpdater certificateUpdater = new AthenzCertificateUpdater(
- new SelfSignedCertificateClient(keyProvider.getKeyPair(), config, getZoneConfig(config, ZONE)),
- sslContextFactory,
- keyProvider,
- config,
- getZoneConfig(config, ZONE));
-
- ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
- when(executor.awaitTermination(anyLong(), any())).thenReturn(true);
-
- InstanceValidator instanceValidator = mock(InstanceValidator.class);
- when(instanceValidator.isValidInstance(any())).thenReturn(true);
-
- IdentityDocumentGenerator identityDocumentGenerator = mock(IdentityDocumentGenerator.class);
-
- AthenzInstanceProviderService athenzInstanceProviderService = new AthenzInstanceProviderService(
- config, executor, ZONE, sslContextFactory, instanceValidator, identityDocumentGenerator, certificateUpdater);
-
- try (CloseableHttpClient client = createHttpClient(domain, service)) {
- assertFalse(getStatus(client));
- certificateUpdater.run();
- assertTrue(getStatus(client));
- assertInstanceConfirmationSucceeds(client, privateKey);
- certificateUpdater.run();
- assertTrue(getStatus(client));
- assertInstanceConfirmationSucceeds(client, privateKey);
- } finally {
- athenzInstanceProviderService.deconstruct();
- }
- }
-
- public static AthenzProviderServiceConfig getAthenzProviderConfig(String domain, String service, String dnsSuffix, Zone zone) {
- AthenzProviderServiceConfig.Zones.Builder zoneConfig =
- new AthenzProviderServiceConfig.Zones.Builder()
- .serviceName(service)
- .secretVersion(0)
- .domain(domain)
- .secretName("s3cr3t");
-
- return new AthenzProviderServiceConfig(
- new AthenzProviderServiceConfig.Builder()
- .zones(ImmutableMap.of(zone.environment().value() + "." + zone.region().value(), zoneConfig))
- .port(PORT)
- .certDnsSuffix(dnsSuffix)
- .ztsUrl("localhost/zts")
- .athenzPrincipalHeaderName("Athenz-Principal-Auth")
- .apiPath(""));
-
- }
-
- public static AthenzProviderServiceConfig.Zones getZoneConfig(AthenzProviderServiceConfig config, Zone zone) {
- return config.zones(zone.environment().value() + "." + zone.region().value());
- }
-
- private static boolean getStatus(HttpClient client) {
- try {
- HttpResponse response = client.execute(new HttpGet("https://localhost:" + PORT + "/status.html"));
- return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;
- } catch (Exception e) {
- log.log(LogLevel.INFO, "Status.html failed: " + e);
- return false;
- }
- }
-
- private static void assertInstanceConfirmationSucceeds(HttpClient client, PrivateKey privateKey) throws IOException {
- HttpPost httpPost = new HttpPost("https://localhost:" + PORT + "/instance");
- httpPost.setEntity(createInstanceConfirmation(privateKey));
- HttpResponse response = client.execute(httpPost);
- assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
- }
-
- private static CloseableHttpClient createHttpClient(String domain, String service)
- throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
- SSLContext sslContext = new SSLContextBuilder()
- .loadTrustMaterial(null, (certificateChain, ignoredAuthType) ->
- certificateChain[0].getSubjectX500Principal().getName().equals("CN=" + domain + "." + service))
- .build();
-
- return HttpClients.custom()
- .setSslcontext(sslContext)
- .setSSLHostnameVerifier(new NoopHostnameVerifier())
- .build();
- }
-
- private static HttpEntity createInstanceConfirmation(PrivateKey privateKey) {
- IdentityDocument identityDocument = new IdentityDocument(
- new ProviderUniqueId("tenant", "application", "environment", "region", "instance", "cluster-id", 0),
- "hostname",
- "instance-hostname",
- Instant.now());
- try {
- ObjectMapper mapper = Utils.getMapper();
- String encodedIdentityDocument =
- Base64.getEncoder().encodeToString(mapper.writeValueAsString(identityDocument).getBytes());
- Signature sigGenerator = Signature.getInstance("SHA512withRSA");
- sigGenerator.initSign(privateKey);
- sigGenerator.update(encodedIdentityDocument.getBytes());
-
- InstanceConfirmation instanceConfirmation = new InstanceConfirmation(
- "provider", "domain", "service",
- new SignedIdentityDocument(encodedIdentityDocument,
- Base64.getEncoder().encodeToString(sigGenerator.sign()),
- 0,
- identityDocument.providerUniqueId.asString(),
- "dnssuffix",
- "service",
- "localhost/zts",
- 1));
- return new StringEntity(mapper.writeValueAsString(instanceConfirmation));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- public static class AutoGeneratedKeyProvider implements KeyProvider {
-
- private final KeyPair keyPair;
-
- public AutoGeneratedKeyProvider() {
- try {
- KeyPairGenerator rsa = KeyPairGenerator.getInstance("RSA");
- rsa.initialize(2048);
- keyPair = rsa.genKeyPair();
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public PrivateKey getPrivateKey(int version) {
- return keyPair.getPrivate();
- }
-
- @Override
- public PublicKey getPublicKey(int version) {
- return keyPair.getPublic();
- }
-
- public KeyPair getKeyPair() {
- return keyPair;
- }
- }
-
- private static class SelfSignedCertificateClient implements CertificateClient {
-
- private final KeyPair keyPair;
- private final AthenzProviderServiceConfig config;
- private final AthenzProviderServiceConfig.Zones zoneConfig;
-
- private SelfSignedCertificateClient(KeyPair keyPair, AthenzProviderServiceConfig config,
- AthenzProviderServiceConfig.Zones zoneConfig) {
- this.keyPair = keyPair;
- this.config = config;
- this.zoneConfig = zoneConfig;
- }
-
- @Override
- public X509Certificate updateCertificate(PrivateKey privateKey, TemporalAmount expiryTime) {
- try {
- ContentSigner contentSigner = new JcaContentSignerBuilder("SHA512WithRSA").build(keyPair.getPrivate());
- X500Name dnName = new X500Name("CN=" + zoneConfig.domain() + "." + zoneConfig.serviceName());
- Calendar calendar = Calendar.getInstance();
- calendar.add(Calendar.HOUR, 1);
- Date endDate = calendar.getTime();
- JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
- dnName, BigInteger.ONE, new Date(), endDate, dnName, keyPair.getPublic());
- certBuilder.addExtension(new ASN1ObjectIdentifier("2.5.29.19"), true, new BasicConstraints(true));
-
- return new JcaX509CertificateConverter()
- .setProvider(new BouncyCastleProvider())
- .getCertificate(certBuilder.build(contentSigner));
- } catch (CertificateException | CertIOException | OperatorCreationException e) {
- throw new RuntimeException(e);
- }
- }
- }
-}
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
new file mode 100644
index 00000000000..ca6b5529b08
--- /dev/null
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AutoGeneratedKeyProvider.java
@@ -0,0 +1,40 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice;
+
+import 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() {
+ try {
+ KeyPairGenerator rsa = KeyPairGenerator.getInstance("RSA");
+ rsa.initialize(2048);
+ keyPair = rsa.genKeyPair();
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @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/TestUtils.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/TestUtils.java
new file mode 100644
index 00000000000..c09a9fb1740
--- /dev/null
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/TestUtils.java
@@ -0,0 +1,31 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice;
+
+import com.google.common.collect.ImmutableMap;
+import com.yahoo.config.provision.Zone;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
+
+/**
+ * @author bjorncs
+ */
+public class TestUtils {
+
+ public static AthenzProviderServiceConfig getAthenzProviderConfig(String domain,
+ String service,
+ String dnsSuffix,
+ Zone zone) {
+ AthenzProviderServiceConfig.Zones.Builder zoneConfig =
+ new AthenzProviderServiceConfig.Zones.Builder()
+ .serviceName(service)
+ .secretVersion(0)
+ .domain(domain)
+ .secretName("s3cr3t");
+ return new AthenzProviderServiceConfig(
+ new AthenzProviderServiceConfig.Builder()
+ .zones(ImmutableMap.of(zone.environment().value() + "." + zone.region().value(), zoneConfig))
+ .certDnsSuffix(dnsSuffix)
+ .ztsUrl("localhost/zts")
+ .athenzPrincipalHeaderName("Athenz-Principal-Auth"));
+ }
+
+}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSignerTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSignerTest.java
new file mode 100644
index 00000000000..e691da0b2c3
--- /dev/null
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSignerTest.java
@@ -0,0 +1,134 @@
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.ca;
+
+import com.yahoo.test.ManualClock;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.ExtensionsGenerator;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
+import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
+import org.junit.Test;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.cert.X509Certificate;
+import java.time.Instant;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author freva
+ */
+public class CertificateSignerTest {
+
+ private final KeyPair clientKeyPair = getKeyPair();
+
+ private final long startTime = 1234567890000L;
+ private final KeyPair caKeyPair = getKeyPair();
+ private final String cfgServerHostname = "cfg1.us-north-1.vespa.domain.tld";
+ private final ManualClock clock = new ManualClock(Instant.ofEpochMilli(startTime));
+ private final CertificateSigner signer = new CertificateSigner(caKeyPair.getPrivate(), cfgServerHostname, clock);
+
+ private final String requestersHostname = "tenant-123.us-north-1.vespa.domain.tld";
+
+ @Test
+ public void test_signing() throws Exception {
+ ExtensionsGenerator extGen = new ExtensionsGenerator();
+ String subject = "C=NO,OU=Vespa,CN=" + requestersHostname;
+ PKCS10CertificationRequest request = makeRequest(subject, extGen.generate());
+
+ X509Certificate certificate = signer.generateX509Certificate(request, requestersHostname);
+ assertCertificate(certificate, subject, Collections.singleton(Extension.basicConstraints.getId()));
+ }
+
+ @Test
+ public void common_name_test() throws Exception {
+ CertificateSigner.verifyCertificateCommonName(
+ new X500Name("CN=" + requestersHostname), requestersHostname);
+ CertificateSigner.verifyCertificateCommonName(
+ new X500Name("C=NO,OU=Vespa,CN=" + requestersHostname), requestersHostname);
+ CertificateSigner.verifyCertificateCommonName(
+ new X500Name("C=NO+OU=org,CN=" + requestersHostname), requestersHostname);
+
+ assertCertificateCommonNameException("C=NO", "Only 1 common name should be set");
+ assertCertificateCommonNameException("C=US+CN=abc123.domain.tld,C=NO+CN=" + requestersHostname, "Only 1 common name should be set");
+ assertCertificateCommonNameException("CN=evil.hostname.domain.tld",
+ "Expected common name to be tenant-123.us-north-1.vespa.domain.tld, but was evil.hostname.domain.tld");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void extensions_test_subject_alternative_names() throws Exception {
+ ExtensionsGenerator extGen = new ExtensionsGenerator();
+ extGen.addExtension(Extension.subjectAlternativeName, false, new GeneralNames(new GeneralName[] {
+ new GeneralName(GeneralName.dNSName, "some.other.domain.tld")}));
+ PKCS10CertificationRequest request = makeRequest("OU=Vespa", extGen.generate());
+
+ CertificateSigner.verifyCertificateExtensions(request);
+ }
+
+ @Test
+ public void extensions_allowed() throws Exception {
+ ExtensionsGenerator extGen = new ExtensionsGenerator();
+ extGen.addExtension(Extension.certificateIssuer, true, new byte[0]);
+ PKCS10CertificationRequest request = makeRequest("OU=Vespa", extGen.generate());
+
+ CertificateSigner.verifyCertificateExtensions(request);
+ }
+
+ private void assertCertificateCommonNameException(String subject, String expectedMessage) {
+ try {
+ CertificateSigner.verifyCertificateCommonName(new X500Name(subject), requestersHostname);
+ fail("Expected to fail");
+ } catch (IllegalArgumentException e) {
+ assertEquals(expectedMessage, e.getMessage());
+ }
+ }
+
+ private void assertCertificate(X509Certificate certificate, String expectedSubjectName, Set<String> expectedExtensions) throws Exception {
+ assertEquals(3, certificate.getVersion());
+ assertEquals(BigInteger.valueOf(startTime), certificate.getSerialNumber());
+ assertEquals(startTime, certificate.getNotBefore().getTime());
+ assertEquals(startTime + CertificateSigner.CERTIFICATE_EXPIRATION.toMillis(), certificate.getNotAfter().getTime());
+ assertEquals(CertificateSigner.SIGNER_ALGORITHM, certificate.getSigAlgName());
+ assertEquals(expectedSubjectName, certificate.getSubjectDN().getName());
+ assertEquals("CN=" + cfgServerHostname, certificate.getIssuerX500Principal().getName());
+
+ Set<String> extensions = Stream.of(certificate.getNonCriticalExtensionOIDs(),
+ certificate.getCriticalExtensionOIDs())
+ .flatMap(Collection::stream)
+ .collect(Collectors.toSet());
+ assertEquals(expectedExtensions, extensions);
+
+ certificate.verify(caKeyPair.getPublic());
+ }
+
+ private PKCS10CertificationRequest makeRequest(String subject, Extensions extensions) throws Exception {
+ PKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(
+ new X500Name(subject), clientKeyPair.getPublic());
+ builder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extensions);
+
+ ContentSigner signGen = new JcaContentSignerBuilder(CertificateSigner.SIGNER_ALGORITHM).build(caKeyPair.getPrivate());
+ return builder.build(signGen);
+ }
+
+ private static KeyPair getKeyPair() {
+ try {
+ return KeyPairGenerator.getInstance("RSA").genKeyPair();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CsrSerializedPayloadTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CsrSerializedPayloadTest.java
new file mode 100644
index 00000000000..b8433856f95
--- /dev/null
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CsrSerializedPayloadTest.java
@@ -0,0 +1,32 @@
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.ca;
+
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author bjorncs
+ */
+public class CsrSerializedPayloadTest {
+
+ @Test
+ public void it_can_be_deserialized() throws IOException {
+ String serialized = "{\"csr\":\"-----BEGIN CERTIFICATE REQUEST-----\\nMIICVDCCATwCAQAwDzENMAsGA1UEAwwEdGV" +
+ "zdDCCASIwDQYJKoZIhvcNAQEBBQAD\\nggEPADCCAQoCggEBAL7xra4De9B54yY6lw8Ka/lt7lDEKQRp42RYzpXjHIQXFgr8" +
+ "\\n+EvJCLEldFoqfOm728KAWQq/8YdFR4hBwOz8Rr8khJKMBCQ2DWvGYz2705nr3j3v\\nsd3RE5i8n8cUdKiHRuOf305xgy" +
+ "970TFb+s5/tQOfDMDfvC/BdHNhB4pc0P04CVs/\\nzusKvghdSXFVufAuVaY30ZyviqrDVlBZnI158MmRzfINwP70ZYn5wsq" +
+ "crKzgSUBp\\nH/WjxaklSzGOH8Uk/EKVx0luzAxtTU8jO7MU1+EG8H4E+FI9ijdjftYyko5UAOQO\\nJGiI9/qHJIMVOIcQa" +
+ "k1PA5+2/0NbtVxihQi/uJcCAwEAAaAAMA0GCSqGSIb3DQEB\\nCwUAA4IBAQAelFvM6PyDFufv9pNmFigNqOO+r8ats9Xak9" +
+ "JVtGERo9KFcNDAkawD\\nMPzWQeB87oPnB5dlSdkI2J/jIV7/zR9Qoa2qZlKeL4vUIvfMTj5EOmQLn4ofoBwa\\n50D8Ro3D" +
+ "06Ohb1KE3seOK2FfVybiATpoaICCjb0ibhx4lNsJGZXpw6F2OdTRi8Fb\\n7kfgLiLPCH+UiHDeVnjVVr/PUKeSImgv44mb4" +
+ "c6EU29MYkM4LxCY9/c4scG7Pq+s\\nuHU5Tepjsnmkdtip5NzS7csPXENEygKyksPHWFFojPrtF6nFkMzzIPUgKbsmm4+H\\" +
+ "nfJihCYL3pc3+bVYl87TIcdohJ1GYvfw7\\n-----END CERTIFICATE REQUEST-----\\n\"}";
+ CsrSerializedPayload csrSerializedPayload = Utils.getMapper().readValue(serialized, CsrSerializedPayload.class);
+ assertThat(csrSerializedPayload.csr, notNullValue());
+ }
+
+}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentGeneratorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java
index d77757374ce..0c12e137e27 100644
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentGeneratorTest.java
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java
@@ -1,4 +1,4 @@
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl;
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument;
import com.google.common.collect.ImmutableSet;
@@ -13,10 +13,9 @@ 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.hosted.athenz.instanceproviderservice.AthenzInstanceProviderServiceTest.AutoGeneratedKeyProvider;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.AutoGeneratedKeyProvider;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.ProviderUniqueId;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.SignedIdentityDocument;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation.InstanceValidator;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import com.yahoo.vespa.hosted.provision.node.Allocation;
@@ -27,8 +26,7 @@ import org.junit.Test;
import java.util.HashSet;
import java.util.Optional;
-import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.AthenzInstanceProviderServiceTest.getAthenzProviderConfig;
-import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.AthenzInstanceProviderServiceTest.getZoneConfig;
+import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.TestUtils.getAthenzProviderConfig;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.eq;
@@ -66,17 +64,9 @@ public class IdentityDocumentGeneratorTest {
String dnsSuffix = "vespa.dns.suffix";
AthenzProviderServiceConfig config = getAthenzProviderConfig("domain", "service", dnsSuffix, ZONE);
- IdentityDocumentGenerator identityDocumentGenerator = new IdentityDocumentGenerator(
- config,
- getZoneConfig(config, ZONE),
- nodeRepository,
- ZONE,
- keyProvider);
- String rawSignedIdentityDocument = identityDocumentGenerator.generateSignedIdentityDocument(hostname);
-
-
- SignedIdentityDocument signedIdentityDocument =
- Utils.getMapper().readValue(rawSignedIdentityDocument, SignedIdentityDocument.class);
+ IdentityDocumentGenerator identityDocumentGenerator =
+ new IdentityDocumentGenerator(config, nodeRepository, ZONE, keyProvider);
+ SignedIdentityDocument signedIdentityDocument = identityDocumentGenerator.generateSignedIdentityDocument(hostname);
// Verify attributes
assertEquals(hostname, signedIdentityDocument.identityDocument.instanceHostname);
@@ -92,7 +82,7 @@ public class IdentityDocumentGeneratorTest {
// Validate signature
assertTrue("Message", InstanceValidator.isSignatureValid(keyProvider.getPublicKey(0),
- signedIdentityDocument.rawIdentityDocument,
- signedIdentityDocument.signature));
+ signedIdentityDocument.rawIdentityDocument,
+ signedIdentityDocument.signature));
}
-} \ No newline at end of file
+}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/InstanceValidatorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java
index c1fab319ebf..c68a8805abc 100644
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/InstanceValidatorTest.java
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java
@@ -1,4 +1,4 @@
-package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl;
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yahoo.config.model.api.ApplicationInfo;
@@ -8,11 +8,12 @@ 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.vespa.hosted.athenz.instanceproviderservice.AthenzInstanceProviderServiceTest.AutoGeneratedKeyProvider;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.IdentityDocument;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.InstanceConfirmation;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.ProviderUniqueId;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.SignedIdentityDocument;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.AutoGeneratedKeyProvider;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.KeyProvider;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument.IdentityDocument;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument.ProviderUniqueId;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.identitydocument.SignedIdentityDocument;
import org.junit.Test;
import java.security.PrivateKey;
@@ -28,8 +29,8 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
-import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.InstanceValidator.SERVICE_PROPERTIES_DOMAIN_KEY;
-import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.InstanceValidator.SERVICE_PROPERTIES_SERVICE_KEY;
+import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation.InstanceValidator.SERVICE_PROPERTIES_DOMAIN_KEY;
+import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.instanceconfirmation.InstanceValidator.SERVICE_PROPERTIES_SERVICE_KEY;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
@@ -168,4 +169,4 @@ public class InstanceValidatorTest {
return new ApplicationInfo(appId, 0, model);
}
-} \ No newline at end of file
+}