summaryrefslogtreecommitdiffstats
path: root/athenz-identity-provider-service
diff options
context:
space:
mode:
authorValerij Fredriksen <valerijf@oath.com>2017-11-09 15:45:02 +0100
committerValerij Fredriksen <valerijf@oath.com>2017-11-09 15:45:02 +0100
commit8e04239b219b26b7cc34a6f8e9173b902eaaf0b6 (patch)
tree2bcfaf32da103facdc286dd13179309da2e3d721 /athenz-identity-provider-service
parentb008228e620c180b2f78542c9104a4eb6d0fd48c (diff)
Add http endpoint to sign certificate
Diffstat (limited to 'athenz-identity-provider-service')
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderService.java13
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSignerServlet.java79
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/model/SignedCertificate.java42
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/model/SigningRequest.java42
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderServiceTest.java6
5 files changed, 180 insertions, 2 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 26a88896fb9..8ac26938633 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,6 +8,9 @@ import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.Zone;
import com.yahoo.jdisc.http.SecretStore;
import com.yahoo.log.LogLevel;
+import com.yahoo.net.HostName;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.ca.CertificateSigner;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.ca.CertificateSignerServlet;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.AthenzCertificateClient;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.CertificateClient;
@@ -64,6 +67,7 @@ public class AthenzInstanceProviderService extends AbstractComponent {
CertificateClient certificateClient,
SslContextFactory sslContextFactory) {
this(config, scheduler, zone, sslContextFactory,
+ new CertificateSigner(keyProvider, getZoneConfig(config, zone), HostName.getLocalhost()),
new InstanceValidator(keyProvider, superModelProvider),
new IdentityDocumentGenerator(config, getZoneConfig(config, zone), nodeRepository, zone, keyProvider),
new AthenzCertificateUpdater(
@@ -74,13 +78,15 @@ public class AthenzInstanceProviderService extends AbstractComponent {
ScheduledExecutorService scheduler,
Zone zone,
SslContextFactory sslContextFactory,
+ CertificateSigner certificateSigner,
InstanceValidator instanceValidator,
IdentityDocumentGenerator identityDocumentGenerator,
AthenzCertificateUpdater reloader) {
// TODO: Enable for all systems. Currently enabled for CD system only
if (SystemName.cd.equals(zone.system())) {
this.scheduler = scheduler;
- this.jetty = createJettyServer(config, sslContextFactory, instanceValidator, identityDocumentGenerator);
+ this.jetty = createJettyServer(config, sslContextFactory,
+ certificateSigner, instanceValidator, identityDocumentGenerator);
// TODO Configurable update frequency
scheduler.scheduleAtFixedRate(reloader, 0, 1, TimeUnit.DAYS);
@@ -97,6 +103,7 @@ public class AthenzInstanceProviderService extends AbstractComponent {
private static Server createJettyServer(AthenzProviderServiceConfig config,
SslContextFactory sslContextFactory,
+ CertificateSigner certificateSigner,
InstanceValidator instanceValidator,
IdentityDocumentGenerator identityDocumentGenerator) {
Server server = new Server();
@@ -105,6 +112,10 @@ public class AthenzInstanceProviderService extends AbstractComponent {
server.addConnector(connector);
ServletHandler handler = new ServletHandler();
+
+ CertificateSignerServlet certificateSignerServlet = new CertificateSignerServlet(certificateSigner);
+ handler.addServletWithMapping(new ServletHolder(certificateSignerServlet), config.apiPath() + "/sign");
+
InstanceConfirmationServlet instanceConfirmationServlet = new InstanceConfirmationServlet(instanceValidator);
handler.addServletWithMapping(new ServletHolder(instanceConfirmationServlet), config.apiPath() + "/instance");
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSignerServlet.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSignerServlet.java
new file mode 100644
index 00000000000..ad69ffa652a
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSignerServlet.java
@@ -0,0 +1,79 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.ca;
+
+import com.yahoo.log.LogLevel;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.ca.model.SignedCertificate;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.ca.model.SigningRequest;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
+import org.bouncycastle.pkcs.PKCS10CertificationRequest;
+import org.bouncycastle.util.io.pem.PemObject;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.security.cert.X509Certificate;
+import java.util.logging.Logger;
+
+/**
+ * @author freva
+ */
+public class CertificateSignerServlet extends HttpServlet {
+
+ private static final Logger log = Logger.getLogger(CertificateSignerServlet.class.getName());
+
+ private final CertificateSigner certificateSigner;
+
+ public CertificateSignerServlet(CertificateSigner certificateSigner) {
+ this.certificateSigner = certificateSigner;
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ try {
+ String remoteHostname = getRemoteHostname(req);
+ SigningRequest signingRequest = Utils.getMapper().readValue(req.getReader(), SigningRequest.class);
+
+ PKCS10CertificationRequest csr = getPKCS10CertRequest(new StringReader(signingRequest.csr));
+ log.log(LogLevel.DEBUG, "Certification request from " + remoteHostname + ": " + csr);
+
+ X509Certificate certificate = certificateSigner.generateX509Certificate(csr, remoteHostname);
+ SignedCertificate signedCertificate = new SignedCertificate(x509CertificateToString(certificate));
+
+ resp.setStatus(HttpServletResponse.SC_OK);
+ resp.setContentType("application/json");
+ resp.getWriter().write(Utils.getMapper().writeValueAsString(signedCertificate));
+ } catch (RuntimeException e) {
+ log.log(LogLevel.ERROR, e.getMessage(), e);
+ resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
+ }
+ }
+
+ private String getRemoteHostname(HttpServletRequest req) {
+ return req.getRemoteHost();
+ }
+
+ private static PKCS10CertificationRequest getPKCS10CertRequest(Reader csrReader) {
+ try (PEMParser pemParser = new PEMParser(csrReader)) {
+ return (PKCS10CertificationRequest) pemParser.readObject();
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to parse CSR", e);
+ }
+ }
+
+ private static String x509CertificateToString(X509Certificate cert) {
+ try (StringWriter stringWriter = new StringWriter(); JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) {
+ pemWriter.writeObject(new PemObject("CERTIFICATE", cert.getEncoded()));
+ pemWriter.flush();
+ return stringWriter.toString();
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to convert X509Certificate to PEM format", e);
+ }
+ }
+}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/model/SignedCertificate.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/model/SignedCertificate.java
new file mode 100644
index 00000000000..1229cebe0c1
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/model/SignedCertificate.java
@@ -0,0 +1,42 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.ca.model;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Contains PEM formatted signed certificate
+ *
+ * @author freva
+ */
+public class SignedCertificate {
+
+ @JsonProperty("certificate") public final String certificate;
+
+ @JsonCreator
+ public SignedCertificate(@JsonProperty("certificate") String certificate) {
+ this.certificate = certificate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ SignedCertificate that = (SignedCertificate) o;
+
+ return certificate.equals(that.certificate);
+ }
+
+ @Override
+ public int hashCode() {
+ return certificate.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "SignedCertificate{" +
+ "certificate='" + certificate + '\'' +
+ '}';
+ }
+}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/model/SigningRequest.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/model/SigningRequest.java
new file mode 100644
index 00000000000..a4d9f276b28
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/model/SigningRequest.java
@@ -0,0 +1,42 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice.ca.model;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Contains PEM formatted Certificate Signing Request (CSR)
+ *
+ * @author freva
+ */
+public class SigningRequest {
+
+ @JsonProperty("csr") public final String csr;
+
+ @JsonCreator
+ public SigningRequest(@JsonProperty("csr") String csr) {
+ this.csr = csr;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ SigningRequest that = (SigningRequest) o;
+
+ return csr.equals(that.csr);
+ }
+
+ @Override
+ public int hashCode() {
+ return csr.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "SigningRequest{" +
+ "csr='" + csr + '\'' +
+ '}';
+ }
+}
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 bf0746aee7e..c8c3826fc39 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
@@ -9,6 +9,7 @@ import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.Zone;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.AthenzInstanceProviderService.AthenzCertificateUpdater;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.ca.CertificateSigner;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.CertificateClient;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.IdentityDocumentGenerator;
@@ -101,13 +102,16 @@ public class AthenzInstanceProviderServiceTest {
ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
when(executor.awaitTermination(anyLong(), any())).thenReturn(true);
+ CertificateSigner certificateSigner = mock(CertificateSigner.class);
+
InstanceValidator instanceValidator = mock(InstanceValidator.class);
when(instanceValidator.isValidInstance(any())).thenReturn(true);
IdentityDocumentGenerator identityDocumentGenerator = mock(IdentityDocumentGenerator.class);
AthenzInstanceProviderService athenzInstanceProviderService = new AthenzInstanceProviderService(
- config, executor, ZONE, sslContextFactory, instanceValidator, identityDocumentGenerator, certificateUpdater);
+ config, executor, ZONE, sslContextFactory, certificateSigner, instanceValidator,
+ identityDocumentGenerator, certificateUpdater);
try (CloseableHttpClient client = createHttpClient(domain, service)) {
assertFalse(getStatus(client));