diff options
Diffstat (limited to 'athenz-identity-provider-service/src')
7 files changed, 156 insertions, 46 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 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()); |