summaryrefslogtreecommitdiffstats
path: root/vespa-athenz
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@oath.com>2018-05-02 12:51:40 +0200
committerBjørn Christian Seime <bjorncs@oath.com>2018-05-03 13:09:47 +0200
commitb668043461ca16db770691a23ee711d9a8861d0b (patch)
tree50f564307237b75786bde4040f68421c2831c1e4 /vespa-athenz
parent9ba951190c3174546fb65943814ea6367a1adfc1 (diff)
Add new ZtsClient implementation
Diffstat (limited to 'vespa-athenz')
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java125
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/InstanceIdentity.java34
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClient.java39
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClientException.java18
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceIdentityCredentials.java49
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRefreshInformation.java27
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRegisterInformation.java47
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/package-info.java8
8 files changed, 347 insertions, 0 deletions
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java
new file mode 100644
index 00000000000..d1b0c08d4ca
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java
@@ -0,0 +1,125 @@
+// 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.client.zts;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.athenz.api.NToken;
+import com.yahoo.vespa.athenz.client.zts.bindings.InstanceIdentityCredentials;
+import com.yahoo.vespa.athenz.client.zts.bindings.InstanceRefreshInformation;
+import com.yahoo.vespa.athenz.client.zts.bindings.InstanceRegisterInformation;
+import com.yahoo.vespa.athenz.tls.Pkcs10Csr;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.methods.RequestBuilder;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.util.EntityUtils;
+import org.eclipse.jetty.http.HttpStatus;
+
+import javax.net.ssl.SSLContext;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.URI;
+
+/**
+ * Default implementation of {@link ZtsClient}
+ *
+ * @author bjorncs
+ * @author mortent
+ */
+public class DefaultZtsClient implements ZtsClient {
+
+ private static final ObjectMapper objectMapper = new ObjectMapper();
+
+ private final URI ztsUrl;
+ private final CloseableHttpClient client;
+
+ public DefaultZtsClient(URI ztsUrl, SSLContext sslContext) {
+ this.ztsUrl = ztsUrl;
+ this.client = HttpClientBuilder.create()
+ .setRetryHandler(new DefaultHttpRequestRetryHandler(3, /*requestSentRetryEnabled*/true))
+ .setSslcontext(sslContext)
+ .build();
+ }
+
+ @Override
+ public InstanceIdentity registerInstance(AthenzService providerIdentity,
+ AthenzService instanceIdentity,
+ String instanceId,
+ String attestationData,
+ boolean requestServiceToken,
+ Pkcs10Csr csr) {
+ InstanceRegisterInformation payload =
+ new InstanceRegisterInformation(providerIdentity, instanceIdentity, attestationData, csr, requestServiceToken);
+ HttpUriRequest request = RequestBuilder.post()
+ .setUri(ztsUrl.resolve("instance/"))
+ .setEntity(toJsonStringEntity(payload))
+ .build();
+ try (CloseableHttpResponse response = client.execute(request)) {
+ return getInstanceIdentity(response);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ @Override
+ public InstanceIdentity refreshInstance(AthenzService providerIdentity,
+ AthenzService instanceIdentity,
+ String instanceId,
+ boolean requestServiceToken,
+ Pkcs10Csr csr) {
+ InstanceRefreshInformation payload = new InstanceRefreshInformation(csr, requestServiceToken);
+ URI uri = ztsUrl.resolve(
+ String.format("instance/%s/%s/%s/%s",
+ providerIdentity.getFullName(),
+ instanceIdentity.getDomain().getName(),
+ instanceIdentity.getName(),
+ instanceId));
+ HttpUriRequest request = RequestBuilder.post()
+ .setUri(uri)
+ .setEntity(toJsonStringEntity(payload))
+ .build();
+ try (CloseableHttpResponse response = client.execute(request)) {
+ return getInstanceIdentity(response);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ private static InstanceIdentity getInstanceIdentity(HttpResponse response) throws IOException {
+ if (HttpStatus.isSuccess(response.getStatusLine().getStatusCode())) {
+ InstanceIdentityCredentials entity =
+ objectMapper.readValue(response.getEntity().getContent(), InstanceIdentityCredentials.class);
+ return entity.getServiceToken() != null
+ ? new InstanceIdentity(entity.getX509Certificate(), new NToken(entity.getServiceToken()))
+ : new InstanceIdentity(entity.getX509Certificate());
+ } else {
+ String message = EntityUtils.toString(response.getEntity());
+ throw new ZtsClientException(
+ String.format("Unable to get identity. http code/message: %d/%s",
+ response.getStatusLine().getStatusCode(), message));
+ }
+ }
+
+ private static StringEntity toJsonStringEntity(Object entity) {
+ try {
+ return new StringEntity(objectMapper.writeValueAsString(entity), ContentType.APPLICATION_JSON);
+ } catch (JsonProcessingException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ @Override
+ public void close() {
+ try {
+ client.close();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/InstanceIdentity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/InstanceIdentity.java
new file mode 100644
index 00000000000..34548351b23
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/InstanceIdentity.java
@@ -0,0 +1,34 @@
+// 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.client.zts;
+
+import com.yahoo.vespa.athenz.api.NToken;
+
+import java.security.cert.X509Certificate;
+import java.util.Optional;
+
+/**
+ * The identity of an instance of a launched service.
+ *
+ * @author bjorncs
+ */
+public class InstanceIdentity {
+ private final X509Certificate certificate;
+ private final NToken nToken;
+
+ public InstanceIdentity(X509Certificate certificate) {
+ this(certificate, null);
+ }
+
+ public InstanceIdentity(X509Certificate certificate, NToken nToken) {
+ this.certificate = certificate;
+ this.nToken = nToken;
+ }
+
+ public X509Certificate certificate() {
+ return certificate;
+ }
+
+ public Optional<NToken> nToken() {
+ return Optional.ofNullable(nToken);
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClient.java
new file mode 100644
index 00000000000..83041289451
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClient.java
@@ -0,0 +1,39 @@
+// 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.client.zts;
+
+import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.athenz.tls.Pkcs10Csr;
+
+import javax.net.ssl.SSLContext;
+
+/**
+ * Interface for a ZTS client.
+ *
+ * @author bjorncs
+ */
+public interface ZtsClient extends AutoCloseable {
+
+ /**
+ * Register an instance using the specified provider.
+ *
+ * @param attestationData The signed identity documented serialized to a string.
+ * @return A x509 certificate + service token (optional)
+ */
+ InstanceIdentity registerInstance(AthenzService providerIdentity,
+ AthenzService instanceIdentity,
+ String instanceId,
+ String attestationData,
+ boolean requestServiceToken,
+ Pkcs10Csr csr);
+
+ /**
+ * Refresh an existing instance
+ *
+ * @return A x509 certificate + service token (optional)
+ */
+ InstanceIdentity refreshInstance(AthenzService providerIdentity,
+ AthenzService instanceIdentity,
+ String instanceId,
+ boolean requestServiceToken,
+ Pkcs10Csr csr);
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClientException.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClientException.java
new file mode 100644
index 00000000000..3d3696ad870
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClientException.java
@@ -0,0 +1,18 @@
+// 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.client.zts;
+
+/**
+ * An exception that can be thrown by {@link ZtsClient} implementations.
+ *
+ * @author bjorncs
+ */
+public class ZtsClientException extends RuntimeException {
+
+ public ZtsClientException(String message) {
+ super(message);
+ }
+
+ public ZtsClientException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceIdentityCredentials.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceIdentityCredentials.java
new file mode 100644
index 00000000000..5c265f14813
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceIdentityCredentials.java
@@ -0,0 +1,49 @@
+// 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.client.zts.bindings;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.yahoo.vespa.athenz.tls.X509CertificateUtils;
+
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+
+/**
+ * Used for deserializing response from ZTS
+ *
+ * @author mortent
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class InstanceIdentityCredentials {
+ @JsonProperty("x509Certificate") private final X509Certificate x509Certificate;
+ @JsonProperty("serviceToken") private final String serviceToken;
+
+ public InstanceIdentityCredentials(
+ @JsonProperty("x509Certificate") @JsonDeserialize(using = X509CertificateDeserializer.class) X509Certificate x509Certificate,
+ @JsonProperty("serviceToken") String serviceToken) {
+ this.x509Certificate = x509Certificate;
+ this.serviceToken = serviceToken;
+ }
+
+ public X509Certificate getX509Certificate() {
+ return x509Certificate;
+ }
+
+ public String getServiceToken() {
+ return serviceToken;
+ }
+
+ public static class X509CertificateDeserializer extends JsonDeserializer<X509Certificate> {
+ @Override
+ public X509Certificate deserialize(JsonParser parser, DeserializationContext context) throws IOException {
+ return X509CertificateUtils.fromPem(parser.getValueAsString());
+ }
+ }
+
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRefreshInformation.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRefreshInformation.java
new file mode 100644
index 00000000000..6c956ddb410
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRefreshInformation.java
@@ -0,0 +1,27 @@
+// 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.client.zts.bindings;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.yahoo.vespa.athenz.tls.Pkcs10Csr;
+import com.yahoo.vespa.athenz.tls.Pkcs10CsrUtils;
+
+/**
+ * @author bjorncs
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class InstanceRefreshInformation {
+
+ @JsonProperty("csr")
+ private final String csr;
+ @JsonProperty("token")
+ private final boolean requestServiceToken;
+
+ public InstanceRefreshInformation(Pkcs10Csr csr,
+ boolean requestServiceToken) {
+ this.csr = Pkcs10CsrUtils.toPem(csr);
+ this.requestServiceToken = requestServiceToken;
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRegisterInformation.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRegisterInformation.java
new file mode 100644
index 00000000000..a70083f9556
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRegisterInformation.java
@@ -0,0 +1,47 @@
+// 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.client.zts.bindings;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.athenz.tls.Pkcs10Csr;
+import com.yahoo.vespa.athenz.tls.Pkcs10CsrUtils;
+
+/**
+ * Used for serializing request to ZTS
+ *
+ * @author mortent
+ * @author bjorncs
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class InstanceRegisterInformation {
+ @JsonProperty("provider")
+ private final String provider;
+ @JsonProperty("domain")
+ private final String domain;
+ @JsonProperty("service")
+ private final String service;
+ @JsonProperty("attestationData")
+ private final String attestationData;
+ @JsonProperty("ssh")
+ private final String ssh = null; // Not needed
+ @JsonProperty("csr")
+ private final String csr;
+ @JsonProperty("token")
+ private final boolean token;
+
+ public InstanceRegisterInformation(AthenzService providerIdentity,
+ AthenzService instanceIdentity,
+ String attestationData,
+ Pkcs10Csr csr,
+ boolean requestServiceToken) {
+ this.provider = providerIdentity.getFullName();
+ this.domain = instanceIdentity.getDomain().getName();
+ this.service = instanceIdentity.getName();
+ this.attestationData = attestationData;
+ this.csr = Pkcs10CsrUtils.toPem(csr);
+ this.token = requestServiceToken;
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/package-info.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/package-info.java
new file mode 100644
index 00000000000..cffe22ae1c0
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/package-info.java
@@ -0,0 +1,8 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+/**
+ * @author bjorncs
+ */
+@ExportPackage
+package com.yahoo.vespa.athenz.client.zts;
+
+import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file