summaryrefslogtreecommitdiffstats
path: root/athenz-identity-provider-service
diff options
context:
space:
mode:
authorMorten Tokle <mortent@oath.com>2017-10-23 11:15:16 +0200
committerMorten Tokle <mortent@oath.com>2017-10-23 11:21:26 +0200
commit4cd5e7ca38c4f8cc752e4c0d0a97c83c8f27863f (patch)
tree1bb4ab06f3adaf9df839c0e016127686f12c95c2 /athenz-identity-provider-service
parentce5b8db39f64101d7ac9fae2847f3db614f14638 (diff)
Add fields to signed identity document
Diffstat (limited to 'athenz-identity-provider-service')
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderService.java10
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentGenerator.java18
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/InstanceValidator.java2
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/model/ProviderUniqueId.java4
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/model/SignedIdentityDocument.java12
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderServiceTest.java115
6 files changed, 139 insertions, 22 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 e862717d1d1..301d6250b31 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
@@ -58,7 +58,7 @@ public class AthenzInstanceProviderService extends AbstractComponent {
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, keyProvider, sslContextFactory,
nodeRepository, zone);
AthenzCertificateUpdater reloader = new AthenzCertificateUpdater(
sslContextFactory, keyProvider, config);
@@ -70,20 +70,20 @@ public class AthenzInstanceProviderService extends AbstractComponent {
}
}
- private static Server createJettyServer(int port, String apiPath,
+ private static Server createJettyServer(AthenzProviderServiceConfig config,
KeyProvider keyProvider,
SslContextFactory sslContextFactory,
NodeRepository nodeRepository,
Zone zone) {
Server server = new Server();
ServerConnector connector = new ServerConnector(server, sslContextFactory);
- connector.setPort(port);
+ connector.setPort(config.port());
server.addConnector(connector);
ServletHandler handler = new ServletHandler();
ProviderServiceServlet providerServiceServlet =
- new ProviderServiceServlet(new InstanceValidator(keyProvider), new IdentityDocumentGenerator(nodeRepository, zone, keyProvider));
- handler.addServletWithMapping(new ServletHolder(providerServiceServlet), apiPath);
+ new ProviderServiceServlet(new InstanceValidator(keyProvider), new IdentityDocumentGenerator(config, nodeRepository, zone, keyProvider));
+ handler.addServletWithMapping(new ServletHolder(providerServiceServlet), config.apiPath());
handler.addServletWithMapping(StatusServlet.class, "/status.html");
server.setHandler(handler);
return server;
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
index 833f1348338..9e01b0e6421 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/impl/IdentityDocumentGenerator.java
@@ -2,6 +2,7 @@ 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.identityproviderservice.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;
@@ -22,11 +23,17 @@ public class IdentityDocumentGenerator {
private final NodeRepository nodeRepository;
private final Zone zone;
private final KeyProvider keyProvider;
+ private final String dnsSuffix;
+ private final String providerService;
+ private final String ztsUrl;
- public IdentityDocumentGenerator(NodeRepository nodeRepository, Zone zone, KeyProvider keyProvider) {
+ public IdentityDocumentGenerator(AthenzProviderServiceConfig config, NodeRepository nodeRepository, Zone zone, KeyProvider keyProvider) {
this.nodeRepository = nodeRepository;
this.zone = zone;
this.keyProvider = keyProvider;
+ this.dnsSuffix = config.certDnsSuffix();
+ this.providerService = config.serviceName();
+ this.ztsUrl = config.ztsUrl();
}
public String generateSignedIdentityDocument(String hostname) {
@@ -49,16 +56,20 @@ public class IdentityDocumentGenerator {
encodedIdentityDocument,
signature,
SignedIdentityDocument.DEFAULT_KEY_VERSION,
+ identityDocument.providerUniqueId.asString(),
+ dnsSuffix,
+ providerService,
+ ztsUrl,
SignedIdentityDocument.DEFAILT_DOCUMENT_VERSION
);
return Utils.getMapper().writeValueAsString(signedIdentityDocument);
} catch (Exception e) {
- throw new RuntimeException("Exception generating identity document: " + e.getMessage());
+ throw new RuntimeException("Exception generating identity document: " + e.getMessage(), e);
}
}
private IdentityDocument generateIdDocument(Node node) {
- Allocation allocation = node.allocation().get();
+ Allocation allocation = node.allocation().orElseThrow(() -> new RuntimeException("No allocation for node " + node.hostname()));
ProviderUniqueId providerUniqueId = new ProviderUniqueId(
allocation.owner().tenant().value(),
allocation.owner().application().value(),
@@ -74,6 +85,5 @@ public class IdentityDocumentGenerator {
node.hostname(),
Instant.now());
}
-
}
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/impl/InstanceValidator.java
index da8a4afebd8..f5c2c319041 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/impl/InstanceValidator.java
@@ -43,7 +43,7 @@ public class InstanceValidator {
return false;
}
- private 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/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 ec699120802..810c75ef0c5 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
@@ -41,6 +41,10 @@ public class ProviderUniqueId {
this.clusterIndex = clusterIndex;
}
+ public String asString() {
+ return String.format("%s.%s.%s.%s.%s.%s.%d", tenant, application, environment, region, instance, clusterId, clusterIndex);
+ }
+
@Override
public String toString() {
return "ProviderUniqueId{" +
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 09d57582fa3..37f94d48a95 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
@@ -23,17 +23,29 @@ public class SignedIdentityDocument {
@JsonIgnore public final IdentityDocument identityDocument;
@JsonProperty("signature") public final String signature;
@JsonProperty("signing-key-version") public final int signingKeyVersion;
+ @JsonProperty("provider-unique-id") public final String providerUniqueId; // String representation
+ @JsonProperty("dns-suffix") public final String dnsSuffix;
+ @JsonProperty("provider-service") public final String providerService;
+ @JsonProperty("zts-endpoint") public final String ztsEndpoint;
@JsonProperty("document-version") public final int documentVersion;
@JsonCreator
public SignedIdentityDocument(@JsonProperty("identity-document") String rawIdentityDocument,
@JsonProperty("signature") String signature,
@JsonProperty("signing-key-version") int signingKeyVersion,
+ @JsonProperty("provider-unique-id") String providerUniqueId,
+ @JsonProperty("dns-suffix") String dnsSuffix,
+ @JsonProperty("provider-service") String providerService,
+ @JsonProperty("zts-endpoint") String ztsEndpoint,
@JsonProperty("document-version") int documentVersion) {
this.rawIdentityDocument = rawIdentityDocument;
this.identityDocument = parseIdentityDocument(rawIdentityDocument);
this.signature = signature;
this.signingKeyVersion = signingKeyVersion;
+ this.providerUniqueId = providerUniqueId;
+ this.dnsSuffix = dnsSuffix;
+ this.providerService = providerService;
+ this.ztsEndpoint = ztsEndpoint;
this.documentVersion = documentVersion;
}
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 900bdec6855..125f8a3cb0f 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
@@ -3,19 +3,34 @@ package com.yahoo.vespa.hosted.athenz.instanceproviderservice;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableSet;
import com.yahoo.athenz.auth.util.Crypto;
+import com.yahoo.component.Version;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.ApplicationName;
+import com.yahoo.config.provision.ClusterMembership;
import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.Flavor;
+import com.yahoo.config.provision.InstanceName;
+import com.yahoo.config.provision.NodeType;
import com.yahoo.config.provision.RegionName;
+import com.yahoo.config.provision.TenantName;
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.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 com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.NodeRepository;
+import com.yahoo.vespa.hosted.provision.node.Allocation;
+import com.yahoo.vespa.hosted.provision.node.Generation;
+import com.yahoo.vespa.hosted.provision.testutils.MockNodeFlavors;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
@@ -27,14 +42,19 @@ 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.openssl.jcajce.JcaPEMWriter;
import org.junit.Ignore;
import org.junit.Test;
import javax.net.ssl.SSLContext;
import java.io.IOException;
+import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
+import java.security.Key;
import java.security.KeyManagementException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
@@ -42,13 +62,18 @@ import java.security.Signature;
import java.security.SignatureException;
import java.time.Instant;
import java.util.Base64;
+import java.util.HashSet;
+import java.util.Optional;
import java.util.logging.Logger;
import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
/**
* @author bjorncs
@@ -66,17 +91,7 @@ public class AthenzInstanceProviderServiceTest {
DummyKeyProvider keyProvider = new DummyKeyProvider();
PrivateKey privateKey = Crypto.loadPrivateKey(keyProvider.getPrivateKey(0));
- AthenzProviderServiceConfig config =
- new AthenzProviderServiceConfig(
- new AthenzProviderServiceConfig.Builder()
- .domain(domain)
- .serviceName(service)
- .port(PORT)
- .keyPathPrefix("dummy-path")
- .certDnsSuffix("INSERT DNS SUFFIX HERE")
- .ztsUrl("INSERT ZTS URL HERE")
- .athenzPrincipalHeaderName("INSERT PRINCIPAL HEADER NAME HERE")
- .apiPath("/"));
+ AthenzProviderServiceConfig config = getAthenzProviderConfig(domain, service, "INSERT ZTS URL HERE", "INSERT DNS SUFFIX HERE");
ScheduledExecutorServiceMock executor = new ScheduledExecutorServiceMock();
NodeRepository nodeRepository = mock(NodeRepository.class);
@@ -97,6 +112,49 @@ public class AthenzInstanceProviderServiceTest {
}
}
+ @Test
+ public void generates_valid_identity_document() throws IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
+ String hostname = "x.y.com";
+ AutoGeneratedKeyProvider keyProvider = new AutoGeneratedKeyProvider();
+ AthenzProviderServiceConfig config = getAthenzProviderConfig("domain", "service", "localhost/zts", "dnsSuffix");
+
+ NodeRepository nodeRepository = mock(NodeRepository.class);
+ MockNodeFlavors nodeFlavors = new MockNodeFlavors();
+ ApplicationId appid = ApplicationId.from(TenantName.from("tenant"), ApplicationName.from("application"), InstanceName.from("default"));
+ Allocation allocation = new Allocation(appid, ClusterMembership.from("container/default/0/0", Version.fromString("1.2.3")), Generation.inital(), false); Flavor flavor = nodeFlavors.getFlavorOrThrow("default");
+ Node n = Node.create("ostkid", ImmutableSet.of("127.0.0.1"), new HashSet<>(), hostname, Optional.empty(), flavor, NodeType.tenant).with(allocation);
+ when(nodeRepository.getNode(eq(hostname))).thenReturn(Optional.of(n));
+ Zone zone = new Zone(Environment.dev, RegionName.from("us-north-1"));
+
+ IdentityDocumentGenerator identityDocumentGenerator = new IdentityDocumentGenerator(config, nodeRepository, zone, keyProvider);
+ String rawSignedIdentityDocument = identityDocumentGenerator.generateSignedIdentityDocument(hostname);
+
+
+ SignedIdentityDocument signedIdentityDocument = Utils.getMapper().readValue(rawSignedIdentityDocument, SignedIdentityDocument.class);
+
+ // Verify attributes
+ assertEquals(hostname, signedIdentityDocument.identityDocument.instanceHostname);
+ ProviderUniqueId expectedProviderUniqueId = new ProviderUniqueId("tenant", "application", "dev", "us-north-1", "default", "default", 0);
+ assertEquals(expectedProviderUniqueId, signedIdentityDocument.identityDocument.providerUniqueId);
+
+ // Validate signature
+ assertTrue("Message", InstanceValidator.isSignatureValid(Crypto.loadPublicKey(keyProvider.getPublicKey(0)), signedIdentityDocument.rawIdentityDocument, signedIdentityDocument.signature));
+
+ }
+
+ private AthenzProviderServiceConfig getAthenzProviderConfig(String domain, String service, String ztsUrl, String dnsSuffix) {
+ return new AthenzProviderServiceConfig(
+ new AthenzProviderServiceConfig.Builder()
+ .domain(domain)
+ .serviceName(service)
+ .port(PORT)
+ .keyPathPrefix("dummy-path")
+ .certDnsSuffix(dnsSuffix)
+ .ztsUrl(ztsUrl)
+ .athenzPrincipalHeaderName("INSERT PRINCIPAL HEADER NAME HERE")
+ .apiPath("/"));
+
+ }
private static boolean getStatus(HttpClient client) {
try {
HttpResponse response = client.execute(new HttpGet("https://localhost:" + PORT + "/status.html"));
@@ -143,7 +201,7 @@ public class AthenzInstanceProviderServiceTest {
InstanceConfirmation instanceConfirmation = new InstanceConfirmation(
"provider", "domain", "service",
- new SignedIdentityDocument(encodedIdentityDocument, signature, 0, 1));
+ new SignedIdentityDocument(encodedIdentityDocument, signature, 0, identityDocument.providerUniqueId.asString(), "dnssuffix", "service", "localhost/zts",1));
return new StringEntity(mapper.writeValueAsString(instanceConfirmation));
} catch (JsonProcessingException
| NoSuchAlgorithmException
@@ -166,4 +224,37 @@ public class AthenzInstanceProviderServiceTest {
return "INSERT PUB KEY";
}
}
+
+ private static class AutoGeneratedKeyProvider implements KeyProvider {
+
+ private final String publicKey;
+ private final String privateKey;
+
+ public AutoGeneratedKeyProvider() throws IOException, NoSuchAlgorithmException {
+ KeyPairGenerator rsa = KeyPairGenerator.getInstance("RSA");
+ rsa.initialize(2048);
+ KeyPair keyPair = rsa.genKeyPair();
+ publicKey = pemEncode("RSA PUBLIC KEY", keyPair.getPublic());
+ privateKey = pemEncode("RSA PRIVATE KEY", keyPair.getPrivate());
+ }
+
+ private String pemEncode(String description, Key key) throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter);
+ pemWriter.writeObject(key);
+ pemWriter.flush();
+ return stringWriter.toString();
+
+ }
+
+ @Override
+ public String getPrivateKey(int version) {
+ return privateKey;
+ }
+
+ @Override
+ public String getPublicKey(int version) {
+ return publicKey;
+ }
+ }
}