From d2bb598fc5c89052e25cfee49960bb177bc9ff03 Mon Sep 17 00:00:00 2001 From: Morten Tokle Date: Tue, 20 Feb 2018 10:50:56 +0100 Subject: Revert "Merge pull request #5072 from vespa-engine/revert-4984-mortent/ckms" This reverts commit 6d7b65adfcd1e918da8173dab25bf701074f3cdc, reversing changes made to 2ecdfefd5616743f62691f64a517ab787d6f0c10. --- .../main/java/com/yahoo/container/jdisc/Ckms.java | 14 + .../jdisc/athenz/AthenzIdentityProvider.java | 3 +- .../jdisc/athenz/impl/AthenzCredentials.java | 51 ---- .../athenz/impl/AthenzCredentialsService.java | 93 ------ .../athenz/impl/AthenzIdentityProviderImpl.java | 330 --------------------- .../container/jdisc/athenz/impl/AthenzService.java | 124 -------- .../container/jdisc/athenz/impl/CryptoUtils.java | 113 ------- .../jdisc/athenz/impl/IdentityDocumentService.java | 85 ------ .../jdisc/athenz/impl/InstanceIdentity.java | 48 --- .../athenz/impl/InstanceRefreshInformation.java | 23 -- .../athenz/impl/InstanceRegisterInformation.java | 38 --- .../jdisc/athenz/impl/SignedIdentityDocument.java | 33 --- .../container/jdisc/athenz/impl/package-info.java | 8 - 13 files changed, 15 insertions(+), 948 deletions(-) create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/Ckms.java delete mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzCredentials.java delete mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzCredentialsService.java delete mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzIdentityProviderImpl.java delete mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzService.java delete mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/CryptoUtils.java delete mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/IdentityDocumentService.java delete mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/InstanceIdentity.java delete mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/InstanceRefreshInformation.java delete mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/InstanceRegisterInformation.java delete mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/SignedIdentityDocument.java delete mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/package-info.java (limited to 'container-disc/src/main/java') diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/Ckms.java b/container-disc/src/main/java/com/yahoo/container/jdisc/Ckms.java new file mode 100644 index 00000000000..26c71686a82 --- /dev/null +++ b/container-disc/src/main/java/com/yahoo/container/jdisc/Ckms.java @@ -0,0 +1,14 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +package com.yahoo.container.jdisc; + +/** + * @author mortent + */ +public interface Ckms { + /** Returns the secret for this key */ + String getSecret(String key); + + /** Returns the secret for this key and version */ + String getSecret(String key, int version); +} 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 c4c57f4bc47..b7190927d11 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 @@ -7,8 +7,7 @@ import javax.net.ssl.SSLContext; * @author mortent */ public interface AthenzIdentityProvider { - String getNToken() throws AthenzIdentityProviderException; String getDomain(); String getService(); - SSLContext getSslContext(); + SSLContext getIdentitySslContext(); } diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzCredentials.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzCredentials.java deleted file mode 100644 index 790a7c54333..00000000000 --- a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzCredentials.java +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.jdisc.athenz.impl; - -import java.security.KeyPair; -import java.security.cert.X509Certificate; -import java.time.Instant; - -/** - * @author bjorncs - */ -class AthenzCredentials { - - private final String nToken; - private final X509Certificate certificate; - private final KeyPair keyPair; - private final SignedIdentityDocument identityDocument; - private final Instant createdAt; - - AthenzCredentials(String nToken, - X509Certificate certificate, - KeyPair keyPair, - SignedIdentityDocument identityDocument, - Instant createdAt) { - this.nToken = nToken; - this.certificate = certificate; - this.keyPair = keyPair; - this.identityDocument = identityDocument; - this.createdAt = createdAt; - } - - String getNToken() { - return nToken; - } - - X509Certificate getCertificate() { - return certificate; - } - - KeyPair getKeyPair() { - return keyPair; - } - - SignedIdentityDocument getIdentityDocument() { - return identityDocument; - } - - Instant getCreatedAt() { - return createdAt; - } - -} diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzCredentialsService.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzCredentialsService.java deleted file mode 100644 index 5786eb9e398..00000000000 --- a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzCredentialsService.java +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.jdisc.athenz.impl; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.yahoo.container.core.identity.IdentityConfig; -import org.bouncycastle.pkcs.PKCS10CertificationRequest; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.security.KeyPair; -import java.security.cert.X509Certificate; -import java.time.Clock; - -/** - * @author bjorncs - */ -class AthenzCredentialsService { - - private static final ObjectMapper mapper = new ObjectMapper(); - - private final IdentityConfig identityConfig; - private final IdentityDocumentService identityDocumentService; - private final AthenzService athenzService; - private final Clock clock; - - AthenzCredentialsService(IdentityConfig identityConfig, - IdentityDocumentService identityDocumentService, - AthenzService athenzService, - Clock clock) { - this.identityConfig = identityConfig; - this.identityDocumentService = identityDocumentService; - this.athenzService = athenzService; - this.clock = clock; - } - - AthenzCredentials registerInstance() { - KeyPair keyPair = CryptoUtils.createKeyPair(); - String rawDocument = identityDocumentService.getSignedIdentityDocument(); - SignedIdentityDocument document = parseSignedIdentityDocument(rawDocument); - PKCS10CertificationRequest csr = CryptoUtils.createCSR(identityConfig.domain(), - identityConfig.service(), - document.dnsSuffix, - document.providerUniqueId, - keyPair); - InstanceRegisterInformation instanceRegisterInformation = - new InstanceRegisterInformation(document.providerService, - identityConfig.domain(), - identityConfig.service(), - rawDocument, - CryptoUtils.toPem(csr)); - InstanceIdentity instanceIdentity = athenzService.sendInstanceRegisterRequest(instanceRegisterInformation, - document.ztsEndpoint); - return toAthenzCredentials(instanceIdentity, keyPair, document); - } - - AthenzCredentials updateCredentials(AthenzCredentials currentCredentials) { - SignedIdentityDocument document = currentCredentials.getIdentityDocument(); - KeyPair newKeyPair = CryptoUtils.createKeyPair(); - PKCS10CertificationRequest csr = CryptoUtils.createCSR(identityConfig.domain(), - identityConfig.service(), - document.dnsSuffix, - document.providerUniqueId, - newKeyPair); - InstanceRefreshInformation refreshInfo = new InstanceRefreshInformation(CryptoUtils.toPem(csr)); - InstanceIdentity instanceIdentity = - athenzService.sendInstanceRefreshRequest(document.providerService, - identityConfig.domain(), - identityConfig.service(), - document.providerUniqueId, - refreshInfo, - document.ztsEndpoint, - currentCredentials.getCertificate(), - currentCredentials.getKeyPair().getPrivate()); - return toAthenzCredentials(instanceIdentity, newKeyPair, document); - } - - private AthenzCredentials toAthenzCredentials(InstanceIdentity instanceIdentity, - KeyPair keyPair, - SignedIdentityDocument identityDocument) { - X509Certificate certificate = instanceIdentity.getX509Certificate(); - String serviceToken = instanceIdentity.getServiceToken(); - return new AthenzCredentials(serviceToken, certificate, keyPair, identityDocument, clock.instant()); - } - - private static SignedIdentityDocument parseSignedIdentityDocument(String rawDocument) { - try { - return mapper.readValue(rawDocument, SignedIdentityDocument.class); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - -} diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzIdentityProviderImpl.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzIdentityProviderImpl.java deleted file mode 100644 index 3b2b065fa8c..00000000000 --- a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzIdentityProviderImpl.java +++ /dev/null @@ -1,330 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.jdisc.athenz.impl; - -import com.google.inject.Inject; -import com.yahoo.component.AbstractComponent; -import com.yahoo.container.core.identity.IdentityConfig; -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.defaults.Defaults; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import java.io.FileInputStream; -import java.io.IOException; -import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.time.Clock; -import java.time.Duration; -import java.time.Instant; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; -import java.util.logging.Logger; - -/** - * @author mortent - * @author bjorncs - */ -public final class AthenzIdentityProviderImpl extends AbstractComponent implements AthenzIdentityProvider { - - private static final Logger log = Logger.getLogger(AthenzIdentityProviderImpl.class.getName()); - - // TODO Make some of these values configurable through config. Match requested expiration of register/update requests. - // TODO These should match the requested expiration - static final Duration EXPIRES_AFTER = Duration.ofDays(1); - static final Duration EXPIRATION_MARGIN = Duration.ofMinutes(30); - static final Duration INITIAL_WAIT_NTOKEN = Duration.ofMinutes(5); - static final Duration UPDATE_PERIOD = EXPIRES_AFTER.dividedBy(3); - static final Duration REDUCED_UPDATE_PERIOD = Duration.ofMinutes(30); - static final Duration INITIAL_BACKOFF_DELAY = Duration.ofMinutes(4); - static final Duration MAX_REGISTER_BACKOFF_DELAY = Duration.ofHours(1); - static final int BACKOFF_DELAY_MULTIPLIER = 2; - static final Duration AWAIT_TERMINTATION_TIMEOUT = Duration.ofSeconds(90); - - private static final Duration CERTIFICATE_EXPIRY_METRIC_UPDATE_PERIOD = Duration.ofMinutes(5); - private static final String CERTIFICATE_EXPIRY_METRIC_NAME = "athenz-tenant-cert.expiry.seconds"; - - static final String REGISTER_INSTANCE_TAG = "register-instance"; - static final String UPDATE_CREDENTIALS_TAG = "update-credentials"; - static final String TIMEOUT_INITIAL_WAIT_TAG = "timeout-initial-wait"; - static final String METRICS_UPDATER_TAG = "metrics-updater"; - - - private final AtomicReference credentials = new AtomicReference<>(); - private final AtomicReference lastThrowable = new AtomicReference<>(); - private final CountDownLatch credentialsRetrievedSignal = new CountDownLatch(1); - private final AthenzCredentialsService athenzCredentialsService; - private final Scheduler scheduler; - private final Clock clock; - private final String domain; - private final String service; - - private final CertificateExpiryMetricUpdater metricUpdater; - - @Inject - public AthenzIdentityProviderImpl(IdentityConfig config, Metric metric) { - this(config, - metric, - new AthenzCredentialsService(config, - new IdentityDocumentService(config.loadBalancerAddress()), - new AthenzService(), - Clock.systemUTC()), - new ThreadPoolScheduler(), - Clock.systemUTC()); - } - - // Test only - AthenzIdentityProviderImpl(IdentityConfig config, - Metric metric, - AthenzCredentialsService athenzCredentialsService, - Scheduler scheduler, - Clock clock) { - this.athenzCredentialsService = athenzCredentialsService; - this.scheduler = scheduler; - this.clock = clock; - this.domain = config.domain(); - this.service = config.service(); - scheduler.submit(new RegisterInstanceTask()); - scheduler.schedule(new TimeoutInitialWaitTask(), INITIAL_WAIT_NTOKEN); - - metricUpdater = new CertificateExpiryMetricUpdater(metric); - } - - @Override - public String getNToken() { - try { - credentialsRetrievedSignal.await(); - AthenzCredentials credentialsSnapshot = credentials.get(); - if (credentialsSnapshot == null) { - throw new AthenzIdentityProviderException("Could not retrieve Athenz credentials", lastThrowable.get()); - } - if (isExpired(credentialsSnapshot)) { - throw new AthenzIdentityProviderException("Athenz credentials are expired", lastThrowable.get()); - } - return credentialsSnapshot.getNToken(); - } catch (InterruptedException e) { - throw new AthenzIdentityProviderException("Failed to register instance credentials", lastThrowable.get()); - } - } - - @Override - public String getDomain() { - return domain; - } - - @Override - public String getService() { - return service; - } - - @Override - public SSLContext getSslContext() { - try { - SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); - sslContext.init(createKeyManagersWithServiceCertificate(), - createTrustManagersWithAthenzCa(), - null); - return sslContext; - } catch (NoSuchAlgorithmException | KeyManagementException e) { - throw new RuntimeException(e); - } - } - - private KeyManager[] createKeyManagersWithServiceCertificate() { - try { - credentialsRetrievedSignal.await(); - KeyStore keyStore = KeyStore.getInstance("JKS"); - keyStore.load(null); - keyStore.setKeyEntry("instance-key", - credentials.get().getKeyPair().getPrivate(), - new char[0], - new Certificate[]{credentials.get().getCertificate()}); - KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - keyManagerFactory.init(keyStore, new char[0]); - return keyManagerFactory.getKeyManagers(); - } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException | IOException e) { - throw new RuntimeException(e); - } catch (InterruptedException e) { - throw new AthenzIdentityProviderException("Failed to register instance credentials", lastThrowable.get()); - } - } - - private static TrustManager[] createTrustManagersWithAthenzCa() { - try { - KeyStore trustStore = KeyStore.getInstance("JKS"); - try (FileInputStream in = new FileInputStream(Defaults.getDefaults().underVespaHome("share/ssl/certs/yahoo_certificate_bundle.jks"))) { - trustStore.load(in, null); - } - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - trustManagerFactory.init(trustStore); - return trustManagerFactory.getTrustManagers(); - } catch (CertificateException | IOException | KeyStoreException | NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - @Override - public void deconstruct() { - scheduler.shutdown(AWAIT_TERMINTATION_TIMEOUT); - } - - private boolean isExpired(AthenzCredentials credentials) { - return clock.instant().isAfter(getExpirationTime(credentials)); - } - - private static Instant getExpirationTime(AthenzCredentials credentials) { - return credentials.getCreatedAt().plus(EXPIRES_AFTER).minus(EXPIRATION_MARGIN); - } - - private class RegisterInstanceTask implements RunnableWithTag { - - private final Duration backoffDelay; - - RegisterInstanceTask() { - this(INITIAL_BACKOFF_DELAY); - } - - RegisterInstanceTask(Duration backoffDelay) { - this.backoffDelay = backoffDelay; - } - - @Override - public void run() { - try { - credentials.set(athenzCredentialsService.registerInstance()); - credentialsRetrievedSignal.countDown(); - scheduler.schedule(new UpdateCredentialsTask(), UPDATE_PERIOD); - scheduler.submit(metricUpdater); - } catch (Throwable t) { - log.log(LogLevel.ERROR, "Failed to register instance: " + t.getMessage(), t); - lastThrowable.set(t); - Duration nextBackoffDelay = backoffDelay.multipliedBy(BACKOFF_DELAY_MULTIPLIER); - if (nextBackoffDelay.compareTo(MAX_REGISTER_BACKOFF_DELAY) > 0) { - nextBackoffDelay = MAX_REGISTER_BACKOFF_DELAY; - } - scheduler.schedule(new RegisterInstanceTask(nextBackoffDelay), backoffDelay); - } - } - - @Override - public String tag() { - return REGISTER_INSTANCE_TAG; - } - } - - private class UpdateCredentialsTask implements RunnableWithTag { - @Override - public void run() { - AthenzCredentials currentCredentials = credentials.get(); - try { - AthenzCredentials newCredentials = isExpired(currentCredentials) - ? athenzCredentialsService.registerInstance() - : athenzCredentialsService.updateCredentials(currentCredentials); - credentials.set(newCredentials); - scheduler.schedule(new UpdateCredentialsTask(), UPDATE_PERIOD); - } catch (Throwable t) { - log.log(LogLevel.WARNING, "Failed to update credentials: " + t.getMessage(), t); - lastThrowable.set(t); - Duration timeToExpiration = Duration.between(clock.instant(), getExpirationTime(currentCredentials)); - // NOTE: Update period might be after timeToExpiration, still we do not want to DDoS Athenz. - Duration updatePeriod = - timeToExpiration.compareTo(UPDATE_PERIOD) > 0 ? UPDATE_PERIOD : REDUCED_UPDATE_PERIOD; - scheduler.schedule(new UpdateCredentialsTask(), updatePeriod); - } - } - - @Override - public String tag() { - return UPDATE_CREDENTIALS_TAG; - } - } - - private class CertificateExpiryMetricUpdater implements RunnableWithTag { - private final Metric metric; - - private CertificateExpiryMetricUpdater(Metric metric) { - this.metric = metric; - } - - @Override - public void run() { - Instant expirationTime = getExpirationTime(credentials.get()); - Duration remainingLifetime = Duration.between(clock.instant(), expirationTime); - metric.set(CERTIFICATE_EXPIRY_METRIC_NAME, remainingLifetime.getSeconds(), null); - scheduler.schedule(this, CERTIFICATE_EXPIRY_METRIC_UPDATE_PERIOD); - } - - @Override - public String tag() { - return METRICS_UPDATER_TAG; - } - } - - private class TimeoutInitialWaitTask implements RunnableWithTag { - @Override - public void run() { - credentialsRetrievedSignal.countDown(); - } - - @Override - public String tag() { - return TIMEOUT_INITIAL_WAIT_TAG; - } - } - - private static class ThreadPoolScheduler implements Scheduler { - - private static final Logger log = Logger.getLogger(ThreadPoolScheduler.class.getName()); - - private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(0); - - @Override - public void schedule(RunnableWithTag runnable, Duration delay) { - log.log(LogLevel.FINE, String.format("Scheduling task '%s' in '%s'", runnable.tag(), delay)); - executor.schedule(runnable, delay.getSeconds(), TimeUnit.SECONDS); - } - - @Override - public void submit(RunnableWithTag runnable) { - log.log(LogLevel.FINE, String.format("Scheduling task '%s' now", runnable.tag())); - executor.submit(runnable); - } - - @Override - public void shutdown(Duration timeout) { - try { - executor.shutdownNow(); - executor.awaitTermination(AWAIT_TERMINTATION_TIMEOUT.getSeconds(), TimeUnit.SECONDS); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - } - - public interface Scheduler { - void schedule(RunnableWithTag runnable, Duration delay); - default void submit(RunnableWithTag runnable) { schedule(runnable, Duration.ZERO); } - default void shutdown(Duration timeout) {} - } - - public interface RunnableWithTag extends Runnable { - - String tag(); - } - -} - diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzService.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzService.java deleted file mode 100644 index 898f90e3438..00000000000 --- a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzService.java +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.jdisc.athenz.impl; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -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; -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; -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; - -/** - * @author mortent - * @author bjorncs - */ -public class AthenzService { - - private static final String INSTANCE_API_PATH = "/zts/v1/instance"; - - private final ObjectMapper objectMapper = new ObjectMapper(); - private final HttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler(3, /*requestSentRetryEnabled*/true); - - /** - * Send instance register request to ZTS, get InstanceIdentity - */ - public InstanceIdentity sendInstanceRegisterRequest(InstanceRegisterInformation instanceRegisterInformation, - URI uri) { - try(CloseableHttpClient client = HttpClientBuilder.create().setRetryHandler(retryHandler).build()) { - HttpUriRequest postRequest = RequestBuilder.post() - .setUri(uri.resolve(INSTANCE_API_PATH)) - .setEntity(toJsonStringEntity(instanceRegisterInformation)) - .build(); - return getInstanceIdentity(client, postRequest); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - 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)) { - URI uri = ztsEndpoint - .resolve(INSTANCE_API_PATH + '/') - .resolve(providerService + '/') - .resolve(instanceDomain + '/') - .resolve(instanceServiceName + '/') - .resolve(instanceId); - HttpUriRequest postRequest = RequestBuilder.post() - .setUri(uri) - .setEntity(toJsonStringEntity(instanceRefreshInformation)) - .build(); - return getInstanceIdentity(client, postRequest); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - private InstanceIdentity getInstanceIdentity(CloseableHttpClient client, HttpUriRequest postRequest) - throws IOException { - try (CloseableHttpResponse response = client.execute(postRequest)) { - if(HttpStatus.isSuccess(response.getStatusLine().getStatusCode())) { - return objectMapper.readValue(response.getEntity().getContent(), InstanceIdentity.class); - } else { - String message = EntityUtils.toString(response.getEntity()); - throw new RuntimeException(String.format("Unable to get identity. http code/message: %d/%s", - response.getStatusLine().getStatusCode(), message)); - } - } - } - - private StringEntity toJsonStringEntity(Object value) throws JsonProcessingException { - return new StringEntity(objectMapper.writeValueAsString(value), ContentType.APPLICATION_JSON); - } - - private static CloseableHttpClient createHttpClientWithTlsAuth(X509Certificate certificate, - PrivateKey privateKey, - 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/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/CryptoUtils.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/CryptoUtils.java deleted file mode 100644 index 388b40a1fe0..00000000000 --- a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/CryptoUtils.java +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.jdisc.athenz.impl; - -import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; -import org.bouncycastle.asn1.x509.Extension; -import org.bouncycastle.asn1.x509.ExtensionsGenerator; -import org.bouncycastle.asn1.x509.GeneralName; -import org.bouncycastle.asn1.x509.GeneralNames; -import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.PEMParser; -import org.bouncycastle.openssl.jcajce.JcaPEMWriter; -import org.bouncycastle.operator.OperatorCreationException; -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; -import org.bouncycastle.pkcs.PKCS10CertificationRequest; -import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; -import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; -import org.bouncycastle.util.io.pem.PemObject; - -import javax.security.auth.x500.X500Principal; -import java.io.IOException; -import java.io.StringReader; -import java.io.StringWriter; -import java.io.UncheckedIOException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; - -/** - * @author bjorncs - */ -class CryptoUtils { - - private static final BouncyCastleProvider bouncyCastleProvider = new BouncyCastleProvider(); - - private CryptoUtils() {} - - static KeyPair createKeyPair() { - try { - KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); - return kpg.generateKeyPair(); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - static PKCS10CertificationRequest createCSR(String identityDomain, - String identityService, - String dnsSuffix, - String providerUniqueId, - KeyPair keyPair) { - try { - // Add SAN dnsname .. - // and SAN dnsname .instanceid.athenz. - GeneralNames subjectAltNames = new GeneralNames(new GeneralName[]{ - new GeneralName(GeneralName.dNSName, String.format("%s.%s.%s", - identityService, - identityDomain.replace(".", "-"), - dnsSuffix)), - new GeneralName(GeneralName.dNSName, String.format("%s.instanceid.athenz.%s", - providerUniqueId, - dnsSuffix)) - }); - - ExtensionsGenerator extGen = new ExtensionsGenerator(); - extGen.addExtension(Extension.subjectAlternativeName, false, subjectAltNames); - - X500Principal subject = new X500Principal( - String.format("CN=%s.%s", identityDomain, identityService)); - - PKCS10CertificationRequestBuilder requestBuilder = - new JcaPKCS10CertificationRequestBuilder(subject, keyPair.getPublic()); - requestBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extGen.generate()); - return requestBuilder.build(new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate())); - } catch (OperatorCreationException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - static String toPem(PKCS10CertificationRequest csr) { - try (StringWriter stringWriter = new StringWriter(); JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) { - pemWriter.writeObject(new PemObject("CERTIFICATE REQUEST", csr.getEncoded())); - pemWriter.flush(); - return stringWriter.toString(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - static X509Certificate parseCertificate(String pemEncodedCertificate) { - try (PEMParser parser = new PEMParser(new StringReader(pemEncodedCertificate))) { - Object pemObject = parser.readObject(); - if (pemObject instanceof X509Certificate) { - return (X509Certificate) pemObject; - } - if (pemObject instanceof X509CertificateHolder) { - return new JcaX509CertificateConverter() - .setProvider(bouncyCastleProvider) - .getCertificate((X509CertificateHolder) pemObject); - } - throw new IllegalArgumentException("Invalid type of PEM object: " + pemObject); - } catch (IOException e) { - throw new UncheckedIOException(e); - } catch (CertificateException e) { - throw new RuntimeException(e); - } - } -} diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/IdentityDocumentService.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/IdentityDocumentService.java deleted file mode 100644 index 7878400964a..00000000000 --- a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/IdentityDocumentService.java +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.jdisc.athenz.impl; - -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.SSLConnectionSocketFactory; -import org.apache.http.conn.ssl.SSLContextBuilder; -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.util.EntityUtils; -import org.eclipse.jetty.http.HttpStatus; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; - -/** - * @author mortent - * @author bjorncs - */ -public class IdentityDocumentService { - - private final URI identityDocumentApiUri; - - public IdentityDocumentService(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(), - SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); - return HttpClientBuilder.create().setSSLSocketFactory(sslSocketFactory).setUserAgent("identity-document-client").build(); - } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException 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") - .addParameter("hostname", Defaults.getDefaults().vespaHostname()) - .build(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - -} diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/InstanceIdentity.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/InstanceIdentity.java deleted file mode 100644 index 20bbb2aa67e..00000000000 --- a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/InstanceIdentity.java +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.jdisc.athenz.impl; - -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 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 InstanceIdentity { - @JsonProperty("x509Certificate") private final X509Certificate x509Certificate; - @JsonProperty("serviceToken") private final String serviceToken; - - public InstanceIdentity(@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 { - @Override - public X509Certificate deserialize(JsonParser parser, DeserializationContext context) throws IOException { - return CryptoUtils.parseCertificate(parser.getValueAsString()); - } - } - -} diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/InstanceRefreshInformation.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/InstanceRefreshInformation.java deleted file mode 100644 index dd893cb3143..00000000000 --- a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/InstanceRefreshInformation.java +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.jdisc.athenz.impl; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * @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 = true; - - public InstanceRefreshInformation(String csr) { - this.csr = csr; - } -} diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/InstanceRegisterInformation.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/InstanceRegisterInformation.java deleted file mode 100644 index e2355cb7a2d..00000000000 --- a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/InstanceRegisterInformation.java +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.jdisc.athenz.impl; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Used for serializing request to ZTS - * - * @author mortent - */ -@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 = true; - - public InstanceRegisterInformation(String provider, String domain, String service, String attestationData, String csr) { - this.provider = provider; - this.domain = domain; - this.service = service; - this.attestationData = attestationData; - this.csr = csr; - } -} diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/SignedIdentityDocument.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/SignedIdentityDocument.java deleted file mode 100644 index 5d5b5430859..00000000000 --- a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/SignedIdentityDocument.java +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.container.jdisc.athenz.impl; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.net.URI; - -/** - * @author bjorncs - */ -// TODO Most of these value should ideally be config provided by config-model -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonInclude(JsonInclude.Include.NON_NULL) -class SignedIdentityDocument { - public final String providerUniqueId; - public final String dnsSuffix; - public final String providerService; - public final URI ztsEndpoint; - - public SignedIdentityDocument(@JsonProperty("provider-unique-id") String providerUniqueId, - @JsonProperty("dns-suffix") String dnsSuffix, - @JsonProperty("provider-service") String providerService, - @JsonProperty("zts-endpoint") URI ztsEndpoint) { - this.providerUniqueId = providerUniqueId; - this.dnsSuffix = dnsSuffix; - this.providerService = providerService; - this.ztsEndpoint = ztsEndpoint; - } - -} - diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/package-info.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/package-info.java deleted file mode 100644 index 2d7cbbb6315..00000000000 --- a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -/** - * @author mortent - */ -@ExportPackage -package com.yahoo.container.jdisc.athenz.impl; - -import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file -- cgit v1.2.3