summaryrefslogtreecommitdiffstats
path: root/vespa-athenz
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@oath.com>2018-05-29 16:35:01 +0200
committerMorten Tokle <mortent@oath.com>2018-06-11 14:15:11 +0200
commitb267b3642c34720e8a6353d9afaf324f64ff2f71 (patch)
treea78bbf5b2deac0c1d8bffaea0ffaa5357f6e8b04 /vespa-athenz
parent673d1bba562da999ea3c92327a0504e0a9691e35 (diff)
Use mutual TLS auth when retrieving identity document
Diffstat (limited to 'vespa-athenz')
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java28
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java20
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentClient.java83
-rw-r--r--vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplTest.java24
4 files changed, 39 insertions, 116 deletions
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java
index e8ef2d9f97e..eedb7a2cc07 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java
@@ -1,13 +1,11 @@
// Copyright 2017 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.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.yahoo.container.core.identity.IdentityConfig;
import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.identityprovider.api.EntityBindingsMapper;
+import com.yahoo.vespa.athenz.identityprovider.api.IdentityDocumentClient;
import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument;
-import com.yahoo.vespa.athenz.identityprovider.api.bindings.SignedIdentityDocumentEntity;
import com.yahoo.vespa.athenz.tls.KeyAlgorithm;
import com.yahoo.vespa.athenz.tls.KeyUtils;
import com.yahoo.vespa.athenz.tls.Pkcs10Csr;
@@ -16,8 +14,6 @@ import com.yahoo.vespa.athenz.tls.SslContextBuilder;
import javax.net.ssl.SSLContext;
import java.io.File;
-import java.io.IOException;
-import java.io.UncheckedIOException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
@@ -25,31 +21,32 @@ import java.security.cert.X509Certificate;
import static com.yahoo.vespa.athenz.tls.KeyStoreType.JKS;
/**
+ * A service that provides method for initially registering the instance and refreshing it.
+ *
* @author bjorncs
*/
class AthenzCredentialsService {
-
- private static final ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
-
private final IdentityConfig identityConfig;
private final IdentityDocumentClient identityDocumentClient;
private final ZtsClient ztsClient;
private final File trustStoreJks;
+ private final String hostname;
AthenzCredentialsService(IdentityConfig identityConfig,
IdentityDocumentClient identityDocumentClient,
ZtsClient ztsClient,
- File trustStoreJks) {
+ File trustStoreJks,
+ String hostname) {
this.identityConfig = identityConfig;
this.identityDocumentClient = identityDocumentClient;
this.ztsClient = ztsClient;
this.trustStoreJks = trustStoreJks;
+ this.hostname = hostname;
}
AthenzCredentials registerInstance() {
KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA);
- String rawDocument = identityDocumentClient.getSignedIdentityDocument();
- SignedIdentityDocument document = parseSignedIdentityDocument(rawDocument);
+ SignedIdentityDocument document = identityDocumentClient.getTenantIdentityDocument(hostname);
InstanceCsrGenerator instanceCsrGenerator = new InstanceCsrGenerator(document.dnsSuffix());
Pkcs10Csr csr = instanceCsrGenerator.generateCsr(
new AthenzService(identityConfig.domain(), identityConfig.service()),
@@ -60,7 +57,7 @@ class AthenzCredentialsService {
new InstanceRegisterInformation(document.providerService().getFullName(),
identityConfig.domain(),
identityConfig.service(),
- rawDocument,
+ EntityBindingsMapper.toAttestationData(document),
Pkcs10CsrUtils.toPem(csr));
InstanceIdentity instanceIdentity = ztsClient.sendInstanceRegisterRequest(instanceRegisterInformation,
document.ztsEndpoint());
@@ -103,11 +100,4 @@ class AthenzCredentialsService {
.build();
}
- private static SignedIdentityDocument parseSignedIdentityDocument(String rawDocument) {
- try {
- return EntityBindingsMapper.toSignedIdentityDocument(mapper.readValue(rawDocument, SignedIdentityDocumentEntity.class));
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java
index 813941ac9b2..32eb32ed366 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java
@@ -16,12 +16,16 @@ import com.yahoo.vespa.athenz.api.AthenzRole;
import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider;
import com.yahoo.vespa.athenz.identity.ServiceIdentityProviderListenerHelper;
+import com.yahoo.vespa.athenz.identity.SiaIdentityProvider;
+import com.yahoo.vespa.athenz.tls.AthenzIdentityVerifier;
import com.yahoo.vespa.athenz.tls.KeyStoreType;
import com.yahoo.vespa.athenz.tls.SslContextBuilder;
+import com.yahoo.vespa.athenz.utils.SiaUtils;
import com.yahoo.vespa.defaults.Defaults;
import javax.net.ssl.SSLContext;
import java.io.File;
+import java.net.URI;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.time.Clock;
@@ -32,6 +36,8 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
+import static java.util.Collections.singleton;
+
/**
* @author mortent
* @author bjorncs
@@ -65,14 +71,16 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen
this(config,
metric,
new AthenzCredentialsService(config,
- new IdentityDocumentClient(config.loadBalancerAddress()),
+ createIdentityDocumentClient(config),
new ZtsClient(),
- getDefaultTrustStoreLocation()),
+ getDefaultTrustStoreLocation(),
+ Defaults.getDefaults().vespaHostname()),
new ScheduledThreadPoolExecutor(1),
Clock.systemUTC());
}
// Test only
+
AthenzIdentityProviderImpl(IdentityConfig config,
Metric metric,
AthenzCredentialsService athenzCredentialsService,
@@ -193,6 +201,14 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen
}
}
+ private static DefaultIdentityDocumentClient createIdentityDocumentClient(IdentityConfig config) {
+ return new DefaultIdentityDocumentClient(
+ URI.create(config.loadBalancerAddress()),
+ new SiaIdentityProvider(
+ new AthenzService(config.nodeIdentityName()), SiaUtils.DEFAULT_SIA_DIRECTORY, getDefaultTrustStoreLocation()),
+ new AthenzIdentityVerifier(singleton(new AthenzService(config.configserverIdentityName()))));
+ }
+
private static File getDefaultTrustStoreLocation() {
return new File(Defaults.getDefaults().underVespaHome("share/ssl/certs/yahoo_certificate_bundle.jks"));
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentClient.java
deleted file mode 100644
index dfc89431ce4..00000000000
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentClient.java
+++ /dev/null
@@ -1,83 +0,0 @@
-// 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.defaults.Defaults;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.utils.URIBuilder;
-import org.apache.http.conn.ssl.NoopHostnameVerifier;
-import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClientBuilder;
-import org.apache.http.ssl.SSLContextBuilder;
-import org.apache.http.util.EntityUtils;
-import org.eclipse.jetty.http.HttpStatus;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.security.GeneralSecurityException;
-
-/**
- * @author mortent
- * @author bjorncs
- */
-public class IdentityDocumentClient {
-
- private final URI identityDocumentApiUri;
-
- public IdentityDocumentClient(String loadBalancerName) {
- this.identityDocumentApiUri = createIdentityDocumentApiUri(loadBalancerName);
- }
-
- /**
- * Get signed identity document from config server
- */
- public String getSignedIdentityDocument() {
- try (CloseableHttpClient httpClient = createHttpClient()) {
- CloseableHttpResponse idDocResponse = httpClient.execute(new HttpGet(identityDocumentApiUri));
- String responseContent = EntityUtils.toString(idDocResponse.getEntity());
- if (HttpStatus.isSuccess(idDocResponse.getStatusLine().getStatusCode())) {
- return responseContent;
- } else {
- // TODO make sure we have retried a few times (AND logged) before giving up
- throw new RuntimeException(
- "Failed to initialize Athenz instance provider: " +
- idDocResponse.getStatusLine() + ": " + responseContent);
- }
- } catch (IOException e) {
- throw new RuntimeException("Failed getting signed identity document", e);
- }
- }
-
- // TODO Use client side auth to establish trusted secure channel
- // TODO Validate TLS certifcate of config server
- private static CloseableHttpClient createHttpClient() {
- try {
- SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
- sslContextBuilder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
- SSLConnectionSocketFactory sslSocketFactory =
- new SSLConnectionSocketFactory(sslContextBuilder.build(),
- NoopHostnameVerifier.INSTANCE);
- return HttpClientBuilder.create().setSSLSocketFactory(sslSocketFactory).setUserAgent("identity-document-client").build();
- } catch (GeneralSecurityException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static URI createIdentityDocumentApiUri(String loadBalancerName) {
- try {
- // TODO Figure out a proper way of determining the hostname matching what's registred in node-repository
- return new URIBuilder()
- .setScheme("https")
- .setHost(loadBalancerName)
- .setPort(4443)
- .setPath("/athenz/v1/provider/identity-document/tenant/" + Defaults.getDefaults().vespaHostname())
- .build();
- } catch (URISyntaxException e) {
- throw new RuntimeException(e);
- }
- }
-
-}
diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplTest.java
index 7ad465a7d80..8b87c93f051 100644
--- a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplTest.java
+++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplTest.java
@@ -1,17 +1,14 @@
// Copyright 2017 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.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.yahoo.container.core.identity.IdentityConfig;
import com.yahoo.container.jdisc.athenz.AthenzIdentityProviderException;
import com.yahoo.jdisc.Metric;
import com.yahoo.test.ManualClock;
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.IdentityDocumentClient;
import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument;
import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId;
import com.yahoo.vespa.athenz.tls.KeyStoreBuilder;
@@ -55,7 +52,13 @@ public class AthenzIdentityProviderImplTest {
private static final IdentityConfig IDENTITY_CONFIG =
new IdentityConfig(new IdentityConfig.Builder()
- .service("tenantService").domain("tenantDomain").loadBalancerAddress("cfg").ztsUrl("https:localhost:4443/zts/v1").athenzDnsSuffix("vespa.cloud"));
+ .service("tenantService")
+ .domain("tenantDomain")
+ .nodeIdentityName("vespa.tenant")
+ .configserverIdentityName("vespa.configserver")
+ .loadBalancerAddress("cfg")
+ .ztsUrl("https:localhost:4443/zts/v1")
+ .athenzDnsSuffix("dev-us-north-1.vespa.cloud"));
@Test(expected = AthenzIdentityProviderException.class)
public void component_creation_fails_when_credentials_not_found() {
@@ -73,7 +76,7 @@ public class AthenzIdentityProviderImplTest {
ManualClock clock = new ManualClock(Instant.EPOCH);
Metric metric = mock(Metric.class);
- when(identityDocumentClient.getSignedIdentityDocument()).thenReturn(getIdentityDocument());
+ when(identityDocumentClient.getTenantIdentityDocument(any())).thenReturn(getIdentityDocument());
when(ztsClient.sendInstanceRegisterRequest(any(), any())).then(new Answer<InstanceIdentity>() {
@Override
public InstanceIdentity answer(InvocationOnMock invocationOnMock) throws Throwable {
@@ -87,7 +90,7 @@ public class AthenzIdentityProviderImplTest {
.thenReturn(new InstanceIdentity(getCertificate(getExpirationSupplier(clock)), "TOKEN"));
AthenzCredentialsService credentialService =
- new AthenzCredentialsService(IDENTITY_CONFIG, identityDocumentClient, ztsClient, createDummyTrustStore());
+ new AthenzCredentialsService(IDENTITY_CONFIG, identityDocumentClient, ztsClient, createDummyTrustStore(), "localhost");
AthenzIdentityProviderImpl identityProvider =
new AthenzIdentityProviderImpl(IDENTITY_CONFIG, metric, credentialService, mock(ScheduledExecutorService.class), clock);
@@ -132,9 +135,9 @@ public class AthenzIdentityProviderImplTest {
return file;
}
- private static String getIdentityDocument() throws JsonProcessingException {
+ private static SignedIdentityDocument getIdentityDocument() {
VespaUniqueInstanceId instanceId = new VespaUniqueInstanceId(0, "default", "default", "application", "tenant", "us-north-1", "dev", IdentityType.TENANT);
- SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument(
+ return new SignedIdentityDocument(
new IdentityDocument(instanceId, "localhost", "x.y.com", Instant.EPOCH, Collections.emptySet()),
"dummysignature",
0,
@@ -148,8 +151,5 @@ public class AthenzIdentityProviderImplTest {
Instant.EPOCH,
Collections.emptySet(),
IdentityType.TENANT);
-
- return new ObjectMapper().registerModule(new JavaTimeModule())
- .writeValueAsString(EntityBindingsMapper.toSignedIdentityDocumentEntity(signedIdentityDocument));
}
}