aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java3
-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.java63
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentService.java3
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/ZtsClient.java (renamed from vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzService.java)93
-rw-r--r--vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplTest.java9
6 files changed, 135 insertions, 64 deletions
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java
index bb6a033a4ab..9762d69ec31 100644
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java
@@ -10,4 +10,7 @@ public interface AthenzIdentityProvider {
String domain();
String service();
SSLContext getIdentitySslContext();
+ SSLContext getRoleSslContext(String domain, String role);
+ String getRoleToken(String domain);
+ String getRoleToken(String domain, String role);
}
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 26fe0b6e930..4278e641166 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
@@ -35,16 +35,16 @@ class AthenzCredentialsService {
private final IdentityConfig identityConfig;
private final IdentityDocumentService identityDocumentService;
- private final AthenzService athenzService;
+ private final ZtsClient ztsClient;
private final File trustStoreJks;
AthenzCredentialsService(IdentityConfig identityConfig,
IdentityDocumentService identityDocumentService,
- AthenzService athenzService,
+ ZtsClient ztsClient,
File trustStoreJks) {
this.identityConfig = identityConfig;
this.identityDocumentService = identityDocumentService;
- this.athenzService = athenzService;
+ this.ztsClient = ztsClient;
this.trustStoreJks = trustStoreJks;
}
@@ -64,13 +64,12 @@ class AthenzCredentialsService {
identityConfig.service(),
rawDocument,
Pkcs10CsrUtils.toPem(csr));
- InstanceIdentity instanceIdentity = athenzService.sendInstanceRegisterRequest(instanceRegisterInformation,
- document.ztsEndpoint);
+ InstanceIdentity instanceIdentity = ztsClient.sendInstanceRegisterRequest(instanceRegisterInformation,
+ document.ztsEndpoint);
return toAthenzCredentials(instanceIdentity, keyPair, document);
}
- AthenzCredentials updateCredentials(AthenzCredentials currentCredentials) {
- SignedIdentityDocument document = currentCredentials.getIdentityDocument();
+ AthenzCredentials updateCredentials(SignedIdentityDocument document, SSLContext sslContext) {
KeyPair newKeyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA);
Pkcs10Csr csr = createCSR(identityConfig.domain(),
identityConfig.service(),
@@ -80,14 +79,13 @@ class AthenzCredentialsService {
newKeyPair);
InstanceRefreshInformation refreshInfo = new InstanceRefreshInformation(Pkcs10CsrUtils.toPem(csr));
InstanceIdentity instanceIdentity =
- athenzService.sendInstanceRefreshRequest(document.providerService,
- identityConfig.domain(),
- identityConfig.service(),
- document.providerUniqueId,
- refreshInfo,
- document.ztsEndpoint,
- currentCredentials.getCertificate(),
- currentCredentials.getKeyPair().getPrivate());
+ ztsClient.sendInstanceRefreshRequest(document.providerService,
+ identityConfig.domain(),
+ identityConfig.service(),
+ document.providerUniqueId,
+ refreshInfo,
+ document.ztsEndpoint,
+ sslContext);
return toAthenzCredentials(instanceIdentity, newKeyPair, document);
}
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 594fa91e18f..eb0ae89fdcf 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
@@ -8,11 +8,17 @@ import com.yahoo.container.jdisc.athenz.AthenzIdentityProvider;
import com.yahoo.container.jdisc.athenz.AthenzIdentityProviderException;
import com.yahoo.jdisc.Metric;
import com.yahoo.log.LogLevel;
+import com.yahoo.vespa.athenz.api.AthenzDomain;
+import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider;
+import com.yahoo.vespa.athenz.tls.KeyStoreType;
+import com.yahoo.vespa.athenz.tls.SslContextBuilder;
import com.yahoo.vespa.defaults.Defaults;
import javax.net.ssl.SSLContext;
import java.io.File;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
@@ -37,20 +43,22 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen
public static final String CERTIFICATE_EXPIRY_METRIC_NAME = "athenz-tenant-cert.expiry.seconds";
private volatile AthenzCredentials credentials;
+ private final ZtsClient ztsClient = new ZtsClient();
private final Metric metric;
private final AthenzCredentialsService athenzCredentialsService;
private final ScheduledExecutorService scheduler;
private final Clock clock;
- private final com.yahoo.vespa.athenz.api.AthenzService identity;
+ private final AthenzService identity;
+ // TODO IdentityConfig should contain ZTS uri and dns suffix
@Inject
public AthenzIdentityProviderImpl(IdentityConfig config, Metric metric) {
this(config,
metric,
new AthenzCredentialsService(config,
new IdentityDocumentService(config.loadBalancerAddress()),
- new AthenzService(),
- new File(Defaults.getDefaults().underVespaHome("share/ssl/certs/yahoo_certificate_bundle.jks"))),
+ new ZtsClient(),
+ getDefaultTrustStoreLocation()),
new ScheduledThreadPoolExecutor(1),
Clock.systemUTC());
}
@@ -65,7 +73,7 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen
this.athenzCredentialsService = athenzCredentialsService;
this.scheduler = scheduler;
this.clock = clock;
- this.identity = new com.yahoo.vespa.athenz.api.AthenzService(config.domain(), config.service());
+ this.identity = new AthenzService(config.domain(), config.service());
registerInstance();
}
@@ -80,7 +88,7 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen
}
@Override
- public com.yahoo.vespa.athenz.api.AthenzService identity() {
+ public AthenzService identity() {
return identity;
}
@@ -100,6 +108,45 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen
}
@Override
+ public SSLContext getRoleSslContext(String domain, String role) {
+ // This ssl context should ideally be cached as it is quite expensive to create.
+ PrivateKey privateKey = credentials.getKeyPair().getPrivate();
+ X509Certificate roleCertificate = ztsClient.getRoleCertificate(
+ new AthenzDomain(domain),
+ role,
+ credentials.getIdentityDocument().dnsSuffix,
+ credentials.getIdentityDocument().ztsEndpoint,
+ identity,
+ privateKey,
+ credentials.getIdentitySslContext());
+ return new SslContextBuilder()
+ .withKeyStore(privateKey, roleCertificate)
+ .withTrustStore(getDefaultTrustStoreLocation(), KeyStoreType.JKS)
+ .build();
+ }
+
+ @Override
+ public String getRoleToken(String domain) {
+ return ztsClient
+ .getRoleToken(
+ new AthenzDomain(domain),
+ credentials.getIdentityDocument().ztsEndpoint,
+ credentials.getIdentitySslContext())
+ .getRawToken();
+ }
+
+ @Override
+ public String getRoleToken(String domain, String role) {
+ return ztsClient
+ .getRoleToken(
+ new AthenzDomain(domain),
+ role,
+ credentials.getIdentityDocument().ztsEndpoint,
+ credentials.getIdentitySslContext())
+ .getRawToken();
+ }
+
+ @Override
public void deconstruct() {
try {
scheduler.shutdownNow();
@@ -109,6 +156,10 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen
}
}
+ private static File getDefaultTrustStoreLocation() {
+ return new File(Defaults.getDefaults().underVespaHome("share/ssl/certs/yahoo_certificate_bundle.jks"));
+ }
+
private boolean isExpired(AthenzCredentials credentials) {
return clock.instant().isAfter(getExpirationTime(credentials));
}
@@ -121,7 +172,7 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen
try {
AthenzCredentials newCredentials = isExpired(credentials)
? athenzCredentialsService.registerInstance()
- : athenzCredentialsService.updateCredentials(credentials);
+ : athenzCredentialsService.updateCredentials(credentials.getIdentityDocument(), credentials.getIdentitySslContext());
credentials = newCredentials;
} catch (Throwable t) {
log.log(LogLevel.WARNING, "Failed to update credentials: " + t.getMessage(), t);
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentService.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentService.java
index 34b28e48914..0d82bb29edd 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentService.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentService.java
@@ -74,8 +74,7 @@ public class IdentityDocumentService {
.setScheme("https")
.setHost(loadBalancerName)
.setPort(4443)
- .setPath("/athenz/v1/provider/identity-document")
- .addParameter("hostname", Defaults.getDefaults().vespaHostname())
+ .setPath("/athenz/v1/provider/identity-document/tenant/" + Defaults.getDefaults().vespaHostname())
.build();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzService.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/ZtsClient.java
index 98307a8a2d1..c995bfba791 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzService.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/ZtsClient.java
@@ -1,13 +1,19 @@
-// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+// 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.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yahoo.athenz.zts.RoleCertificateRequest;
+import com.yahoo.athenz.zts.RoleToken;
+import com.yahoo.athenz.zts.ZTSClient;
+import com.yahoo.vespa.athenz.api.AthenzDomain;
+import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.athenz.api.ZToken;
+import com.yahoo.vespa.athenz.tls.X509CertificateUtils;
import org.apache.http.client.HttpRequestRetryHandler;
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.conn.ssl.SSLContextBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
@@ -20,21 +26,15 @@ import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
-import java.security.KeyManagementException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
-import java.security.UnrecoverableKeyException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
+import java.time.Duration;
/**
* @author mortent
* @author bjorncs
*/
-public class AthenzService {
+class ZtsClient {
private static final String INSTANCE_API_PATH = "/zts/v1/instance";
@@ -44,7 +44,7 @@ public class AthenzService {
/**
* Send instance register request to ZTS, get InstanceIdentity
*/
- public InstanceIdentity sendInstanceRegisterRequest(InstanceRegisterInformation instanceRegisterInformation,
+ InstanceIdentity sendInstanceRegisterRequest(InstanceRegisterInformation instanceRegisterInformation,
URI uri) {
try(CloseableHttpClient client = HttpClientBuilder.create().setRetryHandler(retryHandler).build()) {
HttpUriRequest postRequest = RequestBuilder.post()
@@ -57,15 +57,14 @@ public class AthenzService {
}
}
- public InstanceIdentity sendInstanceRefreshRequest(String providerService,
- String instanceDomain,
- String instanceServiceName,
- String instanceId,
- InstanceRefreshInformation instanceRefreshInformation,
- URI ztsEndpoint,
- X509Certificate certicate,
- PrivateKey privateKey) {
- try (CloseableHttpClient client = createHttpClientWithTlsAuth(certicate, privateKey, retryHandler)) {
+ InstanceIdentity sendInstanceRefreshRequest(String providerService,
+ String instanceDomain,
+ String instanceServiceName,
+ String instanceId,
+ InstanceRefreshInformation instanceRefreshInformation,
+ URI ztsEndpoint,
+ SSLContext sslContext) {
+ try (CloseableHttpClient client = createHttpClientWithTlsAuth(sslContext, retryHandler)) {
URI uri = ztsEndpoint
.resolve(INSTANCE_API_PATH + '/')
.resolve(providerService + '/')
@@ -82,6 +81,43 @@ public class AthenzService {
}
}
+ ZToken getRoleToken(AthenzDomain domain,
+ URI ztsEndpoint,
+ SSLContext sslContext) {
+ // TODO ztsEndpoint should contain '/zts/v1' as path
+ URI correctedZtsEndpoint = ztsEndpoint.resolve("/zts/v1");
+ return new ZToken(
+ new ZTSClient(correctedZtsEndpoint.toString(), sslContext)
+ .getRoleToken(domain.getName()).getToken());
+ }
+
+ ZToken getRoleToken(AthenzDomain domain,
+ String roleName,
+ URI ztsEndpoint,
+ SSLContext sslContext) {
+ // TODO ztsEndpoint should contain '/zts/v1' as path
+ URI correctedZtsEndpoint = ztsEndpoint.resolve("/zts/v1");
+ return new ZToken(
+ new ZTSClient(correctedZtsEndpoint.toString(), sslContext)
+ .getRoleToken(domain.getName(), roleName).getToken());
+ }
+
+ X509Certificate getRoleCertificate(AthenzDomain roleDomain,
+ String roleName,
+ String dnsSuffix,
+ URI ztsEndpoint,
+ AthenzService identity,
+ PrivateKey privateKey,
+ SSLContext sslContext) {
+ // TODO ztsEndpoint should contain '/zts/v1' as path
+ URI correctedZtsEndpoint = ztsEndpoint.resolve("/zts/v1");
+ ZTSClient ztsClient = new ZTSClient(correctedZtsEndpoint.toString(), sslContext);
+ RoleCertificateRequest rcr = ZTSClient.generateRoleCertificateRequest(
+ identity.getDomain().getName(), identity.getName(), roleDomain.getName(), roleName, privateKey, dnsSuffix, (int) Duration.ofHours(1).getSeconds());
+ RoleToken pemCert = ztsClient.postRoleCertificateRequest(roleDomain.getName(), roleName, rcr);
+ return X509CertificateUtils.fromPem(pemCert.token);
+ }
+
private InstanceIdentity getInstanceIdentity(CloseableHttpClient client, HttpUriRequest postRequest)
throws IOException {
try (CloseableHttpResponse response = client.execute(postRequest)) {
@@ -99,26 +135,11 @@ public class AthenzService {
return new StringEntity(objectMapper.writeValueAsString(value), ContentType.APPLICATION_JSON);
}
- private static CloseableHttpClient createHttpClientWithTlsAuth(X509Certificate certificate,
- PrivateKey privateKey,
+ private static CloseableHttpClient createHttpClientWithTlsAuth(SSLContext sslContext,
HttpRequestRetryHandler retryHandler) {
- try {
- String dummyPassword = "athenz";
- KeyStore keyStore = KeyStore.getInstance("JKS");
- keyStore.load(null);
- keyStore.setKeyEntry("athenz", privateKey, dummyPassword.toCharArray(), new Certificate[]{certificate});
- SSLContext sslContext = new SSLContextBuilder()
- .loadKeyMaterial(keyStore, dummyPassword.toCharArray())
- .build();
return HttpClientBuilder.create()
.setRetryHandler(retryHandler)
.setSslcontext(sslContext)
.build();
- } catch (KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException |
- KeyManagementException | CertificateException e) {
- throw new RuntimeException(e);
- } catch (IOException e) {
- throw new UncheckedIOException(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 a0ae6ca61db..91c68702e87 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
@@ -58,26 +58,25 @@ public class AthenzIdentityProviderImplTest {
@Test
public void metrics_updated_on_refresh() throws IOException {
IdentityDocumentService identityDocumentService = mock(IdentityDocumentService.class);
- AthenzService athenzService = mock(AthenzService.class);
+ ZtsClient ztsClient = mock(ZtsClient.class);
ManualClock clock = new ManualClock(Instant.EPOCH);
Metric metric = mock(Metric.class);
when(identityDocumentService.getSignedIdentityDocument()).thenReturn(getIdentityDocument());
- when(athenzService.sendInstanceRegisterRequest(any(), any())).then(new Answer<InstanceIdentity>() {
+ when(ztsClient.sendInstanceRegisterRequest(any(), any())).then(new Answer<InstanceIdentity>() {
@Override
public InstanceIdentity answer(InvocationOnMock invocationOnMock) throws Throwable {
return new InstanceIdentity(getCertificate(getExpirationSupplier(clock)), "TOKEN");
}
});
- when(athenzService.sendInstanceRefreshRequest(anyString(), anyString(), anyString(),
- anyString(), any(), any(), any(), any()))
+ when(ztsClient.sendInstanceRefreshRequest(anyString(), anyString(), anyString(), anyString(), any(), any(), any()))
.thenThrow(new RuntimeException("#1"))
.thenThrow(new RuntimeException("#2"))
.thenReturn(new InstanceIdentity(getCertificate(getExpirationSupplier(clock)), "TOKEN"));
AthenzCredentialsService credentialService =
- new AthenzCredentialsService(IDENTITY_CONFIG, identityDocumentService, athenzService, createDummyTrustStore());
+ new AthenzCredentialsService(IDENTITY_CONFIG, identityDocumentService, ztsClient, createDummyTrustStore());
AthenzIdentityProviderImpl identityProvider =
new AthenzIdentityProviderImpl(IDENTITY_CONFIG, metric, credentialService, mock(ScheduledExecutorService.class), clock);