aboutsummaryrefslogtreecommitdiffstats
path: root/node-repository
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@oath.com>2018-04-20 14:36:15 +0200
committerBjørn Christian Seime <bjorncs@oath.com>2018-04-20 14:36:15 +0200
commit5edeae1a5ce7c788f3b9af96867d777523fe1483 (patch)
tree4bbdf087db360eb60b23675adb533c5627da4a21 /node-repository
parent7cf59fb74a0d1d08708ab60cdd3470a70e4ba261 (diff)
Accept Athenz certs from controller in HostAuthenticator
- Change NodePrincipal to better distinguish between different types of identities - Change HostAuthenticator to allow any Athenz identity
Diffstat (limited to 'node-repository')
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilter.java1
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/HostAuthenticator.java12
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodePrincipal.java46
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/HostAuthenticatorTest.java36
4 files changed, 77 insertions, 18 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilter.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilter.java
index ccc09aad24a..3899cdaecdc 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilter.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizationFilter.java
@@ -27,6 +27,7 @@ import java.util.stream.Stream;
* Authorization filter for all paths in config server.
*
* @author mpolden
+ * @author bjorncs
*/
public class AuthorizationFilter implements SecurityRequestFilter {
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/HostAuthenticator.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/HostAuthenticator.java
index de8d117de11..977ff97c4ca 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/HostAuthenticator.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/HostAuthenticator.java
@@ -39,22 +39,18 @@ class HostAuthenticator {
.findFirst()
.orElseThrow(() -> new AuthenticationException("Certificate subject common name is missing!"));
if (isAthenzIssued(clientCertificate)) {
- String hostname;
List<SubjectAlternativeName> sans = X509CertificateUtils.getSubjectAlternativeNames(clientCertificate);
switch (subjectCommonName) {
case TENANT_DOCKER_HOST_IDENTITY:
- hostname = getHostFromCalypsoCertificate(sans);
- break;
+ return NodePrincipal.withAthenzIdentity(subjectCommonName, getHostFromCalypsoCertificate(sans), certificateChain);
case TENANT_DOCKER_CONTAINER_IDENTITY:
- hostname = getHostFromVespaCertificate(sans);
- break;
+ return NodePrincipal.withAthenzIdentity(subjectCommonName, getHostFromVespaCertificate(sans), certificateChain);
default:
- throw new AuthenticationException("Untrusted common name in subject: " + subjectCommonName);
+ return NodePrincipal.withAthenzIdentity(subjectCommonName, certificateChain);
}
- return new NodePrincipal(hostname, certificateChain);
} else { // self-signed where common name is hostname
// TODO Remove this branch once self-signed certificates are gone
- return new NodePrincipal(subjectCommonName, certificateChain);
+ return NodePrincipal.withLegacyIdentity(subjectCommonName, certificateChain);
}
}
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodePrincipal.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodePrincipal.java
index dbff2b0da34..cc95945c495 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodePrincipal.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/NodePrincipal.java
@@ -4,30 +4,66 @@ package com.yahoo.vespa.hosted.provision.restapi.v2.filter;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.List;
+import java.util.Optional;
/**
+ * Represents the identity of a hosted Vespa node
+ *
* @author bjorncs
*/
public class NodePrincipal implements Principal {
- private final String hostIdentity;
+ private final String identityName;
+ private final String hostname;
private final List<X509Certificate> clientCertificateChain;
+ private final Type type;
- public NodePrincipal(String hostIdentity, List<X509Certificate> clientCertificateChain) {
- this.hostIdentity = hostIdentity;
+ public static NodePrincipal withAthenzIdentity(String identityName,
+ List<X509Certificate> clientCertificateChain) {
+ return withAthenzIdentity(identityName, null, clientCertificateChain);
+ }
+
+ public static NodePrincipal withAthenzIdentity(String identityName,
+ String hostname,
+ List<X509Certificate> clientCertificateChain) {
+ return new NodePrincipal(identityName, hostname, clientCertificateChain, Type.ATHENZ);
+ }
+
+ public static NodePrincipal withLegacyIdentity(String hostname,
+ List<X509Certificate> clientCertificateChain) {
+ return new NodePrincipal(hostname, hostname, clientCertificateChain, Type.LEGACY);
+ }
+
+ private NodePrincipal(String identityName,
+ String hostname,
+ List<X509Certificate> clientCertificateChain,
+ Type type) {
+ this.identityName = identityName;
+ this.hostname = hostname;
this.clientCertificateChain = clientCertificateChain;
+ this.type = type;
}
public String getHostIdentityName() {
- return hostIdentity;
+ return identityName;
+ }
+
+ public Optional<String> getHostname() {
+ return Optional.ofNullable(hostname);
}
public List<X509Certificate> getClientCertificateChain() {
return clientCertificateChain;
}
+ public Type getType() {
+ return type;
+ }
+
@Override
public String getName() {
- return hostIdentity;
+ return identityName;
}
+ public enum Type { ATHENZ, LEGACY }
+
}
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/HostAuthenticatorTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/HostAuthenticatorTest.java
index e301d8a80f9..fa9bc47c2d5 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/HostAuthenticatorTest.java
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/HostAuthenticatorTest.java
@@ -34,6 +34,8 @@ import static com.yahoo.vespa.athenz.tls.SignatureAlgorithm.SHA256_WITH_RSA;
import static java.util.Collections.emptySet;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
/**
@@ -56,7 +58,9 @@ public class HostAuthenticatorTest {
.build();
HostAuthenticator authenticator = new HostAuthenticator(ZONE, nodeRepositoryDummy.nodeRepository());
NodePrincipal identity = authenticator.authenticate(singletonList(certificate));
- assertEquals(HOSTNAME, identity.getName());
+ assertTrue(identity.getHostname().isPresent());
+ assertEquals(HOSTNAME, identity.getHostname().get());
+ assertEquals(HOSTNAME, identity.getHostIdentityName());
}
@Test
@@ -64,8 +68,9 @@ public class HostAuthenticatorTest {
NodeRepositoryTester nodeRepositoryDummy = new NodeRepositoryTester();
nodeRepositoryDummy.addNode(OPENSTACK_ID, HOSTNAME, INSTANCE_ID, NodeType.host);
nodeRepositoryDummy.setNodeState(HOSTNAME, Node.State.active);
+ String identityName = "vespa.vespa.tenant-host";
Pkcs10Csr csr = Pkcs10CsrBuilder
- .fromKeypair(new X500Principal("CN=vespa.vespa.tenant-host"), KEYPAIR, SHA256_WITH_RSA)
+ .fromKeypair(new X500Principal("CN=" + identityName), KEYPAIR, SHA256_WITH_RSA)
.build();
X509Certificate certificate = X509CertificateBuilder
.fromCsr(csr, ATHENZ_CA_DUMMY.getSubjectX500Principal(), Instant.EPOCH, Instant.EPOCH.plusSeconds(60), KEYPAIR.getPrivate(), SHA256_WITH_RSA, 1)
@@ -73,7 +78,9 @@ public class HostAuthenticatorTest {
.build();
HostAuthenticator authenticator = new HostAuthenticator(ZONE, nodeRepositoryDummy.nodeRepository());
NodePrincipal identity = authenticator.authenticate(singletonList(certificate));
- assertEquals(HOSTNAME, identity.getName());
+ assertTrue(identity.getHostname().isPresent());
+ assertEquals(HOSTNAME, identity.getHostname().get());
+ assertEquals(identityName, identity.getHostIdentityName());
}
@Test
@@ -87,8 +94,9 @@ public class HostAuthenticatorTest {
NodeRepositoryTester nodeRepositoryDummy = new NodeRepositoryTester();
Node node = createNode(clusterId, clusterIndex, tenant, application);
nodeRepositoryDummy.nodeRepository().addDockerNodes(singletonList(node));
+ String identityName = "vespa.vespa.tenant";
Pkcs10Csr csr = Pkcs10CsrBuilder
- .fromKeypair(new X500Principal("CN=vespa.vespa.tenant"), KEYPAIR, SHA256_WITH_RSA)
+ .fromKeypair(new X500Principal("CN=" + identityName), KEYPAIR, SHA256_WITH_RSA)
.build();
VespaUniqueInstanceId vespaUniqueInstanceId = new VespaUniqueInstanceId(clusterIndex, clusterId, INSTANCE_ID, application, tenant, region, environment);
X509Certificate certificate = X509CertificateBuilder
@@ -97,7 +105,25 @@ public class HostAuthenticatorTest {
.build();
HostAuthenticator authenticator = new HostAuthenticator(ZONE, nodeRepositoryDummy.nodeRepository());
NodePrincipal identity = authenticator.authenticate(singletonList(certificate));
- assertEquals(HOSTNAME, identity.getName());
+ assertTrue(identity.getHostname().isPresent());
+ assertEquals(HOSTNAME, identity.getHostname().get());
+ assertEquals(identityName, identity.getHostIdentityName());
+ }
+
+ @Test
+ public void accepts_controller_certificate() {
+ NodeRepositoryTester nodeRepositoryDummy = new NodeRepositoryTester();
+ String identityName = "vespa.vespa.hosting";
+ Pkcs10Csr csr = Pkcs10CsrBuilder
+ .fromKeypair(new X500Principal("CN=" + identityName), KEYPAIR, SHA256_WITH_RSA)
+ .build();
+ X509Certificate certificate = X509CertificateBuilder
+ .fromCsr(csr, ATHENZ_CA_DUMMY.getSubjectX500Principal(), Instant.EPOCH, Instant.EPOCH.plusSeconds(60), KEYPAIR.getPrivate(), SHA256_WITH_RSA, 1)
+ .build();
+ HostAuthenticator authenticator = new HostAuthenticator(ZONE, nodeRepositoryDummy.nodeRepository());
+ NodePrincipal identity = authenticator.authenticate(singletonList(certificate));
+ assertFalse(identity.getHostname().isPresent());
+ assertEquals(identityName, identity.getHostIdentityName());
}
private static Node createNode(String clusterId, int clusterIndex, String tenant, String application) {