summaryrefslogtreecommitdiffstats
path: root/athenz-identity-provider-service
diff options
context:
space:
mode:
authorMorten Tokle <mortent@verizonmedia.com>2019-11-26 09:21:10 +0100
committerMorten Tokle <mortent@verizonmedia.com>2019-11-26 09:21:10 +0100
commitb96148d4bc405d7179a7cd670c674d464e28493a (patch)
treebcbaa9207dd57b4abf46f6315a4aad38a799bde1 /athenz-identity-provider-service
parent670ca80219ccc3d29ae0b599eb42527978e078db (diff)
Read principal from cert CN on refresh
Diffstat (limited to 'athenz-identity-provider-service')
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java15
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificateTester.java9
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java42
3 files changed, 47 insertions, 19 deletions
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java
index 4c01b0943e4..a1984557c31 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiHandler.java
@@ -38,6 +38,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.logging.Level;
+import java.util.stream.Stream;
/**
* REST API for issuing and refreshing node certificates in a hosted Vespa system.
@@ -113,7 +114,9 @@ public class CertificateAuthorityApiHandler extends LoggingRequestHandler {
private HttpResponse refreshInstance(HttpRequest request, String provider, String service, String instanceId) {
var instanceRefresh = deserializeRequest(request, InstanceSerializer::refreshFromSlime);
var instanceIdFromCsr = Certificates.instanceIdFrom(instanceRefresh.csr());
- var athenzService = new AthenzService(request.getJDiscRequest().getUserPrincipal().getName());
+
+ var athenzService = getRequestAthenzService(request);
+
if (!instanceIdFromCsr.equals(instanceId)) {
throw new IllegalArgumentException("Mismatch between instance ID in URL path and instance ID in CSR " +
"[instanceId=" + instanceId + ",instanceIdFromCsr=" + instanceIdFromCsr +
@@ -172,6 +175,16 @@ public class CertificateAuthorityApiHandler extends LoggingRequestHandler {
.orElse(Collections.emptyList());
}
+ private AthenzService getRequestAthenzService(HttpRequest request) {
+ return getRequestCertificateChain(request).stream()
+ .findFirst()
+ .map(X509CertificateUtils::getSubjectCommonNames)
+ .map(List::stream)
+ .flatMap(Stream::findFirst)
+ .map(AthenzService::new)
+ .orElseThrow(() -> new RuntimeException("No certificate found"));
+ }
+
/** Returns CA private key from secret store */
private PrivateKey caPrivateKey() {
return KeyUtils.fromPemEncodedPrivateKey(secretStore.getSecret(caPrivateKeySecretName));
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificateTester.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificateTester.java
index f24f731801d..9304354a011 100644
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificateTester.java
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/CertificateTester.java
@@ -56,8 +56,15 @@ public class CertificateTester {
return createCsr(dnsNames, List.of());
}
+ public static Pkcs10Csr createCsr(String cn, List<String> dnsNames) {
+ return createCsr(cn, dnsNames, List.of());
+ }
+
public static Pkcs10Csr createCsr(List<String> dnsNames, List<String> ipAddresses) {
- X500Principal subject = new X500Principal("CN=subject");
+ return createCsr("subject", dnsNames, ipAddresses);
+ }
+ public static Pkcs10Csr createCsr(String cn, List<String> dnsNames, List<String> ipAddresses) {
+ X500Principal subject = new X500Principal("CN=" + cn);
KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 256);
var builder = Pkcs10CsrBuilder.fromKeypair(subject, keyPair, SignatureAlgorithm.SHA512_WITH_ECDSA);
for (var dnsName : dnsNames) {
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java
index 88ee154dee8..c63ed3fceba 100644
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/ca/restapi/CertificateAuthorityApiTest.java
@@ -41,6 +41,9 @@ public class CertificateAuthorityApiTest extends ContainerTester {
private static final String INVALID_INSTANCE_ID = "1.cluster1.default.otherapp.othertenant.us-north-1.prod.node";
private static final String INVALID_INSTANCE_ID_WITH_SUFFIX = INVALID_INSTANCE_ID + ".instanceid.athenz.dev-us-north-1.vespa.aws.oath.cloud";
+ private static final String CONTAINER_IDENTITY = "vespa.external.tenant";
+ private static final String HOST_IDENTITY = "vespa.external.tenant-host";
+
@Before
public void before() {
setCaCertificateAndKey();
@@ -55,9 +58,9 @@ public class CertificateAuthorityApiTest extends ContainerTester {
Request.Method.POST));
// POST instance registration with ZTS client
- var ztsClient = new TestZtsClient(new AthenzPrincipal(new AthenzService("vespa.external.tenant-host")), null, URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
+ var ztsClient = new TestZtsClient(new AthenzPrincipal(new AthenzService(HOST_IDENTITY)), null, URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
var instanceIdentity = ztsClient.registerInstance(new AthenzService("vespa.external", "provider_prod_us-north-1"),
- new AthenzService("vespa.external", "tenant"),
+ new AthenzService(CONTAINER_IDENTITY),
getAttestationData(),
csr);
assertEquals("CN=Vespa CA", instanceIdentity.certificate().getIssuerX500Principal().getName());
@@ -65,15 +68,15 @@ public class CertificateAuthorityApiTest extends ContainerTester {
private X509Certificate registerInstance() throws Exception {
// POST instance registration
- var csr = CertificateTester.createCsr(List.of("node1.example.com", INSTANCE_ID_WITH_SUFFIX));
+ var csr = CertificateTester.createCsr(CONTAINER_IDENTITY, List.of("node1.example.com", INSTANCE_ID_WITH_SUFFIX));
assertIdentityResponse(new Request("http://localhost:12345/ca/v1/instance/",
instanceRegistrationJson(csr),
Request.Method.POST));
// POST instance registration with ZTS client
- var ztsClient = new TestZtsClient(new AthenzPrincipal(new AthenzService("vespa.external.tenant-host")), null, URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
+ var ztsClient = new TestZtsClient(new AthenzPrincipal(new AthenzService(HOST_IDENTITY)), null, URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
var instanceIdentity = ztsClient.registerInstance(new AthenzService("vespa.external", "provider_prod_us-north-1"),
- new AthenzService("vespa.external", "tenant"),
+ new AthenzService(CONTAINER_IDENTITY),
getAttestationData(),
csr);
return instanceIdentity.certificate();
@@ -85,8 +88,8 @@ public class CertificateAuthorityApiTest extends ContainerTester {
var certificate = registerInstance();
// POST instance refresh
- var csr = CertificateTester.createCsr(List.of("node1.example.com", INSTANCE_ID_WITH_SUFFIX));
- var principal = new AthenzPrincipal(new AthenzService("vespa.external.tenant"));
+ var principal = new AthenzPrincipal(new AthenzService(CONTAINER_IDENTITY));
+ var csr = CertificateTester.createCsr(principal.getIdentity().getFullName(), List.of("node1.example.com", INSTANCE_ID_WITH_SUFFIX));
var request = new Request("http://localhost:12345/ca/v1/instance/vespa.external.provider_prod_us-north-1/vespa.external/tenant/" + INSTANCE_ID,
instanceRefreshJson(csr),
Request.Method.POST,
@@ -97,7 +100,7 @@ public class CertificateAuthorityApiTest extends ContainerTester {
// POST instance refresh with ZTS client
var ztsClient = new TestZtsClient(principal, certificate, URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
var instanceIdentity = ztsClient.refreshInstance(new AthenzService("vespa.external", "provider_prod_us-north-1"),
- new AthenzService("vespa.external", "tenant"),
+ new AthenzService(CONTAINER_IDENTITY),
INSTANCE_ID,
csr);
assertEquals("CN=Vespa CA", instanceIdentity.certificate().getIssuerX500Principal().getName());
@@ -125,21 +128,26 @@ public class CertificateAuthorityApiTest extends ContainerTester {
Request.Method.POST));
// POST instance refresh where instanceId does not match CSR dnsName
- var principal = new AthenzPrincipal(new AthenzService("vespa.external.tenant"));
- csr = CertificateTester.createCsr(List.of("node1.example.com", INSTANCE_ID_WITH_SUFFIX));
- assertResponse(400, "{\"error-code\":\"BAD_REQUEST\",\"message\":\"POST http://localhost:12345/ca/v1/instance/vespa.external.provider_prod_us-north-1/vespa.external/tenant/foobar failed: Mismatch between instance ID in URL path and instance ID in CSR [instanceId=foobar,instanceIdFromCsr=1.cluster1.default.app1.tenant1.us-north-1.prod.node]\"}",
- new Request("http://localhost:12345/ca/v1/instance/vespa.external.provider_prod_us-north-1/vespa.external/tenant/foobar",
- instanceRefreshJson(csr),
- Request.Method.POST,
- principal));
+ var principal = new AthenzPrincipal(new AthenzService(CONTAINER_IDENTITY));
+ var cert = CertificateTester.createCertificate(CONTAINER_IDENTITY, KeyUtils.generateKeypair(KeyAlgorithm.EC));
+ csr = CertificateTester.createCsr(principal.getIdentity().getFullName(), List.of("node1.example.com", INSTANCE_ID_WITH_SUFFIX));
+ request = new Request("http://localhost:12345/ca/v1/instance/vespa.external.provider_prod_us-north-1/vespa.external/tenant/foobar",
+ instanceRefreshJson(csr),
+ Request.Method.POST,
+ principal);
+ request.getAttributes().put(ServletRequest.JDISC_REQUEST_X509CERT, new X509Certificate[]{cert});
+ assertResponse(
+ 400,
+ "{\"error-code\":\"BAD_REQUEST\",\"message\":\"POST http://localhost:12345/ca/v1/instance/vespa.external.provider_prod_us-north-1/vespa.external/tenant/foobar failed: Mismatch between instance ID in URL path and instance ID in CSR [instanceId=foobar,instanceIdFromCsr=1.cluster1.default.app1.tenant1.us-north-1.prod.node]\"}",
+ request);
// POST instance refresh using zts client where client cert does not contain instanceid
var certificate = registerInstance();
var ztsClient = new TestZtsClient(principal, certificate, URI.create("http://localhost:12345/ca/v1/"), SSLContext.getDefault());
try {
- var invalidCsr = CertificateTester.createCsr(List.of("node1.example.com", INVALID_INSTANCE_ID_WITH_SUFFIX));
+ var invalidCsr = CertificateTester.createCsr(principal.getIdentity().getFullName(), List.of("node1.example.com", INVALID_INSTANCE_ID_WITH_SUFFIX));
var instanceIdentity = ztsClient.refreshInstance(new AthenzService("vespa.external", "provider_prod_us-north-1"),
- new AthenzService("vespa.external", "tenant"),
+ new AthenzService(CONTAINER_IDENTITY),
INSTANCE_ID,
invalidCsr);
fail("Refresh instance should have failed");