diff options
author | Morten Tokle <mortent@oath.com> | 2017-10-20 11:06:26 +0200 |
---|---|---|
committer | Morten Tokle <mortent@oath.com> | 2017-10-20 11:13:01 +0200 |
commit | ce5b8db39f64101d7ac9fae2847f3db614f14638 (patch) | |
tree | 215f43b5d6c5c5ed3a9fc0b6d9a16639a9f90877 | |
parent | 36476464f1eea5627d8975ea18fd647080dc93f8 (diff) |
Add Athenz identity provider and configuration
28 files changed, 599 insertions, 58 deletions
diff --git a/athenz-identity-provider-service/pom.xml b/athenz-identity-provider-service/pom.xml index e3998b02ad0..074d4aa54f4 100644 --- a/athenz-identity-provider-service/pom.xml +++ b/athenz-identity-provider-service/pom.xml @@ -78,6 +78,23 @@ <artifactId>jetty-servlet</artifactId> <scope>provided</scope> </dependency> + <dependency> + <groupId>com.yahoo.vespa</groupId> + <artifactId>node-repository</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>com.yahoo.vespa</groupId> + <artifactId>config-provisioning</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.yahoo.vespa</groupId> + <artifactId>container-disc</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> <!-- TEST --> <dependency> @@ -103,6 +120,11 @@ <version>4.4.1</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> 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 index e3b31263421..e862717d1d1 100644 --- 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 @@ -8,16 +8,19 @@ import com.yahoo.athenz.auth.util.Crypto; import com.yahoo.athenz.zts.InstanceRefreshRequest; import com.yahoo.athenz.zts.ZTSClient; import com.yahoo.component.AbstractComponent; +import com.yahoo.config.provision.Zone; import com.yahoo.jdisc.http.ssl.ReaderForPath; import com.yahoo.jdisc.http.ssl.pem.PemKeyStore; import com.yahoo.jdisc.http.ssl.pem.PemSslKeyStore; import com.yahoo.log.LogLevel; import com.yahoo.vespa.hosted.athenz.identityproviderservice.config.AthenzProviderServiceConfig; import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.FileBackedKeyProvider; +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.ProviderServiceServlet; 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; @@ -45,16 +48,18 @@ public class AthenzInstanceProviderService extends AbstractComponent { private final Server jetty; @Inject - public AthenzInstanceProviderService(AthenzProviderServiceConfig config) { - this(config, new FileBackedKeyProvider(config.keyPathPrefix()), Executors.newSingleThreadScheduledExecutor()); + public AthenzInstanceProviderService(AthenzProviderServiceConfig config, NodeRepository nodeRepository, Zone zone) { + this(config, new FileBackedKeyProvider(config.keyPathPrefix()), Executors.newSingleThreadScheduledExecutor(), + nodeRepository, zone); } AthenzInstanceProviderService(AthenzProviderServiceConfig config, KeyProvider keyProvider, - ScheduledExecutorService scheduler) { + ScheduledExecutorService scheduler, NodeRepository nodeRepository, Zone zone) { this.scheduler = scheduler; SslContextFactory sslContextFactory = createSslContextFactory(); - this.jetty = createJettyServer(config.port(), config.apiPath(), keyProvider, sslContextFactory); + this.jetty = createJettyServer(config.port(), config.apiPath(), keyProvider, sslContextFactory, + nodeRepository, zone); AthenzCertificateUpdater reloader = new AthenzCertificateUpdater( sslContextFactory, keyProvider, config); scheduler.scheduleAtFixedRate(reloader, 0, 1, TimeUnit.DAYS); @@ -66,7 +71,10 @@ public class AthenzInstanceProviderService extends AbstractComponent { } private static Server createJettyServer(int port, String apiPath, - KeyProvider keyProvider, SslContextFactory sslContextFactory) { + KeyProvider keyProvider, + SslContextFactory sslContextFactory, + NodeRepository nodeRepository, + Zone zone) { Server server = new Server(); ServerConnector connector = new ServerConnector(server, sslContextFactory); connector.setPort(port); @@ -74,7 +82,7 @@ public class AthenzInstanceProviderService extends AbstractComponent { ServletHandler handler = new ServletHandler(); ProviderServiceServlet providerServiceServlet = - new ProviderServiceServlet(new InstanceValidator(keyProvider)); + new ProviderServiceServlet(new InstanceValidator(keyProvider), new IdentityDocumentGenerator(nodeRepository, zone, keyProvider)); handler.addServletWithMapping(new ServletHolder(providerServiceServlet), apiPath); handler.addServletWithMapping(StatusServlet.class, "/status.html"); server.setHandler(handler); 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/impl/IdentityDocumentGenerator.java new file mode 100644 index 00000000000..833f1348338 --- /dev/null +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentGenerator.java @@ -0,0 +1,79 @@ +package com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl; + +import com.yahoo.athenz.auth.util.Crypto; +import com.yahoo.config.provision.Zone; +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.provision.Node; +import com.yahoo.vespa.hosted.provision.NodeRepository; +import com.yahoo.vespa.hosted.provision.node.Allocation; + +import java.security.PrivateKey; +import java.security.Signature; +import java.time.Instant; +import java.util.Base64; + +/** + * @author mortent + */ +public class IdentityDocumentGenerator { + + private final NodeRepository nodeRepository; + private final Zone zone; + private final KeyProvider keyProvider; + + public IdentityDocumentGenerator(NodeRepository nodeRepository, Zone zone, KeyProvider keyProvider) { + this.nodeRepository = nodeRepository; + this.zone = zone; + this.keyProvider = keyProvider; + } + + public String generateSignedIdentityDocument(String hostname) { + Node node = nodeRepository.getNode(hostname).orElseThrow(() -> new RuntimeException("Unable to find node " + hostname)); + try { + IdentityDocument identityDocument = generateIdDocument(node); + String identityDocumentString = Utils.getMapper().writeValueAsString(identityDocument); + + String encodedIdentityDocument = + Base64.getEncoder().encodeToString(identityDocumentString.getBytes()); + Signature sigGenerator = Signature.getInstance("SHA512withRSA"); + + // TODO: Get the correct version 0 ok for now + PrivateKey privateKey = Crypto.loadPrivateKey(keyProvider.getPrivateKey(0)); + sigGenerator.initSign(privateKey); + sigGenerator.update(encodedIdentityDocument.getBytes()); + String signature = Base64.getEncoder().encodeToString(sigGenerator.sign()); + + SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument( + encodedIdentityDocument, + signature, + SignedIdentityDocument.DEFAULT_KEY_VERSION, + SignedIdentityDocument.DEFAILT_DOCUMENT_VERSION + ); + return Utils.getMapper().writeValueAsString(signedIdentityDocument); + } catch (Exception e) { + throw new RuntimeException("Exception generating identity document: " + e.getMessage()); + } + } + + private IdentityDocument generateIdDocument(Node node) { + Allocation allocation = node.allocation().get(); + ProviderUniqueId providerUniqueId = new ProviderUniqueId( + allocation.owner().tenant().value(), + allocation.owner().application().value(), + zone.environment().value(), + zone.region().value(), + allocation.owner().instance().value(), + allocation.membership().cluster().id().value(), + allocation.membership().index()); + + return new IdentityDocument( + providerUniqueId, + "localhost", + node.hostname(), + Instant.now()); + } + +} + diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/ProviderServiceServlet.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/ProviderServiceServlet.java index 62b2d13cda0..7766dc9cc3c 100644 --- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/ProviderServiceServlet.java +++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/ProviderServiceServlet.java @@ -12,6 +12,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; +import java.io.PrintWriter; import java.io.Reader; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -26,9 +27,11 @@ public class ProviderServiceServlet extends HttpServlet { private static final Logger log = Logger.getLogger(ProviderServiceServlet.class.getName()); private final InstanceValidator instanceValidator; + private final IdentityDocumentGenerator identityDocumentGenerator; - public ProviderServiceServlet(InstanceValidator instanceValidator) { + public ProviderServiceServlet(InstanceValidator instanceValidator, IdentityDocumentGenerator identityDocumentGenerator) { this.instanceValidator = instanceValidator; + this.identityDocumentGenerator = identityDocumentGenerator; } @Override @@ -54,6 +57,21 @@ public class ProviderServiceServlet extends HttpServlet { } } + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + // TODO verify tls client cert + String hostname = req.getParameter("hostname"); + try { + String signedIdentityDocument = identityDocumentGenerator.generateSignedIdentityDocument(hostname); + resp.setContentType("application/json"); + PrintWriter writer = resp.getWriter(); + writer.print(signedIdentityDocument); + writer.flush(); + } catch (Exception e) { + resp.sendError(HttpServletResponse.SC_NOT_FOUND, String.format("Unable to generate identity doument [%s]", e.getMessage())); + } + } + 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/model/IdentityDocument.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/model/IdentityDocument.java index 0b4fc38b00d..41ce5d969a7 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/impl/model/IdentityDocument.java @@ -11,10 +11,6 @@ import java.util.Objects; */ public class IdentityDocument { - @JsonProperty("athens-domain") - public final String athensDomain; - @JsonProperty("athens-service") - public final String athensService; @JsonProperty("provider-unique-id") public final ProviderUniqueId providerUniqueId; @JsonProperty("configserver-hostname") @@ -24,14 +20,11 @@ public class IdentityDocument { @JsonProperty("created-at") public final Instant createdAt; - public IdentityDocument(@JsonProperty("athens-domain") String athensDomain, - @JsonProperty("athens-service") String athensService, + public IdentityDocument( @JsonProperty("provider-unique-id") ProviderUniqueId providerUniqueId, @JsonProperty("configserver-hostname") String configServerHostname, @JsonProperty("instance-hostname") String instanceHostname, @JsonProperty("created-at") Instant createdAt) { - this.athensDomain = athensDomain; - this.athensService = athensService; this.providerUniqueId = providerUniqueId; this.configServerHostname = configServerHostname; this.instanceHostname = instanceHostname; @@ -41,9 +34,7 @@ public class IdentityDocument { @Override public String toString() { return "IdentityDocument{" + - "athensDomain='" + athensDomain + '\'' + - ", athensService='" + athensService + '\'' + - ", providerUniqueId=" + providerUniqueId + + "providerUniqueId=" + providerUniqueId + ", configServerHostname='" + configServerHostname + '\'' + ", instanceHostname='" + instanceHostname + '\'' + ", createdAt=" + createdAt + @@ -55,9 +46,7 @@ public class IdentityDocument { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; IdentityDocument that = (IdentityDocument) o; - return Objects.equals(athensDomain, that.athensDomain) && - Objects.equals(athensService, that.athensService) && - Objects.equals(providerUniqueId, that.providerUniqueId) && + return Objects.equals(providerUniqueId, that.providerUniqueId) && Objects.equals(configServerHostname, that.configServerHostname) && Objects.equals(instanceHostname, that.instanceHostname) && Objects.equals(createdAt, that.createdAt); @@ -65,6 +54,6 @@ public class IdentityDocument { @Override public int hashCode() { - return Objects.hash(athensDomain, athensService, providerUniqueId, configServerHostname, instanceHostname, createdAt); + return Objects.hash(providerUniqueId, configServerHostname, instanceHostname, createdAt); } } 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/impl/model/ProviderUniqueId.java index 4c09dd917a4..ec699120802 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/impl/model/ProviderUniqueId.java @@ -10,13 +10,20 @@ import java.util.Objects; */ public class ProviderUniqueId { - @JsonProperty("tenant") public final String tenant; - @JsonProperty("application") public final String application; - @JsonProperty("environment") public final String environment; - @JsonProperty("region") public final String region; - @JsonProperty("instance") public final String instance; - @JsonProperty("cluster-id") public final String clusterId; - @JsonProperty("cluster-index") public final int clusterIndex; + @JsonProperty("tenant") + public final String tenant; + @JsonProperty("application") + public final String application; + @JsonProperty("environment") + public final String environment; + @JsonProperty("region") + public final String region; + @JsonProperty("instance") + public final String instance; + @JsonProperty("cluster-id") + public final String clusterId; + @JsonProperty("cluster-index") + public final int clusterIndex; public ProviderUniqueId(@JsonProperty("tenant") String tenant, @JsonProperty("application") String application, @@ -37,14 +44,14 @@ public class ProviderUniqueId { @Override public String toString() { return "ProviderUniqueId{" + - "tenant='" + tenant + '\'' + - ", application='" + application + '\'' + - ", environment='" + environment + '\'' + - ", region='" + region + '\'' + - ", instance='" + instance + '\'' + - ", clusterId='" + clusterId + '\'' + - ", clusterIndex=" + clusterIndex + - '}'; + "tenant='" + tenant + '\'' + + ", application='" + application + '\'' + + ", environment='" + environment + '\'' + + ", region='" + region + '\'' + + ", instance='" + instance + '\'' + + ", clusterId='" + clusterId + '\'' + + ", clusterIndex=" + clusterIndex + + '}'; } @Override @@ -53,16 +60,16 @@ public class ProviderUniqueId { if (o == null || getClass() != o.getClass()) return false; ProviderUniqueId that = (ProviderUniqueId) o; return clusterIndex == that.clusterIndex && - Objects.equals(tenant, that.tenant) && - Objects.equals(application, that.application) && - Objects.equals(environment, that.environment) && - Objects.equals(region, that.region) && - Objects.equals(instance, that.instance) && - Objects.equals(clusterId, that.clusterId); + Objects.equals(tenant, that.tenant) && + Objects.equals(application, that.application) && + Objects.equals(environment, that.environment) && + Objects.equals(region, that.region) && + Objects.equals(instance, that.instance) && + Objects.equals(clusterId, that.clusterId); } @Override 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/impl/model/SignedIdentityDocument.java index df1bfe772e8..09d57582fa3 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/impl/model/SignedIdentityDocument.java @@ -16,6 +16,9 @@ import java.util.Objects; */ public class SignedIdentityDocument { + public static final int DEFAULT_KEY_VERSION = 0; + public static final int DEFAILT_DOCUMENT_VERSION = 1; + @JsonProperty("identity-document")public final String rawIdentityDocument; @JsonIgnore public final IdentityDocument identityDocument; @JsonProperty("signature") public final String signature; 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 index 5a700c32f7e..900bdec6855 100644 --- 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 @@ -4,6 +4,9 @@ package com.yahoo.vespa.hosted.athenz.instanceproviderservice; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.yahoo.athenz.auth.util.Crypto; +import com.yahoo.config.provision.Environment; +import com.yahoo.config.provision.RegionName; +import com.yahoo.config.provision.Zone; import com.yahoo.log.LogLevel; import com.yahoo.vespa.hosted.athenz.identityproviderservice.config.AthenzProviderServiceConfig; import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.KeyProvider; @@ -12,6 +15,7 @@ import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.model.Identity 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.provision.NodeRepository; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; @@ -44,6 +48,7 @@ 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.Mockito.mock; /** * @author bjorncs @@ -74,7 +79,9 @@ public class AthenzInstanceProviderServiceTest { .apiPath("/")); ScheduledExecutorServiceMock executor = new ScheduledExecutorServiceMock(); - AthenzInstanceProviderService athenzInstanceProviderService = new AthenzInstanceProviderService(config, keyProvider, executor); + NodeRepository nodeRepository = mock(NodeRepository.class); + Zone zone = new Zone(Environment.dev, RegionName.from("us-north-1")); + AthenzInstanceProviderService athenzInstanceProviderService = new AthenzInstanceProviderService(config, keyProvider, executor, nodeRepository, zone); try (CloseableHttpClient client = createHttpClient(domain, service)) { Runnable certificateRefreshCommand = executor.getCommand().orElseThrow(() -> new AssertionError("Command not present")); @@ -122,7 +129,6 @@ public class AthenzInstanceProviderServiceTest { private static HttpEntity createInstanceConfirmation(PrivateKey privateKey) { IdentityDocument identityDocument = new IdentityDocument( - "domain", "service", new ProviderUniqueId( "tenant", "application", "environment", "region", "instance", "cluster-id", 0), "hostname", "instance-hostname", Instant.now()); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/Container.java b/config-model/src/main/java/com/yahoo/vespa/model/container/Container.java index 8e2c6b8532d..ddd998c8816 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/Container.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/Container.java @@ -7,6 +7,7 @@ import com.yahoo.config.model.producer.AbstractConfigProducer; import com.yahoo.container.ComponentsConfig; import com.yahoo.container.QrConfig; import com.yahoo.container.core.ContainerHttpConfig; +import com.yahoo.container.core.identity.IdentityConfig; import com.yahoo.container.jdisc.ContainerMbusConfig; import com.yahoo.container.jdisc.JdiscBindingsConfig; import com.yahoo.search.config.QrStartConfig; @@ -48,7 +49,8 @@ public class Container extends AbstractService implements ComponentsConfig.Producer, JdiscBindingsConfig.Producer, ContainerHttpConfig.Producer, - ContainerMbusConfig.Producer { + ContainerMbusConfig.Producer, + IdentityConfig.Producer { public static final int BASEPORT = Defaults.getDefaults().vespaWebServicePort(); public static final String SINGLENODE_CONTAINER_SERVICESPEC = "default_singlenode_container"; @@ -394,6 +396,18 @@ public class Container extends AbstractService implements this.httpServerEnabled = httpServerEnabled; } + @Override + public void getConfig(IdentityConfig.Builder builder) { + if(parent instanceof ContainerCluster) { + ContainerCluster containerCluster = (ContainerCluster) parent; + Optional<Identity> identity = containerCluster.getIdentity(); + identity.ifPresent(id -> { + builder.serviceName(id.getService()); + builder.domain(id.getDomain()); + }); + } + } + public static final class PortOverride { public final ComponentSpecification serverId; public final int port; diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java index 77029360593..2437562c56a 100755 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ContainerCluster.java @@ -7,11 +7,11 @@ import com.yahoo.cloud.config.RoutingProviderConfig; import com.yahoo.component.ComponentId; import com.yahoo.component.ComponentSpecification; import com.yahoo.config.FileReference; +import com.yahoo.config.application.api.ApplicationMetaData; +import com.yahoo.config.application.api.ComponentInfo; import com.yahoo.config.application.api.DeploymentSpec; import com.yahoo.config.docproc.DocprocConfig; import com.yahoo.config.docproc.SchemamappingConfig; -import com.yahoo.config.application.api.ApplicationMetaData; -import com.yahoo.config.application.api.ComponentInfo; import com.yahoo.config.model.ApplicationConfigProducerRoot; import com.yahoo.config.model.producer.AbstractConfigProducer; import com.yahoo.config.model.producer.AbstractConfigProducerRoot; @@ -65,8 +65,8 @@ import com.yahoo.vespa.model.container.component.ConfigProducerGroup; import com.yahoo.vespa.model.container.component.DiscBindingsConfigGenerator; import com.yahoo.vespa.model.container.component.FileStatusHandlerComponent; import com.yahoo.vespa.model.container.component.Handler; -import com.yahoo.vespa.model.container.component.SimpleComponent; import com.yahoo.vespa.model.container.component.Servlet; +import com.yahoo.vespa.model.container.component.SimpleComponent; import com.yahoo.vespa.model.container.component.StatisticsComponent; import com.yahoo.vespa.model.container.component.chain.ProcessingHandler; import com.yahoo.vespa.model.container.docproc.ContainerDocproc; @@ -82,7 +82,6 @@ import com.yahoo.vespa.model.content.Content; import com.yahoo.vespa.model.search.AbstractSearchCluster; import com.yahoo.vespa.model.utils.FileSender; import com.yahoo.vespaclient.config.FeederConfig; - import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -187,6 +186,8 @@ public final class ContainerCluster private Optional<String> hostClusterId = Optional.empty(); private Optional<Integer> memoryPercentage = Optional.empty(); + private Identity identity; + private static class AcceptAllVerifier implements ContainerClusterVerifier { @Override public boolean acceptComponent(Component component) { return true; } @@ -239,6 +240,9 @@ public final class ContainerCluster public void setZone(Zone zone) { this.zone = zone; } + public Zone getZone() { + return zone; + } public void addMetricStateHandler() { Handler<AbstractConfigProducer<?>> stateHandler = new Handler<>( @@ -837,6 +841,14 @@ public final class ContainerCluster */ public Optional<Integer> getMemoryPercentage() { return memoryPercentage; } + public Optional<Identity> getIdentity() { + return Optional.ofNullable(identity); + } + + public void setIdentity(Identity identity) { + this.identity = identity; + } + @Override public String toString() { return "container cluster '" + getName() + "'"; diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/Identity.java b/config-model/src/main/java/com/yahoo/vespa/model/container/Identity.java new file mode 100644 index 00000000000..e872cb7a73b --- /dev/null +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/Identity.java @@ -0,0 +1,22 @@ +package com.yahoo.vespa.model.container; + +/** + * @author mortent + */ +public class Identity { + private final String domain; + private final String service; + + public Identity(String domain, String service) { + this.domain = domain; + this.service = service; + } + + public String getDomain() { + return domain; + } + + public String getService() { + return service; + } +} diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/configserver/ConfigserverCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/configserver/ConfigserverCluster.java index c3361426d64..f7caa0c734c 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/configserver/ConfigserverCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/configserver/ConfigserverCluster.java @@ -141,6 +141,9 @@ public class ConfigserverCluster extends AbstractConfigProducer if (options.dockerVespaBaseImage().isPresent()) { builder.dockerVespaBaseImage(options.dockerVespaBaseImage().get()); } + if (options.serviceProviderEndpoint().isPresent()) { + builder.serviceProviderEndpoint(options.serviceProviderEndpoint().get()); + } } private String[] getConfigModelPluginDirs() { diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/configserver/option/CloudConfigOptions.java b/config-model/src/main/java/com/yahoo/vespa/model/container/configserver/option/CloudConfigOptions.java index 8acab12414d..ae29a912c11 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/configserver/option/CloudConfigOptions.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/configserver/option/CloudConfigOptions.java @@ -43,4 +43,5 @@ public interface CloudConfigOptions { Optional<Integer> numParallelTenantLoaders(); Optional<String> dockerRegistry(); Optional<String> dockerVespaBaseImage(); + Optional<String> serviceProviderEndpoint(); } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java index 49229fcc87a..0d221b57748 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java @@ -4,16 +4,16 @@ package com.yahoo.vespa.model.container.xml; import com.google.common.collect.ImmutableList; import com.yahoo.component.Version; import com.yahoo.config.application.Xml; -import com.yahoo.config.model.ConfigModelContext; import com.yahoo.config.application.api.ApplicationPackage; import com.yahoo.config.application.api.DeployLogger; +import com.yahoo.config.model.ConfigModelContext; import com.yahoo.config.model.application.provider.IncludeDirs; import com.yahoo.config.model.builder.xml.ConfigModelBuilder; import com.yahoo.config.model.builder.xml.ConfigModelId; import com.yahoo.config.model.producer.AbstractConfigProducer; import com.yahoo.config.provision.Capacity; -import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.ClusterMembership; +import com.yahoo.config.provision.ClusterSpec; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.NodeType; import com.yahoo.container.jdisc.config.MetricDefaultsConfig; @@ -22,14 +22,15 @@ import com.yahoo.text.XML; import com.yahoo.vespa.defaults.Defaults; import com.yahoo.vespa.model.AbstractService; import com.yahoo.vespa.model.HostResource; +import com.yahoo.vespa.model.container.Identity; import com.yahoo.vespa.model.builder.xml.dom.DomClientProviderBuilder; import com.yahoo.vespa.model.builder.xml.dom.DomComponentBuilder; import com.yahoo.vespa.model.builder.xml.dom.DomFilterBuilder; import com.yahoo.vespa.model.builder.xml.dom.DomHandlerBuilder; import com.yahoo.vespa.model.builder.xml.dom.ModelElement; import com.yahoo.vespa.model.builder.xml.dom.NodesSpecification; -import com.yahoo.vespa.model.builder.xml.dom.VespaDomBuilder; import com.yahoo.vespa.model.builder.xml.dom.ServletBuilder; +import com.yahoo.vespa.model.builder.xml.dom.VespaDomBuilder; import com.yahoo.vespa.model.builder.xml.dom.chains.docproc.DomDocprocChainsBuilder; import com.yahoo.vespa.model.builder.xml.dom.chains.processing.DomProcessingBuilder; import com.yahoo.vespa.model.builder.xml.dom.chains.search.DomSearchChainsBuilder; @@ -52,12 +53,15 @@ import com.yahoo.vespa.model.container.search.QueryProfiles; import com.yahoo.vespa.model.container.search.SemanticRules; import com.yahoo.vespa.model.container.search.searchchain.SearchChains; import com.yahoo.vespa.model.container.xml.document.DocumentFactoryBuilder; - import com.yahoo.vespa.model.content.StorageGroup; import org.w3c.dom.Element; import org.w3c.dom.Node; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -108,7 +112,6 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { ContainerCluster cluster = createContainerCluster(spec, modelContext); addClusterContent(cluster, spec, modelContext); addBundlesForPlatformComponents(cluster); - model.setCluster(cluster); } @@ -157,6 +160,9 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { addServerProviders(spec, cluster); addLegacyFilters(spec, cluster); // TODO: Remove for Vespa 7 + // Athenz copper argos + addIdentity(spec, cluster); + //TODO: overview handler, see DomQrserverClusterBuilder } @@ -683,6 +689,16 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { } } + private void addIdentity(Element element, ContainerCluster cluster) { + Element identityElement = XML.getChild(element, "identity"); + if(identityElement != null) { + String domain = XML.getValue(XML.getChild(identityElement, "domain")); + String service = XML.getValue(XML.getChild(identityElement, "service")); + Identity identity = new Identity(domain.trim(), service.trim()); + cluster.setIdentity(identity); + } + } + /** * Disallow renderers named "DefaultRenderer" or "JsonRenderer" */ diff --git a/config-model/src/main/resources/schema/containercluster.rnc b/config-model/src/main/resources/schema/containercluster.rnc index 85219bdb46a..af9b89b8553 100644 --- a/config-model/src/main/resources/schema/containercluster.rnc +++ b/config-model/src/main/resources/schema/containercluster.rnc @@ -7,7 +7,8 @@ ContainerCluster = element container | jdisc { ContainerServices & DocumentBinding* & Aliases? & - NodesOfContainerCluster? + NodesOfContainerCluster? & + Identity? } ContainerServices = @@ -225,3 +226,7 @@ DocumentBinding = element document { attribute bundle { xsd:NCName } } +Identity = element identity { + element domain { xsd:NCName } & + element service { xsd:NCName } +} diff --git a/config-model/src/test/java/com/yahoo/vespa/model/admin/DedicatedAdminV4Test.java b/config-model/src/test/java/com/yahoo/vespa/model/admin/DedicatedAdminV4Test.java index e2ed57251d9..4c09fd8d713 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/admin/DedicatedAdminV4Test.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/admin/DedicatedAdminV4Test.java @@ -62,6 +62,10 @@ public class DedicatedAdminV4Test { " <metric name='nginx.upstreams.down.last' output-name='nginx.upstreams.down'/>" + " </consumer>" + " </metric-consumers>" + + " <identity>" + + " <domain>mydomain</domain>" + + " <service>myservice</service>" + + " </identity>" + " </admin>" + "</services>"; diff --git a/config-model/src/test/schema-test-files/services.xml b/config-model/src/test/schema-test-files/services.xml index bf2a924e62d..380ce7f5a3d 100644 --- a/config-model/src/test/schema-test-files/services.xml +++ b/config-model/src/test/schema-test-files/services.xml @@ -36,6 +36,10 @@ </config> <jdisc id='qrsCluster_1' version='1.0'> + <identity> + <domain>mydomain</domain> + <service>myservice</service> + </identity> <rest-api path="jersey1"> <components bundle="my-bundle" /> <components bundle="other-bundle"> diff --git a/configdefinitions/src/vespa/configserver.def b/configdefinitions/src/vespa/configserver.def index 28081f8323c..346d4b9c862 100644 --- a/configdefinitions/src/vespa/configserver.def +++ b/configdefinitions/src/vespa/configserver.def @@ -40,3 +40,6 @@ useVespaVersionInRequest bool default=false # Docker config dockerRegistry string default="" dockerVespaBaseImage string default="" + +# Athenz config +serviceProviderEndpoint string default=""
\ No newline at end of file diff --git a/configserver/src/main/resources/configserver-app/services.xml b/configserver/src/main/resources/configserver-app/services.xml index 7aec27a96f5..db95cb17530 100644 --- a/configserver/src/main/resources/configserver-app/services.xml +++ b/configserver/src/main/resources/configserver-app/services.xml @@ -134,5 +134,7 @@ <server port="19071" id="configserver" /> <preprocess:include file='hosted-vespa/http-server.xml' required='false' /> </http> + + <preprocess:include file='athenz-identity-provider.xml' required='false' /> </jdisc> </services> diff --git a/container-core/src/main/java/com/yahoo/container/core/identity/package-info.java b/container-core/src/main/java/com/yahoo/container/core/identity/package-info.java new file mode 100644 index 00000000000..f0d45f003c2 --- /dev/null +++ b/container-core/src/main/java/com/yahoo/container/core/identity/package-info.java @@ -0,0 +1,7 @@ +/** + * @author mortent + */ +@ExportPackage +package com.yahoo.container.core.identity; + +import com.yahoo.osgi.annotation.ExportPackage;
\ No newline at end of file diff --git a/container-core/src/main/resources/configdefinitions/identity.def b/container-core/src/main/resources/configdefinitions/identity.def new file mode 100644 index 00000000000..0f5080632b2 --- /dev/null +++ b/container-core/src/main/resources/configdefinitions/identity.def @@ -0,0 +1,4 @@ +namespace=container.core.identity + +domain string +serviceName string diff --git a/container-disc/pom.xml b/container-disc/pom.xml index 44afe74459d..5409d9b1a40 100644 --- a/container-disc/pom.xml +++ b/container-disc/pom.xml @@ -130,6 +130,17 @@ <scope>provided</scope> </dependency> <!-- end WARNING --> + <!-- Athenz --> + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpclient</artifactId> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>com.yahoo.athenz</groupId> + <artifactId>athenz-auth-core</artifactId> + <version>${athenz.version}</version> + </dependency> </dependencies> <properties> <!-- These versions must be the ones used by the current jersey version (see vespa_jersey2/target/dependency). diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java new file mode 100644 index 00000000000..f67afdfc458 --- /dev/null +++ b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java @@ -0,0 +1,121 @@ +package com.yahoo.container.jdisc.athenz; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.inject.Inject; +import com.yahoo.athenz.auth.util.Crypto; +import com.yahoo.cloud.config.ConfigserverConfig; +import com.yahoo.component.AbstractComponent; +import com.yahoo.container.core.identity.IdentityConfig; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.operator.OperatorCreationException; + +import java.io.IOException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; + +/** + * @author mortent + */ +public final class AthenzIdentityProvider extends AbstractComponent { + + private InstanceIdentity instanceIdentity; + + private final String athenzUrl; + + private final String dnsSuffix; + private final String providerUniqueId; + private final String providerServiceName; + + @Inject + public AthenzIdentityProvider(IdentityConfig config, ConfigserverConfig configserverConfig) throws IOException { + this(config, new ServiceProviderApi(configserverConfig.serviceProviderEndpoint()), new AthenzService()); + } + + // Test only + public AthenzIdentityProvider(IdentityConfig config, ServiceProviderApi serviceProviderApi, AthenzService athenzService) throws IOException { + KeyPair keyPair = createKeyPair(); + String signedIdentityDocument = serviceProviderApi.getSignedIdentityDocument(); + this.athenzUrl = getZtsEndpoint(signedIdentityDocument); + dnsSuffix = getDnsSuffix(signedIdentityDocument); + providerUniqueId = getProviderUniqueId(signedIdentityDocument); + providerServiceName = getProviderServiceName(signedIdentityDocument); + + InstanceRegisterInformation instanceRegisterInformation = new InstanceRegisterInformation( + providerServiceName, + config.domain(), + config.serviceName(), + signedIdentityDocument, + createCSR(keyPair, config), + true + ); + instanceIdentity = athenzService.sendInstanceRegisterRequest(instanceRegisterInformation, athenzUrl); + } + + private String getProviderUniqueId(String signedIdentityDocument) throws IOException { + return getJsonNode(signedIdentityDocument, "provider-unique-id"); + } + + private String getDnsSuffix(String signedIdentityDocument) throws IOException { + return getJsonNode(signedIdentityDocument, "dns-suffix"); + } + + private String getProviderServiceName(String signedIdentityDocument) throws IOException { + return getJsonNode(signedIdentityDocument, "provider-service"); + } + + private String getZtsEndpoint(String signedIdentityDocument) throws IOException { + return getJsonNode(signedIdentityDocument, "zts-endpoint"); + } + + private String getJsonNode(String jsonString, String path) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = mapper.readTree(jsonString); + return jsonNode.get(path).asText(); + } + + private KeyPair createKeyPair() { + try { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + return kpg.generateKeyPair(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + private String createCSR(KeyPair keyPair, IdentityConfig identityConfig) throws IOException { + + try { + // Add SAN dnsname <service>.<domain-with-dashes>.<provider-dnsname-suffix> + // and SAN dnsname <provider-unique-instance-id>.instanceid.athenz.<provider-dnsname-suffix> + GeneralName[] sanDnsNames = new GeneralName[]{ + new GeneralName(GeneralName.dNSName, String.format("%s.%s.%s", + identityConfig.serviceName(), + identityConfig.domain().replace(".", "-"), + dnsSuffix)), + new GeneralName(GeneralName.dNSName, String.format("%s.instanceid.athenz.%s", + providerUniqueId, + dnsSuffix)) + }; + + return Crypto.generateX509CSR(keyPair.getPrivate(), + keyPair.getPublic(), + String.format("CN=%s.%s", identityConfig.domain(), identityConfig.serviceName()), + sanDnsNames); + } catch (OperatorCreationException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + public String getNToken() { + return instanceIdentity.getServiceToken(); + } + + public String getX509Cert() { + return instanceIdentity.getX509Certificate(); + } +} + diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzService.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzService.java new file mode 100644 index 00000000000..2acd630aa7d --- /dev/null +++ b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzService.java @@ -0,0 +1,47 @@ +package com.yahoo.container.jdisc.athenz; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.eclipse.jetty.http.HttpStatus; + +import java.io.IOException; + +/** + * @author mortent + */ +public class AthenzService { + + /** + * Send instance register request to ZTS, get InstanceIdentity + * + * @param instanceRegisterInformation + */ + InstanceIdentity sendInstanceRegisterRequest(InstanceRegisterInformation instanceRegisterInformation, String athenzUrl) { + try(CloseableHttpClient client = HttpClientBuilder.create().build()) { + ObjectMapper objectMapper = new ObjectMapper(); + System.out.println(objectMapper.writeValueAsString(instanceRegisterInformation)); + HttpUriRequest postRequest = RequestBuilder.post() + .setUri(athenzUrl + "/instance") + .setEntity(new StringEntity(objectMapper.writeValueAsString(instanceRegisterInformation), ContentType.APPLICATION_JSON)) + .build(); + CloseableHttpResponse response = client.execute(postRequest); + if(HttpStatus.isSuccess(response.getStatusLine().getStatusCode())) { + return objectMapper.readValue(response.getEntity().getContent(), InstanceIdentity.class); + } else { + String s = EntityUtils.toString(response.getEntity()); + System.out.println("s = " + s); + throw new RuntimeException(response.toString()); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/InstanceIdentity.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/InstanceIdentity.java new file mode 100644 index 00000000000..e2b65685cdb --- /dev/null +++ b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/InstanceIdentity.java @@ -0,0 +1,47 @@ +package com.yahoo.container.jdisc.athenz; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Map; + +/** + * Used for deserializing response from ZTS + * + * @author mortent + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +class InstanceIdentity { + @JsonProperty("attributes") + Map<String, String> attributes; + @JsonProperty("provider") + private String provider; + @JsonProperty("name") + private String name; + @JsonProperty("instanceId") + private String instanceId; + @JsonProperty("x509Certificate") + private String x509Certificate; + @JsonProperty("x509CertificateSigner") + private String x509CertificateSigner; + @JsonProperty("sshCertificate") + private String sshCertificate; + @JsonProperty("sshCertificateSigner") + private String sshCertificateSigner; + @JsonProperty("serviceToken") + private String serviceToken; + + public String getX509Certificate() { + return x509Certificate; + } + + public String getServiceToken() { + return serviceToken; + } + + public void setServiceToken(String serviceToken) { + this.serviceToken = serviceToken; + } +} diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/InstanceRegisterInformation.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/InstanceRegisterInformation.java new file mode 100644 index 00000000000..2fa3c598c58 --- /dev/null +++ b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/InstanceRegisterInformation.java @@ -0,0 +1,38 @@ +package com.yahoo.container.jdisc.athenz; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Used for serializing request to ZTS + * + * @author mortent + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +class InstanceRegisterInformation { + @JsonProperty("provider") + private final String provider; + @JsonProperty("domain") + private final String domain; + @JsonProperty("service") + private final String service; + @JsonProperty("attestationData") + private final String attestationData; + @JsonProperty("ssh") + private final String ssh = null; // Not needed + @JsonProperty("csr") + private final String csr; + @JsonProperty("token") + private final boolean token; + + public InstanceRegisterInformation(String provider, String domain, String service, String attestationData, String csr, boolean token) { + this.provider = provider; + this.domain = domain; + this.service = service; + this.attestationData = attestationData; + this.csr = csr; + this.token = token; + } +} diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/ServiceProviderApi.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/ServiceProviderApi.java new file mode 100644 index 00000000000..0b417a4d440 --- /dev/null +++ b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/ServiceProviderApi.java @@ -0,0 +1,47 @@ +package com.yahoo.container.jdisc.athenz; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.eclipse.jetty.http.HttpStatus; + +import java.io.IOException; + +/** + * @author mortent + */ +public class ServiceProviderApi { + + private final String providerEndpoint; + + public ServiceProviderApi(String providerEndpoint) { + this.providerEndpoint = providerEndpoint; + } + + + /** + * Get signed identity document from config server + * + * @return + */ + String getSignedIdentityDocument() { + + // TODO Use client side auth to establish trusted secure channel + try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) { + + CloseableHttpResponse idDocResponse = httpClient.execute(RequestBuilder.get().setUri(providerEndpoint + "/identity-document").build()); + if (HttpStatus.isSuccess(idDocResponse.getStatusLine().getStatusCode())) { + return EntityUtils.toString(idDocResponse.getEntity()); + } else { + // make sure we have retried a few times (AND logged) before giving up + throw new RuntimeException("Failed to initialize Athenz instance provider"); + } + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + +} diff --git a/standalone-container/src/main/scala/com/yahoo/container/standalone/CloudConfigYinstVariables.scala b/standalone-container/src/main/scala/com/yahoo/container/standalone/CloudConfigYinstVariables.scala index 0a888dc1208..009de6fd541 100644 --- a/standalone-container/src/main/scala/com/yahoo/container/standalone/CloudConfigYinstVariables.scala +++ b/standalone-container/src/main/scala/com/yahoo/container/standalone/CloudConfigYinstVariables.scala @@ -38,6 +38,7 @@ class CloudConfigYinstVariables extends CloudConfigOptions { override val numParallelTenantLoaders = optionalYinstVar[java.lang.Integer]("num_parallel_tenant_loaders") override val dockerRegistry = optionalYinstVar[java.lang.String]("docker_registry") override val dockerVespaBaseImage = optionalYinstVar[java.lang.String]("docker_vespa_base_image") + override val serviceProviderEndpoint = optionalYinstVar[java.lang.String]("service_provider_endpoint") } object CloudConfigYinstVariables { |