diff options
-rw-r--r-- | athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ConfigserverSslContextFactoryProvider.java | 80 |
1 files changed, 47 insertions, 33 deletions
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 index 971c2c00859..0eeeae457b6 100644 --- 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 @@ -2,17 +2,13 @@ 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.impl.TlsContextBasedProvider; +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.security.SslContextBuilder; -import com.yahoo.security.tls.DefaultTlsContext; -import com.yahoo.security.tls.MutableX509KeyManager; -import com.yahoo.security.tls.PeerAuthentication; -import com.yahoo.security.tls.TlsContext; import com.yahoo.vespa.athenz.api.AthenzService; import com.yahoo.vespa.athenz.client.zts.DefaultZtsClient; import com.yahoo.vespa.athenz.client.zts.Identity; @@ -21,11 +17,12 @@ 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 javax.net.ssl.SSLContext; 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; @@ -45,15 +42,14 @@ import java.util.logging.Logger; * * @author bjorncs */ -public class ConfigserverSslContextFactoryProvider extends TlsContextBasedProvider { +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 TlsContext tlsContext; - private final MutableX509KeyManager keyManager = new MutableX509KeyManager(); + private final SslContextFactory sslContextFactory; private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> new Thread(runnable, "configserver-ssl-context-factory-provider")); private final ZtsClient ztsClient; @@ -73,20 +69,25 @@ public class ConfigserverSslContextFactoryProvider extends TlsContextBasedProvid Duration updatePeriod = Duration.ofDays(config.updatePeriodDays()); Path trustStoreFile = Paths.get(config.athenzCaTrustStore()); - this.tlsContext = createTlsContext(keyProvider, keyManager, trustStoreFile, updatePeriod, configserverIdentity, ztsClient, athenzProviderServiceConfig); - scheduler.scheduleAtFixedRate(new KeystoreUpdater(keyManager), + this.sslContextFactory = initializeSslContextFactory(keyProvider, trustStoreFile, updatePeriod, configserverIdentity, ztsClient, athenzProviderServiceConfig); + scheduler.scheduleAtFixedRate(new KeystoreUpdater(sslContextFactory), updatePeriod.toDays()/*initial delay*/, updatePeriod.toDays(), TimeUnit.DAYS); } @Override - protected TlsContext getTlsContext(String containerId, int port) { - return tlsContext; + public SslContextFactory getInstance(String containerId, int port) { + return sslContextFactory; } Instant getCertificateNotAfter() { - return keyManager.currentManager().getCertificateChain(CERTIFICATE_ALIAS)[0].getNotAfter().toInstant(); + 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 @@ -95,28 +96,38 @@ public class ConfigserverSslContextFactoryProvider extends TlsContextBasedProvid scheduler.shutdownNow(); scheduler.awaitTermination(30, TimeUnit.SECONDS); ztsClient.close(); - super.deconstruct(); } catch (InterruptedException e) { throw new RuntimeException("Failed to shutdown Athenz certificate updater on time", e); } } - private static TlsContext createTlsContext(KeyProvider keyProvider, - MutableX509KeyManager keyManager, - Path trustStoreFile, - Duration updatePeriod, - AthenzService configserverIdentity, - ZtsClient ztsClient, - AthenzProviderServiceConfig zoneConfig) { + private static SslContextFactory initializeSslContextFactory(KeyProvider keyProvider, + Path trustStoreFile, + Duration updatePeriod, + AthenzService configserverIdentity, + ZtsClient ztsClient, + AthenzProviderServiceConfig zoneConfig) { + + // TODO Use DefaultTlsContext to configure SslContextFactory (ensure that cipher/protocol configuration is same across all TLS endpoints) + + SslContextFactory.Server factory = new SslContextFactory.Server(); + + 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)); - keyManager.updateKeystore(keyStore, new char[0]); - SSLContext sslContext = new SslContextBuilder() - .withTrustStore(trustStoreFile) - .withKeyManager(keyManager) - .build(); - return new DefaultTlsContext(sslContext, PeerAuthentication.WANT); + factory.setKeyStore(keyStore); + factory.setKeyStorePassword(""); + factory.setExcludeProtocols("TLSv1.3"); // TLSv1.3 is broken is multiple OpenJDK 11 versions + factory.setEndpointIdentificationAlgorithm(null); // disable https hostname verification of clients (must be disabled when using Athenz x509 certificates) + return factory; } private static Optional<KeyStore> tryReadKeystoreFile(AthenzService configserverIdentity, Duration updatePeriod) { @@ -160,10 +171,10 @@ public class ConfigserverSslContextFactoryProvider extends TlsContextBasedProvid } private class KeystoreUpdater implements Runnable { - final MutableX509KeyManager keyManager; + final SslContextFactory sslContextFactory; - KeystoreUpdater(MutableX509KeyManager keyManager) { - this.keyManager = keyManager; + KeystoreUpdater(SslContextFactory sslContextFactory) { + this.sslContextFactory = sslContextFactory; } @Override @@ -172,7 +183,10 @@ public class ConfigserverSslContextFactoryProvider extends TlsContextBasedProvid log.log(LogLevel.INFO, "Updating configserver provider certificate from ZTS"); char[] keystorePwd = generateKeystorePassword(); KeyStore keyStore = updateKeystore(configserverIdentity, keystorePwd, keyProvider, ztsClient, athenzProviderServiceConfig); - keyManager.updateKeystore(keyStore, keystorePwd); + 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); |