summaryrefslogtreecommitdiffstats
path: root/athenz-identity-provider-service
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@oath.com>2018-09-06 13:32:07 +0200
committerBjørn Christian Seime <bjorncs@oath.com>2018-09-11 14:01:35 +0200
commit89d9b98ed0aac700ff414b85fa3e406a14529df2 (patch)
tree05c4528b0da6205efe1045864778bea20b549c2c /athenz-identity-provider-service
parent5f2187d413304eaaafed3f655b5e7a052e249a47 (diff)
Configure https connector using SslContextFactoryProvider
Diffstat (limited to 'athenz-identity-provider-service')
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java203
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslTrustStoreConfigurator.java53
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/CertificateExpiryMetricUpdater.java30
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ConfigserverSslContextFactoryProvider.java199
4 files changed, 206 insertions, 279 deletions
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java
deleted file mode 100644
index 0b64f206267..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java
+++ /dev/null
@@ -1,203 +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.hosted.athenz.instanceproviderservice;
-
-import com.google.inject.Inject;
-import com.yahoo.cloud.config.ConfigserverConfig;
-import com.yahoo.component.AbstractComponent;
-import com.yahoo.config.provision.Zone;
-import com.yahoo.jdisc.http.ssl.SslKeyStoreConfigurator;
-import com.yahoo.jdisc.http.ssl.SslKeyStoreContext;
-import com.yahoo.log.LogLevel;
-import com.yahoo.vespa.athenz.api.AthenzService;
-import com.yahoo.vespa.athenz.client.zts.DefaultZtsClient;
-import com.yahoo.vespa.athenz.client.zts.Identity;
-import com.yahoo.vespa.athenz.client.zts.ZtsClient;
-import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider;
-import com.yahoo.security.KeyStoreBuilder;
-import com.yahoo.security.KeyStoreType;
-import com.yahoo.security.KeyUtils;
-import com.yahoo.vespa.athenz.utils.SiaUtils;
-import com.yahoo.vespa.defaults.Defaults;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
-
-import java.net.URI;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.security.KeyPair;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.cert.X509Certificate;
-import java.time.Duration;
-import java.time.Instant;
-import java.util.List;
-import java.util.Optional;
-import java.util.UUID;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Logger;
-
-import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils.getZoneConfig;
-
-/**
- * A component that is responsible for retrieving an Athenz TLS certificate and configuring the configserver to use
- * that certificate for its HTTPS endpoint.
- *
- * @author bjorncs
- */
-@SuppressWarnings("unused") // Component injected into Jetty connector factory
-public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements SslKeyStoreConfigurator {
- private static final Logger log = Logger.getLogger(AthenzSslKeyStoreConfigurator.class.getName());
- private static final String CERTIFICATE_ALIAS = "athenz";
- private static final Duration EXPIRATION_MARGIN = Duration.ofHours(6);
- private static final Path VESPA_SIA_DIRECTORY = Paths.get(Defaults.getDefaults().underVespaHome("var/vespa/sia"));
-
- private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
- private final ZtsClient ztsClient;
- private final KeyProvider keyProvider;
- private final AthenzProviderServiceConfig.Zones zoneConfig;
- private final Duration updatePeriod;
- private final AthenzService configserverIdentity;
- private volatile KeyStoreAndPassword currentKeyStore;
-
- @Inject
- public AthenzSslKeyStoreConfigurator(ServiceIdentityProvider bootstrapIdentity,
- KeyProvider keyProvider,
- AthenzProviderServiceConfig config,
- Zone zone,
- ConfigserverConfig configserverConfig) {
- AthenzProviderServiceConfig.Zones zoneConfig = getZoneConfig(config, zone);
- AthenzService configserverIdentity = new AthenzService(zoneConfig.domain(), zoneConfig.serviceName());
- Duration updatePeriod = Duration.ofDays(config.updatePeriodDays());
- DefaultZtsClient ztsClient = new DefaultZtsClient(URI.create(zoneConfig.ztsUrl()), bootstrapIdentity);
- this.ztsClient = ztsClient;
- this.keyProvider = keyProvider;
- this.zoneConfig = zoneConfig;
- this.currentKeyStore = initializeKeystore(configserverIdentity, keyProvider, ztsClient, zoneConfig, updatePeriod);
- this.updatePeriod = updatePeriod;
- this.configserverIdentity = configserverIdentity;
- }
-
- private static KeyStoreAndPassword initializeKeystore(AthenzService configserverIdentity,
- KeyProvider keyProvider,
- ZtsClient ztsClient,
- AthenzProviderServiceConfig.Zones keystoreCacheDirectory,
- Duration updatePeriod) {
- return tryReadKeystoreFile(configserverIdentity, updatePeriod)
- .orElseGet(() -> downloadCertificate(configserverIdentity, keyProvider, ztsClient, keystoreCacheDirectory));
- }
-
- private static Optional<KeyStoreAndPassword> tryReadKeystoreFile(AthenzService configserverIdentity,
- Duration updatePeriod) {
- Optional<X509Certificate> certificate = SiaUtils.readCertificateFile(VESPA_SIA_DIRECTORY, configserverIdentity);
- if (!certificate.isPresent()) return Optional.empty();
- Optional<PrivateKey> privateKey = SiaUtils.readPrivateKeyFile(VESPA_SIA_DIRECTORY, configserverIdentity);
- if (!privateKey.isPresent()) return Optional.empty();
- Instant minimumExpiration = Instant.now().plus(updatePeriod).plus(EXPIRATION_MARGIN);
- boolean isExpired = certificate.get().getNotAfter().toInstant().isBefore(minimumExpiration);
- if (isExpired) return Optional.empty();
- char[] password = generateKeystorePassword();
- KeyStore keyStore = KeyStoreBuilder.withType(KeyStoreType.JKS)
- .withKeyEntry(CERTIFICATE_ALIAS, privateKey.get(), password, certificate.get())
- .build();
- return Optional.of(new KeyStoreAndPassword(keyStore, password));
- }
-
- @Override
- public void configure(SslKeyStoreContext sslKeyStoreContext) {
- sslKeyStoreContext.updateKeyStore(currentKeyStore.keyStore, new String(currentKeyStore.password));
- scheduler.scheduleAtFixedRate(new AthenzCertificateUpdater(sslKeyStoreContext),
- updatePeriod.toDays()/*initial delay*/,
- updatePeriod.toDays(),
- TimeUnit.DAYS);
- }
-
- @Override
- public void deconstruct() {
- try {
- scheduler.shutdownNow();
- scheduler.awaitTermination(30, TimeUnit.SECONDS);
- ztsClient.close();
- } catch (InterruptedException e) {
- throw new RuntimeException("Failed to shutdown Athenz certificate updater on time", e);
- }
- }
-
- Instant getCertificateExpiry() throws KeyStoreException {
- return getCertificateExpiry(currentKeyStore.keyStore);
- }
-
- private static Instant getCertificateExpiry(KeyStore keyStore) throws KeyStoreException {
- X509Certificate certificate = (X509Certificate) keyStore.getCertificate(CERTIFICATE_ALIAS);
- return certificate.getNotAfter().toInstant();
- }
-
- private static KeyStoreAndPassword downloadCertificate(AthenzService configserverIdentity,
- KeyProvider keyProvider,
- ZtsClient ztsClient,
- AthenzProviderServiceConfig.Zones zoneConfig) {
- PrivateKey privateKey = keyProvider.getPrivateKey(zoneConfig.secretVersion());
- PublicKey publicKey = KeyUtils.extractPublicKey(privateKey);
- Identity serviceIdentity = ztsClient.getServiceIdentity(configserverIdentity,
- Integer.toString(zoneConfig.secretVersion()),
- new KeyPair(publicKey, privateKey),
- zoneConfig.certDnsSuffix());
- X509Certificate certificate = serviceIdentity.certificate();
- writeCredentials(configserverIdentity, certificate, serviceIdentity.caCertificates(), privateKey);
- Instant expirationTime = certificate.getNotAfter().toInstant();
- Duration expiry = Duration.between(certificate.getNotBefore().toInstant(), expirationTime);
- log.log(LogLevel.INFO, String.format("Got Athenz x509 certificate with expiry %s (expires %s)", expiry, expirationTime));
-
- char[] keystorePassword = generateKeystorePassword();
- KeyStore keyStore = KeyStoreBuilder.withType(KeyStoreType.JKS)
- .withKeyEntry(CERTIFICATE_ALIAS, privateKey, keystorePassword, certificate)
- .build();
- return new KeyStoreAndPassword(keyStore, keystorePassword);
- }
-
- private static void writeCredentials(AthenzService configserverIdentity,
- X509Certificate certificate,
- List<X509Certificate> caCertificates,
- PrivateKey privateKey) {
- SiaUtils.writeCertificateFile(VESPA_SIA_DIRECTORY, configserverIdentity, certificate);
- SiaUtils.writePrivateKeyFile(VESPA_SIA_DIRECTORY, configserverIdentity, privateKey);
- }
-
- private static char[] generateKeystorePassword() {
- return UUID.randomUUID().toString().toCharArray();
- }
-
- private class AthenzCertificateUpdater implements Runnable {
-
- private final SslKeyStoreContext sslKeyStoreContext;
-
- AthenzCertificateUpdater(SslKeyStoreContext sslKeyStoreContext) {
- this.sslKeyStoreContext = sslKeyStoreContext;
- }
-
- @Override
- public void run() {
- try {
- log.log(LogLevel.INFO, "Updating Athenz certificate from ZTS");
- currentKeyStore = downloadCertificate(configserverIdentity, keyProvider, ztsClient, zoneConfig);
- sslKeyStoreContext.updateKeyStore(currentKeyStore.keyStore, new String(currentKeyStore.password));
- log.log(LogLevel.INFO, "Athenz certificate reload successfully completed");
- } catch (Throwable e) {
- log.log(LogLevel.ERROR, "Failed to update certificate from ZTS: " + e.getMessage(), e);
- }
- }
-
- }
-
- private static class KeyStoreAndPassword {
- final KeyStore keyStore;
- final char[] password;
-
- KeyStoreAndPassword(KeyStore keyStore, char[] password) {
- this.keyStore = keyStore;
- this.password = password;
- }
- }
-}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslTrustStoreConfigurator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslTrustStoreConfigurator.java
deleted file mode 100644
index da5fd430f1c..00000000000
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslTrustStoreConfigurator.java
+++ /dev/null
@@ -1,53 +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.hosted.athenz.instanceproviderservice;
-
-import com.google.inject.Inject;
-import com.yahoo.jdisc.http.ssl.SslTrustStoreConfigurator;
-import com.yahoo.jdisc.http.ssl.SslTrustStoreContext;
-import com.yahoo.security.KeyStoreBuilder;
-import com.yahoo.security.KeyStoreType;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
-
-import java.nio.file.Paths;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.cert.X509Certificate;
-import java.time.Instant;
-
-/**
- * Programmatic configuration of configserver's truststore
- *
- * @author bjorncs
- */
-public class AthenzSslTrustStoreConfigurator implements SslTrustStoreConfigurator {
-
- private static final String CERTIFICATE_ALIAS = "cfgselfsigned";
-
- private final KeyStore trustStore;
-
- @Inject
- public AthenzSslTrustStoreConfigurator(AthenzProviderServiceConfig athenzProviderServiceConfig) {
- this.trustStore = createTrustStore(athenzProviderServiceConfig);
- }
-
- @Override
- public void configure(SslTrustStoreContext sslTrustStoreContext) {
- sslTrustStoreContext.updateTrustStore(trustStore);
- }
-
- Instant getTrustStoreExpiry() throws KeyStoreException {
- X509Certificate certificate = (X509Certificate) trustStore.getCertificate(CERTIFICATE_ALIAS);
- return certificate.getNotAfter().toInstant();
- }
-
- private static KeyStore createTrustStore(AthenzProviderServiceConfig athenzProviderServiceConfig) {
- try {
- return KeyStoreBuilder.withType(KeyStoreType.JKS)
- .fromFile(Paths.get(athenzProviderServiceConfig.athenzCaTrustStore()))
- .build();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
-}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/CertificateExpiryMetricUpdater.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/CertificateExpiryMetricUpdater.java
index 2d80b15c7ec..cd69099ea80 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/CertificateExpiryMetricUpdater.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/CertificateExpiryMetricUpdater.java
@@ -1,12 +1,10 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.athenz.instanceproviderservice;
+import com.google.inject.Inject;
import com.yahoo.component.AbstractComponent;
import com.yahoo.jdisc.Metric;
-import com.google.inject.Inject;
-
-import java.security.KeyStoreException;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.Executors;
@@ -21,23 +19,18 @@ import java.util.logging.Logger;
public class CertificateExpiryMetricUpdater extends AbstractComponent {
private static final Duration METRIC_REFRESH_PERIOD = Duration.ofMinutes(5);
- private static final String NODE_CA_CERT_METRIC_NAME = "node-ca-cert.expiry.seconds";
private static final String ATHENZ_CONFIGSERVER_CERT_METRIC_NAME = "athenz-configserver-cert.expiry.seconds";
private final Logger logger = Logger.getLogger(CertificateExpiryMetricUpdater.class.getName());
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private final Metric metric;
- private final AthenzSslKeyStoreConfigurator keyStoreConfigurator;
- private final AthenzSslTrustStoreConfigurator trustStoreConfigurator;
+ private final ConfigserverSslContextFactoryProvider provider;
@Inject
public CertificateExpiryMetricUpdater(Metric metric,
- AthenzSslKeyStoreConfigurator keyStoreConfigurator,
- AthenzSslTrustStoreConfigurator trustStoreConfigurator) {
+ ConfigserverSslContextFactoryProvider provider) {
this.metric = metric;
- this.keyStoreConfigurator = keyStoreConfigurator;
- this.trustStoreConfigurator = trustStoreConfigurator;
-
+ this.provider = provider;
scheduler.scheduleAtFixedRate(this::updateMetrics,
30/*initial delay*/,
@@ -56,20 +49,11 @@ public class CertificateExpiryMetricUpdater extends AbstractComponent {
}
private void updateMetrics() {
- Instant now = Instant.now();
-
try {
- Duration keyStoreExpiry = Duration.between(now, keyStoreConfigurator.getCertificateExpiry());
+ Duration keyStoreExpiry = Duration.between(Instant.now(), provider.getCertificateNotAfter());
metric.set(ATHENZ_CONFIGSERVER_CERT_METRIC_NAME, keyStoreExpiry.getSeconds(), null);
- } catch (KeyStoreException e) {
- logger.log(Level.WARNING, "Failed to update key store expiry metric", e);
- }
-
- try {
- Duration trustStoreExpiry = Duration.between(now, trustStoreConfigurator.getTrustStoreExpiry());
- metric.set(NODE_CA_CERT_METRIC_NAME, trustStoreExpiry.getSeconds(), null);
- } catch (KeyStoreException e) {
- logger.log(Level.WARNING, "Failed to update trust store expiry metric", e);
+ } catch (Exception e) {
+ logger.log(Level.WARNING, "Failed to update key store expiry metric: " + e.getMessage(), e);
}
}
}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ConfigserverSslContextFactoryProvider.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ConfigserverSslContextFactoryProvider.java
new file mode 100644
index 00000000000..6d06a36d332
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ConfigserverSslContextFactoryProvider.java
@@ -0,0 +1,199 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice;
+
+import com.google.inject.Inject;
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.config.provision.Zone;
+import com.yahoo.jdisc.http.ssl.SslContextFactoryProvider;
+import com.yahoo.log.LogLevel;
+import com.yahoo.security.KeyStoreBuilder;
+import com.yahoo.security.KeyStoreType;
+import com.yahoo.security.KeyUtils;
+import com.yahoo.vespa.athenz.api.AthenzService;
+import com.yahoo.vespa.athenz.client.zts.DefaultZtsClient;
+import com.yahoo.vespa.athenz.client.zts.Identity;
+import com.yahoo.vespa.athenz.client.zts.ZtsClient;
+import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider;
+import com.yahoo.vespa.athenz.utils.SiaUtils;
+import com.yahoo.vespa.defaults.Defaults;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+import java.net.URI;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils.getZoneConfig;
+
+/**
+ * Configures the JDisc https connector with the configserver's Athenz provider certificate and private key.
+ *
+ * @author bjorncs
+ */
+public class ConfigserverSslContextFactoryProvider extends AbstractComponent implements SslContextFactoryProvider {
+ private static final String CERTIFICATE_ALIAS = "athenz";
+ private static final Duration EXPIRATION_MARGIN = Duration.ofHours(6);
+ private static final Path VESPA_SIA_DIRECTORY = Paths.get(Defaults.getDefaults().underVespaHome("var/vespa/sia"));
+
+ private static final Logger log = Logger.getLogger(ConfigserverSslContextFactoryProvider.class.getName());
+
+ private final SslContextFactory sslContextFactory;
+ private final ScheduledExecutorService scheduler =
+ Executors.newSingleThreadScheduledExecutor(runnable -> new Thread(runnable, "configserver-ssl-context-factory-provider"));
+ private final ZtsClient ztsClient;
+ private final KeyProvider keyProvider;
+ private final AthenzProviderServiceConfig.Zones zoneConfig;
+ private final AthenzService configserverIdentity;
+
+ @Inject
+ public ConfigserverSslContextFactoryProvider(ServiceIdentityProvider bootstrapIdentity,
+ KeyProvider keyProvider,
+ AthenzProviderServiceConfig config,
+ Zone zone) {
+ this.zoneConfig = getZoneConfig(config, zone);
+ this.ztsClient = new DefaultZtsClient(URI.create(zoneConfig.ztsUrl()), bootstrapIdentity);
+ this.keyProvider = keyProvider;
+ this.configserverIdentity = new AthenzService(zoneConfig.domain(), zoneConfig.serviceName());
+
+ Duration updatePeriod = Duration.ofDays(config.updatePeriodDays());
+ Path trustStoreFile = Paths.get(config.athenzCaTrustStore());
+ this.sslContextFactory = initializeSslContextFactory(keyProvider, trustStoreFile, updatePeriod, configserverIdentity, ztsClient, zoneConfig);
+ scheduler.scheduleAtFixedRate(new KeystoreUpdater(sslContextFactory),
+ updatePeriod.toDays()/*initial delay*/,
+ updatePeriod.toDays(),
+ TimeUnit.DAYS);
+ }
+
+ @Override
+ public SslContextFactory getInstance(String containerId, int port) {
+ return sslContextFactory;
+ }
+
+ Instant getCertificateNotAfter() {
+ try {
+ X509Certificate certificate = (X509Certificate) sslContextFactory.getKeyStore().getCertificate(CERTIFICATE_ALIAS);
+ return certificate.getNotAfter().toInstant();
+ } catch (GeneralSecurityException e) {
+ throw new IllegalStateException("Unable to find configserver certificate from keystore: " + e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public void deconstruct() {
+ try {
+ scheduler.shutdownNow();
+ scheduler.awaitTermination(30, TimeUnit.SECONDS);
+ ztsClient.close();
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Failed to shutdown Athenz certificate updater on time", e);
+ }
+ }
+
+ private static SslContextFactory initializeSslContextFactory(KeyProvider keyProvider,
+ Path trustStoreFile,
+ Duration updatePeriod,
+ AthenzService configserverIdentity,
+ ZtsClient ztsClient,
+ AthenzProviderServiceConfig.Zones zoneConfig) {
+ SslContextFactory factory = new SslContextFactory();
+
+ // Allow safe TLS_RSA* ciphers
+ String[] excludedCiphersWithoutTlsRsaExclusion = Arrays.stream(factory.getExcludeCipherSuites())
+ .filter(cipher -> !cipher.equals("^TLS_RSA_.*$"))
+ .toArray(String[]::new);
+ factory.setExcludeCipherSuites(excludedCiphersWithoutTlsRsaExclusion);
+
+ factory.setWantClientAuth(true);
+
+ KeyStore trustStore =
+ KeyStoreBuilder.withType(KeyStoreType.JKS)
+ .fromFile(trustStoreFile)
+ .build();
+ factory.setTrustStore(trustStore);
+
+ KeyStore keyStore =
+ tryReadKeystoreFile(configserverIdentity, updatePeriod)
+ .orElseGet(() -> updateKeystore(configserverIdentity, generateKeystorePassword(), keyProvider, ztsClient, zoneConfig));
+ factory.setKeyStore(keyStore);
+ return factory;
+ }
+
+ private static Optional<KeyStore> tryReadKeystoreFile(AthenzService configserverIdentity, Duration updatePeriod) {
+ Optional<X509Certificate> certificate = SiaUtils.readCertificateFile(VESPA_SIA_DIRECTORY, configserverIdentity);
+ if (!certificate.isPresent()) return Optional.empty();
+ Optional<PrivateKey> privateKey = SiaUtils.readPrivateKeyFile(VESPA_SIA_DIRECTORY, configserverIdentity);
+ if (!privateKey.isPresent()) return Optional.empty();
+ Instant minimumExpiration = Instant.now().plus(updatePeriod).plus(EXPIRATION_MARGIN);
+ boolean isExpired = certificate.get().getNotAfter().toInstant().isBefore(minimumExpiration);
+ if (isExpired) return Optional.empty();
+ KeyStore keyStore = KeyStoreBuilder.withType(KeyStoreType.JKS)
+ .withKeyEntry(CERTIFICATE_ALIAS, privateKey.get(), certificate.get())
+ .build();
+ return Optional.of(keyStore);
+ }
+
+ private static KeyStore updateKeystore(AthenzService configserverIdentity,
+ char[] keystorePwd,
+ KeyProvider keyProvider,
+ ZtsClient ztsClient,
+ AthenzProviderServiceConfig.Zones zoneConfig) {
+ PrivateKey privateKey = keyProvider.getPrivateKey(zoneConfig.secretVersion());
+ PublicKey publicKey = KeyUtils.extractPublicKey(privateKey);
+ Identity serviceIdentity = ztsClient.getServiceIdentity(configserverIdentity,
+ Integer.toString(zoneConfig.secretVersion()),
+ new KeyPair(publicKey, privateKey),
+ zoneConfig.certDnsSuffix());
+ X509Certificate certificate = serviceIdentity.certificate();
+ SiaUtils.writeCertificateFile(VESPA_SIA_DIRECTORY, configserverIdentity, certificate);
+ SiaUtils.writePrivateKeyFile(VESPA_SIA_DIRECTORY, configserverIdentity, privateKey);
+ Instant expirationTime = certificate.getNotAfter().toInstant();
+ Duration expiry = Duration.between(certificate.getNotBefore().toInstant(), expirationTime);
+ log.log(LogLevel.INFO, String.format("Got Athenz x509 certificate with expiry %s (expires %s)", expiry, expirationTime));
+ return KeyStoreBuilder.withType(KeyStoreType.JKS)
+ .withKeyEntry(CERTIFICATE_ALIAS, privateKey, keystorePwd, certificate)
+ .build();
+ }
+
+ private static char[] generateKeystorePassword() {
+ return UUID.randomUUID().toString().toCharArray();
+ }
+
+ private class KeystoreUpdater implements Runnable {
+ final SslContextFactory sslContextFactory;
+
+ KeystoreUpdater(SslContextFactory sslContextFactory) {
+ this.sslContextFactory = sslContextFactory;
+ }
+
+ @Override
+ public void run() {
+ try {
+ log.log(LogLevel.INFO, "Updating configserver provider certificate from ZTS");
+ char[] keystorePwd = generateKeystorePassword();
+ KeyStore keyStore = updateKeystore(configserverIdentity, keystorePwd, keyProvider, ztsClient, zoneConfig);
+ sslContextFactory.reload(scf -> {
+ scf.setKeyStore(keyStore);
+ scf.setKeyStorePassword(new String(keystorePwd));
+ });
+ log.log(LogLevel.INFO, "Certificate successfully updated");
+ } catch (Throwable t) {
+ log.log(LogLevel.ERROR, "Failed to update certificate from ZTS: " + t.getMessage(), t);
+ }
+ }
+ }
+}