summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java20
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java30
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java13
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java23
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSigner.java85
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/SignatureAlgorithm.java3
-rw-r--r--vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java50
7 files changed, 154 insertions, 70 deletions
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java
index 5fff85f695d..0abbb5a64f5 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGenerator.java
@@ -5,11 +5,11 @@ import com.google.inject.Inject;
import com.yahoo.config.provision.Zone;
import com.yahoo.net.HostName;
import com.yahoo.vespa.athenz.api.AthenzService;
-import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper;
import com.yahoo.vespa.athenz.identityprovider.api.IdentityDocument;
import com.yahoo.vespa.athenz.identityprovider.api.IdentityType;
import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument;
import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId;
+import com.yahoo.vespa.athenz.identityprovider.client.IdentityDocumentSigner;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.KeyProvider;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils;
@@ -19,9 +19,7 @@ import com.yahoo.vespa.hosted.provision.node.Allocation;
import java.net.URI;
import java.security.PrivateKey;
-import java.security.Signature;
import java.time.Instant;
-import java.util.Base64;
import java.util.HashSet;
import java.util.Set;
@@ -33,6 +31,7 @@ import java.util.Set;
*/
public class IdentityDocumentGenerator {
+ private final IdentityDocumentSigner signer = new IdentityDocumentSigner();
private final NodeRepository nodeRepository;
private final Zone zone;
private final KeyProvider keyProvider;
@@ -53,16 +52,13 @@ public class IdentityDocumentGenerator {
Node node = nodeRepository.getNode(hostname).orElseThrow(() -> new RuntimeException("Unable to find node " + hostname));
try {
IdentityDocument identityDocument = generateIdDocument(node, identityType);
- String identityDocumentString = Utils.getMapper().writeValueAsString(EntityBindingsMapper.toIdentityDocumentEntity(identityDocument));
-
- String encodedIdentityDocument =
- Base64.getEncoder().encodeToString(identityDocumentString.getBytes());
- Signature sigGenerator = Signature.getInstance("SHA512withRSA");
PrivateKey privateKey = keyProvider.getPrivateKey(zoneConfig.secretVersion());
- sigGenerator.initSign(privateKey);
- sigGenerator.update(encodedIdentityDocument.getBytes());
- String signature = Base64.getEncoder().encodeToString(sigGenerator.sign());
+ AthenzService providerService = new AthenzService(zoneConfig.domain(), zoneConfig.serviceName());
+
+ String signature = signer.generateSignature(
+ identityDocument.providerUniqueId(), providerService, identityDocument.configServerHostname(),
+ identityDocument.instanceHostname(), identityDocument.createdAt(), identityDocument.ipAddresses(), identityType, privateKey);
return new SignedIdentityDocument(
identityDocument,
@@ -70,7 +66,7 @@ public class IdentityDocumentGenerator {
SignedIdentityDocument.DEFAULT_KEY_VERSION,
identityDocument.providerUniqueId(),
toZoneDnsSuffix(zone, zoneConfig.certDnsSuffix()),
- new AthenzService(zoneConfig.domain(), zoneConfig.serviceName()),
+ providerService,
URI.create(zoneConfig.ztsUrl()),
SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION,
identityDocument.configServerHostname(),
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java
index 0201c46b253..b75f7d05394 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidator.java
@@ -11,15 +11,10 @@ import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper;
import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument;
import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId;
-import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity;
+import com.yahoo.vespa.athenz.identityprovider.client.IdentityDocumentSigner;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.KeyProvider;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.util.Base64;
import java.util.Optional;
import java.util.logging.Logger;
@@ -35,6 +30,7 @@ public class InstanceValidator {
static final String SERVICE_PROPERTIES_DOMAIN_KEY = "identity.domain";
static final String SERVICE_PROPERTIES_SERVICE_KEY = "identity.service";
+ private final IdentityDocumentSigner signer = new IdentityDocumentSigner();
private final KeyProvider keyProvider;
private final SuperModelProvider superModelProvider;
@@ -55,7 +51,9 @@ public class InstanceValidator {
}
log.log(LogLevel.INFO, () -> String.format("Validating instance %s.", providerUniqueId));
- if (isInstanceSignatureValid(instanceConfirmation)) {
+
+ PublicKey publicKey = keyProvider.getPublicKey(signedIdentityDocument.signingKeyVersion());
+ if (signer.hasValidSignature(signedIdentityDocument, publicKey)) {
log.log(LogLevel.INFO, () -> String.format("Instance %s is valid.", providerUniqueId));
return true;
}
@@ -63,24 +61,6 @@ public class InstanceValidator {
return false;
}
- boolean isInstanceSignatureValid(InstanceConfirmation instanceConfirmation) {
- SignedIdentityDocumentEntity signedIdentityDocument = instanceConfirmation.signedIdentityDocument;
-
- PublicKey publicKey = keyProvider.getPublicKey(signedIdentityDocument.signingKeyVersion);
- return isSignatureValid(publicKey, signedIdentityDocument.rawIdentityDocument, signedIdentityDocument.signature);
- }
-
- public static boolean isSignatureValid(PublicKey publicKey, String rawIdentityDocument, String signature) {
- try {
- Signature signatureVerifier = Signature.getInstance("SHA512withRSA");
- signatureVerifier.initVerify(publicKey);
- signatureVerifier.update(rawIdentityDocument.getBytes());
- return signatureVerifier.verify(Base64.getDecoder().decode(signature));
- } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
- throw new RuntimeException(e);
- }
- }
-
// If/when we dont care about logging exactly whats wrong, this can be simplified
// TODO Use identity type to determine if this check should be performed
boolean isSameIdentityAsInServicesXml(ApplicationId applicationId, String domain, String service) {
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java
index 578c044796f..a1839ec62a2 100644
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/identitydocument/IdentityDocumentGeneratorTest.java
@@ -14,14 +14,12 @@ import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.Zone;
-import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper;
import com.yahoo.vespa.athenz.identityprovider.api.IdentityType;
import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument;
import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId;
-import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity;
+import com.yahoo.vespa.athenz.identityprovider.client.IdentityDocumentSigner;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.AutoGeneratedKeyProvider;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
-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;
@@ -48,7 +46,7 @@ public class IdentityDocumentGeneratorTest {
private static final Zone ZONE = new Zone(SystemName.cd, Environment.dev, RegionName.from("us-north-1"));
@Test
- public void generates_valid_identity_document() throws Exception {
+ public void generates_valid_identity_document() {
String parentHostname = "docker-host";
String containerHostname = "docker-container";
@@ -99,12 +97,9 @@ public class IdentityDocumentGeneratorTest {
// Validate that container ips are present
assertThat(signedIdentityDocument.ipAddresses(), Matchers.containsInAnyOrder("::1"));
- SignedIdentityDocumentEntity signedIdentityDocumentEntity = EntityBindingsMapper.toSignedIdentityDocumentEntity(signedIdentityDocument);
+ IdentityDocumentSigner signer = new IdentityDocumentSigner();
// Validate signature
- assertTrue("Message", InstanceValidator.isSignatureValid(keyProvider.getPublicKey(0),
- signedIdentityDocumentEntity.rawIdentityDocument,
- signedIdentityDocument.signature()));
-
+ assertTrue(signer.hasValidSignature(signedIdentityDocument, keyProvider.getPublicKey(0)));
}
}
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java
index 54411b424eb..04c4d4da51a 100644
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/instanceconfirmation/InstanceValidatorTest.java
@@ -14,8 +14,6 @@ import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper;
import com.yahoo.vespa.athenz.identityprovider.api.bindings.IdentityDocumentEntity;
import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity;
import com.yahoo.vespa.athenz.identityprovider.api.bindings.VespaUniqueInstanceIdEntity;
-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 org.junit.Test;
@@ -49,27 +47,6 @@ public class InstanceValidatorTest {
private final String domain = "domain";
private final String service = "service";
- @Test
- public void valid_signature() throws Exception {
- KeyProvider keyProvider = new AutoGeneratedKeyProvider();
- InstanceValidator instanceValidator = new InstanceValidator(keyProvider, null);
- InstanceConfirmation instanceConfirmation = createInstanceConfirmation(
- keyProvider.getPrivateKey(0), applicationId, domain, service);
-
- assertTrue(instanceValidator.isInstanceSignatureValid(instanceConfirmation));
- }
-
- @Test
- public void invalid_signature() throws Exception {
- KeyProvider keyProvider = new AutoGeneratedKeyProvider();
- InstanceValidator instanceValidator = new InstanceValidator(keyProvider, null);
-
- KeyProvider fakeKeyProvider = new AutoGeneratedKeyProvider();
- InstanceConfirmation instanceConfirmation = createInstanceConfirmation(
- fakeKeyProvider.getPrivateKey(0), applicationId, domain, service);
-
- assertFalse(instanceValidator.isInstanceSignatureValid(instanceConfirmation));
- }
@Test
public void application_does_not_exist() {
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSigner.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSigner.java
new file mode 100644
index 00000000000..b3b5df0e68b
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSigner.java
@@ -0,0 +1,85 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.identityprovider.client;
+
+import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.athenz.identityprovider.api.IdentityType;
+import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument;
+import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId;
+import com.yahoo.vespa.athenz.tls.SignatureAlgorithm;
+
+import java.nio.ByteBuffer;
+import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.time.Instant;
+import java.util.Base64;
+import java.util.Set;
+import java.util.TreeSet;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * Generates and validates the signature for a {@link SignedIdentityDocument}
+ *
+ * @author bjorncs
+ */
+public class IdentityDocumentSigner {
+
+ public String generateSignature(VespaUniqueInstanceId providerUniqueId,
+ AthenzService providerService,
+ String configServerHostname,
+ String instanceHostname,
+ Instant createdAt,
+ Set<String> ipAddresses,
+ IdentityType identityType,
+ PrivateKey privateKey) {
+ try {
+ Signature signer = createSigner();
+ signer.initSign(privateKey);
+ writeToSigner(signer, providerUniqueId, providerService, configServerHostname, instanceHostname, createdAt, ipAddresses, identityType);
+ byte[] signature = signer.sign();
+ return Base64.getEncoder().encodeToString(signature);
+ } catch (GeneralSecurityException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public boolean hasValidSignature(SignedIdentityDocument doc, PublicKey publicKey) {
+ try {
+ Signature signer = createSigner();
+ signer.initVerify(publicKey);
+ writeToSigner(signer, doc.providerUniqueId(), doc.providerService(), doc.configServerHostname(), doc.instanceHostname(), doc.createdAt(), doc.ipAddresses(), doc.identityType());
+ return signer.verify(Base64.getDecoder().decode(doc.signature()));
+ } catch (GeneralSecurityException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static Signature createSigner() throws NoSuchAlgorithmException {
+ return Signature.getInstance(SignatureAlgorithm.SHA512_WITH_RSA.getAlgorithmName());
+ }
+
+ private static void writeToSigner(Signature signer,
+ VespaUniqueInstanceId providerUniqueId,
+ AthenzService providerService,
+ String configServerHostname,
+ String instanceHostname,
+ Instant createdAt,
+ Set<String> ipAddresses,
+ IdentityType identityType) throws SignatureException {
+ signer.update(providerUniqueId.asDottedString().getBytes(UTF_8));
+ signer.update(providerService.getFullName().getBytes(UTF_8));
+ signer.update(configServerHostname.getBytes(UTF_8));
+ signer.update(instanceHostname.getBytes(UTF_8));
+ ByteBuffer timestampAsBuffer = ByteBuffer.allocate(Long.BYTES);
+ timestampAsBuffer.putLong(createdAt.toEpochMilli());
+ signer.update(timestampAsBuffer.array());
+ for (String ipAddress : new TreeSet<>(ipAddresses)) {
+ signer.update(ipAddress.getBytes(UTF_8));
+ }
+ signer.update(identityType.id().getBytes(UTF_8));
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/SignatureAlgorithm.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/SignatureAlgorithm.java
index 3d85a12f714..2f3e2721751 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/SignatureAlgorithm.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/SignatureAlgorithm.java
@@ -5,7 +5,8 @@ package com.yahoo.vespa.athenz.tls;
* @author bjorncs
*/
public enum SignatureAlgorithm {
- SHA256_WITH_RSA("SHA256withRSA");
+ SHA256_WITH_RSA("SHA256withRSA"),
+ SHA512_WITH_RSA("SHA512withRSA");
private final String algorithmName;
diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java
new file mode 100644
index 00000000000..9cc500ee241
--- /dev/null
+++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java
@@ -0,0 +1,50 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.identityprovider.client;
+
+import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.athenz.identityprovider.api.IdentityType;
+import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument;
+import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId;
+import com.yahoo.vespa.athenz.tls.KeyAlgorithm;
+import com.yahoo.vespa.athenz.tls.KeyUtils;
+import org.junit.Test;
+
+import java.net.URI;
+import java.security.KeyPair;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.HashSet;
+
+import static com.yahoo.vespa.athenz.identityprovider.api.IdentityType.TENANT;
+import static com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION;
+import static com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument.DEFAULT_KEY_VERSION;
+import static org.junit.Assert.*;
+
+/**
+ * @author bjorncs
+ */
+public class IdentityDocumentSignerTest {
+
+ @Test
+ public void generates_and_validates_signature() {
+ IdentityDocumentSigner signer = new IdentityDocumentSigner();
+ IdentityType identityType = TENANT;
+ VespaUniqueInstanceId id =
+ new VespaUniqueInstanceId(1, "cluster-id", "instance", "application", "tenant", "region", "environment", identityType);
+ AthenzService providerService = new AthenzService("vespa", "service");
+ KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA);
+ String configserverHostname = "configserverhostname";
+ String instanceHostname = "instancehostname";
+ Instant createdAt = Instant.EPOCH;
+ HashSet<String> ipAddresses = new HashSet<>(Arrays.asList("1.2.3.4", "::1"));
+ String signature =
+ signer.generateSignature(id, providerService, configserverHostname, instanceHostname, createdAt, ipAddresses, identityType, keyPair.getPrivate());
+
+ SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument(
+ null, signature, DEFAULT_KEY_VERSION, id, "dns-suffix", providerService, URI.create("https://zts"),
+ DEFAULT_DOCUMENT_VERSION, configserverHostname, instanceHostname, createdAt, ipAddresses, identityType);
+
+ assertTrue(signer.hasValidSignature(signedIdentityDocument, keyPair.getPublic()));
+ }
+
+} \ No newline at end of file