diff options
Diffstat (limited to 'jdisc_http_service')
7 files changed, 165 insertions, 58 deletions
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactory.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactory.java index 8255e16e0ee..e4a21f2eb73 100644 --- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactory.java +++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactory.java @@ -2,12 +2,19 @@ package com.yahoo.jdisc.http.server.jetty; import com.google.inject.Inject; +import com.yahoo.config.InnerNode; import com.yahoo.jdisc.Metric; import com.yahoo.jdisc.http.ConnectorConfig; import com.yahoo.jdisc.http.ConnectorConfig.Ssl; +import com.yahoo.jdisc.http.ConnectorConfig.Ssl.ExcludeCipherSuite; +import com.yahoo.jdisc.http.ConnectorConfig.Ssl.ExcludeProtocol; +import com.yahoo.jdisc.http.ConnectorConfig.Ssl.IncludeCipherSuite; +import com.yahoo.jdisc.http.ConnectorConfig.Ssl.IncludeProtocol; import com.yahoo.jdisc.http.SecretStore; import com.yahoo.jdisc.http.ssl.DefaultSslKeyStoreContext; +import com.yahoo.jdisc.http.ssl.DefaultSslTrustStoreContext; import com.yahoo.jdisc.http.ssl.SslKeyStoreConfigurator; +import com.yahoo.jdisc.http.ssl.SslTrustStoreConfigurator; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; @@ -18,6 +25,10 @@ import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.util.ssl.SslContextFactory; import java.nio.channels.ServerSocketChannel; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; /** * @author Einar M R Rosenvinge @@ -26,27 +37,16 @@ import java.nio.channels.ServerSocketChannel; public class ConnectorFactory { private final ConnectorConfig connectorConfig; - private final SecretStore secretStore; private final SslKeyStoreConfigurator sslKeyStoreConfigurator; + private final SslTrustStoreConfigurator sslTrustStoreConfigurator; @Inject public ConnectorFactory(ConnectorConfig connectorConfig, - SecretStore secretStore, - SslKeyStoreConfigurator sslKeyStoreConfigurator) { + SslKeyStoreConfigurator sslKeyStoreConfigurator, + SslTrustStoreConfigurator sslTrustStoreConfigurator) { this.connectorConfig = connectorConfig; - this.secretStore = secretStore; this.sslKeyStoreConfigurator = sslKeyStoreConfigurator; - - if (connectorConfig.ssl().enabled()) - validateSslConfig(connectorConfig); - } - - // TODO: can be removed when we have dedicated SSL config in services.xml - private static void validateSslConfig(ConnectorConfig config) { - ConnectorConfig.Ssl ssl = config.ssl(); - if (!ssl.trustStorePath().isEmpty() && ssl.useTrustStorePassword() && ssl.keyDbKey().isEmpty()) { - throw new IllegalArgumentException("Missing password for JKS truststore"); - } + this.sslTrustStoreConfigurator = sslTrustStoreConfigurator; } public ConnectorConfig getConnectorConfig() { @@ -93,13 +93,13 @@ public class ConnectorFactory { return new HttpConnectionFactory(httpConfig); } - //TODO: does not support loading non-yahoo readable JKS key stores. private SslConnectionFactory newSslConnectionFactory() { Ssl sslConfig = connectorConfig.ssl(); SslContextFactory factory = new SslContextFactory(); sslKeyStoreConfigurator.configure(new DefaultSslKeyStoreContext(factory)); + sslTrustStoreConfigurator.configure(new DefaultSslTrustStoreContext(factory)); switch (sslConfig.clientAuth()) { case NEED_AUTH: @@ -114,49 +114,28 @@ public class ConnectorFactory { factory.setSecureRandomAlgorithm(sslConfig.prng()); } - if (!sslConfig.excludeProtocol().isEmpty()) { - String[] prots = new String[sslConfig.excludeProtocol().size()]; - for (int i = 0; i < prots.length; i++) { - prots[i] = sslConfig.excludeProtocol(i).name(); - } - factory.setExcludeProtocols(prots); - } - if (!sslConfig.includeProtocol().isEmpty()) { - String[] prots = new String[sslConfig.includeProtocol().size()]; - for (int i = 0; i < prots.length; i++) { - prots[i] = sslConfig.includeProtocol(i).name(); - } - factory.setIncludeProtocols(prots); - } - if (!sslConfig.excludeCipherSuite().isEmpty()) { - String[] ciphs = new String[sslConfig.excludeCipherSuite().size()]; - for (int i = 0; i < ciphs.length; i++) { - ciphs[i] = sslConfig.excludeCipherSuite(i).name(); - } - factory.setExcludeCipherSuites(ciphs); - - } - if (!sslConfig.includeCipherSuite().isEmpty()) { - String[] ciphs = new String[sslConfig.includeCipherSuite().size()]; - for (int i = 0; i < ciphs.length; i++) { - ciphs[i] = sslConfig.includeCipherSuite(i).name(); - } - factory.setIncludeCipherSuites(ciphs); - } - - String keyDbPassword = sslConfig.keyDbKey(); - - if (!sslConfig.trustStorePath().isEmpty()) { - factory.setTrustStorePath(sslConfig.trustStorePath()); - factory.setTrustStoreType(sslConfig.trustStoreType().toString()); - if (sslConfig.useTrustStorePassword()) { - factory.setTrustStorePassword(secretStore.getSecret(keyDbPassword)); - } - } + setStringArrayParameter( + factory, sslConfig.excludeProtocol(), ExcludeProtocol::name, SslContextFactory::setExcludeProtocols); + setStringArrayParameter( + factory, sslConfig.includeProtocol(), IncludeProtocol::name, SslContextFactory::setIncludeProtocols); + setStringArrayParameter( + factory, sslConfig.excludeCipherSuite(), ExcludeCipherSuite::name, SslContextFactory::setExcludeCipherSuites); + setStringArrayParameter( + factory, sslConfig.includeCipherSuite(), IncludeCipherSuite::name, SslContextFactory::setIncludeCipherSuites); factory.setKeyManagerFactoryAlgorithm(sslConfig.sslKeyManagerFactoryAlgorithm()); factory.setProtocol(sslConfig.protocol()); return new SslConnectionFactory(factory, HttpVersion.HTTP_1_1.asString()); } + private static <T extends InnerNode> void setStringArrayParameter(SslContextFactory sslContextFactory, + List<T> configValues, + Function<T, String> nameProperty, + BiConsumer<SslContextFactory, String[]> setter) { + if (!configValues.isEmpty()) { + String[] nameArray = configValues.stream().map(nameProperty).toArray(String[]::new); + setter.accept(sslContextFactory, nameArray); + } + } + } diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/DefaultSslTrustStoreConfigurator.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/DefaultSslTrustStoreConfigurator.java new file mode 100644 index 00000000000..8af21d48e9a --- /dev/null +++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/DefaultSslTrustStoreConfigurator.java @@ -0,0 +1,40 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.http.ssl; + +import com.google.inject.Inject; +import com.yahoo.jdisc.http.ConnectorConfig; +import com.yahoo.jdisc.http.SecretStore; + +/** + * @author bjorncs + */ +public class DefaultSslTrustStoreConfigurator implements SslTrustStoreConfigurator { + + private final SecretStore secretStore; + private final ConnectorConfig.Ssl config; + + @Inject + public DefaultSslTrustStoreConfigurator(ConnectorConfig config, SecretStore secretStore) { + validateConfig(config.ssl()); + this.secretStore = secretStore; + this.config = config.ssl(); + } + + @Override + public void configure(SslTrustStoreContext context) { + if (!config.enabled()) return; + String keyDbPassword = config.keyDbKey(); + if (!config.trustStorePath().isEmpty()) { + String password = config.useTrustStorePassword() ? secretStore.getSecret(keyDbPassword) : null; + context.updateTrustStore(config.trustStorePath(), config.trustStoreType().toString(), password); + } + } + + private static void validateConfig(ConnectorConfig.Ssl config) { + if (!config.enabled()) return; + if (!config.trustStorePath().isEmpty() && config.useTrustStorePassword() && config.keyDbKey().isEmpty()) { + throw new IllegalArgumentException("Missing password for JKS truststore"); + } + } + +} diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/DefaultSslTrustStoreContext.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/DefaultSslTrustStoreContext.java new file mode 100644 index 00000000000..c2d91cca3ea --- /dev/null +++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/DefaultSslTrustStoreContext.java @@ -0,0 +1,54 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.http.ssl; + +import org.eclipse.jetty.util.ssl.SslContextFactory; + +import java.security.KeyStore; +import java.util.function.Consumer; + +/** + * @author bjorncs + */ +public class DefaultSslTrustStoreContext implements SslTrustStoreContext { + + private final SslContextFactory sslContextFactory; + + public DefaultSslTrustStoreContext(SslContextFactory sslContextFactory) { + this.sslContextFactory = sslContextFactory; + } + + @Override + public void updateTrustStore(KeyStore trustStore) { + updateTrustStore(trustStore, null); + } + + @Override + public void updateTrustStore(KeyStore trustStore, String password) { + updateTrustStore(sslContextFactory -> { + sslContextFactory.setTrustStore(trustStore); + if (password != null) { + sslContextFactory.setTrustStorePassword(password); + } + }); + } + + @Override + public void updateTrustStore(String trustStorePath, String trustStoreType, String trustStorePassword) { + updateTrustStore(sslContextFactory -> { + sslContextFactory.setTrustStorePath(trustStorePath); + sslContextFactory.setTrustStoreType(trustStoreType); + if (trustStorePassword != null) { + sslContextFactory.setTrustStorePassword(trustStorePassword); + } + }); + } + + private void updateTrustStore(Consumer<SslContextFactory> reloader) { + try { + sslContextFactory.reload(reloader); + } catch (Exception e) { + throw new RuntimeException("Could not update truststore: " + e.getMessage(), e); + } + } + +} diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/SslTrustStoreConfigurator.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/SslTrustStoreConfigurator.java new file mode 100644 index 00000000000..de1119a5275 --- /dev/null +++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/SslTrustStoreConfigurator.java @@ -0,0 +1,14 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.http.ssl; + +/** + * An interface for an component that can configure an {@link SslTrustStoreContext}. The implementor can assume that + * the {@link SslTrustStoreContext} instance is thread-safe and be updated at any time + * during and after the call to{@link #configure(SslTrustStoreContext)}. + * Modifying the {@link SslKeyStoreContext} instance will trigger a hot reload of the truststore in JDisc. + * + * @author bjorncs + */ +public interface SslTrustStoreConfigurator { + void configure(SslTrustStoreContext context); +} diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/SslTrustStoreContext.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/SslTrustStoreContext.java new file mode 100644 index 00000000000..fc8cf397b24 --- /dev/null +++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/SslTrustStoreContext.java @@ -0,0 +1,16 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.http.ssl; + +import java.security.KeyStore; + +/** + * An interface to update the truststore in JDisc. Any update will trigger a hot reload and new connections will + * authenticated using the update truststore. + * + * @author bjorncs + */ +public interface SslTrustStoreContext { + void updateTrustStore(KeyStore trustStore); + void updateTrustStore(KeyStore trustStore, String password); + void updateTrustStore(String trustStorePath, String trustStoreType, String trustStorePassword); +} diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/guiceModules/ConnectorFactoryRegistryModule.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/guiceModules/ConnectorFactoryRegistryModule.java index 0d8f433cc39..6281907e083 100644 --- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/guiceModules/ConnectorFactoryRegistryModule.java +++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/guiceModules/ConnectorFactoryRegistryModule.java @@ -12,6 +12,7 @@ import com.yahoo.jdisc.http.SecretStore; import com.yahoo.jdisc.http.server.jetty.ConnectorFactory; import com.yahoo.jdisc.http.server.jetty.TestDrivers; import com.yahoo.jdisc.http.ssl.DefaultSslKeyStoreConfigurator; +import com.yahoo.jdisc.http.ssl.DefaultSslTrustStoreConfigurator; /** * Guice module for test ConnectorFactories @@ -48,8 +49,8 @@ public class ConnectorFactoryRegistryModule implements Module { public StaticKeyDbConnectorFactory(ConnectorConfig connectorConfig) { super(connectorConfig, - new MockSecretStore(), - new DefaultSslKeyStoreConfigurator(connectorConfig, new MockSecretStore())); + new DefaultSslKeyStoreConfigurator(connectorConfig, new MockSecretStore()), + new DefaultSslTrustStoreConfigurator(connectorConfig, new MockSecretStore())); } } diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactoryTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactoryTest.java index 781bc6a7b5f..103a317094b 100644 --- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactoryTest.java +++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactoryTest.java @@ -5,6 +5,7 @@ import com.yahoo.jdisc.Metric; import com.yahoo.jdisc.http.ConnectorConfig; import com.yahoo.jdisc.http.SecretStore; import com.yahoo.jdisc.http.ssl.DefaultSslKeyStoreConfigurator; +import com.yahoo.jdisc.http.ssl.DefaultSslTrustStoreConfigurator; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; @@ -107,7 +108,9 @@ public class ConnectorFactoryTest { private static ConnectorFactory createConnectorFactory(ConnectorConfig config) { ThrowingSecretStore secretStore = new ThrowingSecretStore(); - return new ConnectorFactory(config, secretStore, new DefaultSslKeyStoreConfigurator(config, secretStore)); + return new ConnectorFactory(config, + new DefaultSslKeyStoreConfigurator(config, secretStore), + new DefaultSslTrustStoreConfigurator(config, secretStore)); } private static class HelloWorldHandler extends AbstractHandler { |