diff options
author | Bjørn Christian Seime <bjorn.christian@seime.no> | 2017-11-29 14:34:04 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-29 14:34:04 +0100 |
commit | 7b91729fda17a57f768b4e73aeee4765a99bf07d (patch) | |
tree | 3b1359a008666b228e27e6368742e2992f353d2e | |
parent | f4bace7c470cca2db836c8039dc42154e87ee5f0 (diff) | |
parent | 5a24610fab32f9df42f3fec475ea72cd67ae9d49 (diff) |
Merge pull request #4299 from vespa-engine/bjorncs/ssl-truststore-configurator
Bjorncs/ssl truststore configurator
12 files changed, 240 insertions, 90 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ConnectorFactory.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ConnectorFactory.java index 22c42056b3f..33f5edded3c 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ConnectorFactory.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ConnectorFactory.java @@ -5,6 +5,7 @@ import com.yahoo.component.ComponentId; import com.yahoo.container.bundle.BundleInstantiationSpecification; import com.yahoo.jdisc.http.ConnectorConfig; import com.yahoo.jdisc.http.ssl.DefaultSslKeyStoreConfigurator; +import com.yahoo.jdisc.http.ssl.DefaultSslTrustStoreConfigurator; import com.yahoo.osgi.provider.model.ComponentModel; import com.yahoo.text.XML; import com.yahoo.vespa.model.container.component.SimpleComponent; @@ -24,10 +25,14 @@ public class ConnectorFactory extends SimpleComponent implements ConnectorConfig private final Element legacyConfig; public ConnectorFactory(String name, int listenPort) { - this(name, listenPort, null, null); + this(name, listenPort, null, null, null); } - public ConnectorFactory(String name, int listenPort, Element legacyConfig, Element sslKeystoreConfigurator) { + public ConnectorFactory(String name, + int listenPort, + Element legacyConfig, + Element sslKeystoreConfigurator, + Element sslTruststoreConfigurator) { super(new ComponentModel( new BundleInstantiationSpecification(new ComponentId(name), fromString("com.yahoo.jdisc.http.server.jetty.ConnectorFactory"), @@ -35,9 +40,8 @@ public class ConnectorFactory extends SimpleComponent implements ConnectorConfig this.name = name; this.listenPort = listenPort; this.legacyConfig = legacyConfig; - SimpleComponent sslKeyStoreConfigurator = getSslKeyStoreConfigurator(name, sslKeystoreConfigurator); - addChild(sslKeyStoreConfigurator); - inject(sslKeyStoreConfigurator); + addSslKeyStoreConfigurator(name, sslKeystoreConfigurator); + addSslTrustStoreConfigurator(name, sslTruststoreConfigurator); } @Override @@ -149,16 +153,30 @@ public class ConnectorFactory extends SimpleComponent implements ConnectorConfig } } - private static SimpleComponent getSslKeyStoreConfigurator(String name, Element sslKeystoreConfigurator) { - String idSpec = "ssl-keystore-configurator@" + name; - if (sslKeystoreConfigurator != null) { - String className = sslKeystoreConfigurator.getAttribute("class"); - String bundleName = sslKeystoreConfigurator.getAttribute("bundle"); - return new SimpleComponent(new ComponentModel(idSpec, className, bundleName)); + private void addSslKeyStoreConfigurator(String name, Element sslKeystoreConfigurator) { + addSslConfigurator("ssl-keystore-configurator@" + name, + DefaultSslKeyStoreConfigurator.class, + sslKeystoreConfigurator); + } + + private void addSslTrustStoreConfigurator(String name, Element sslKeystoreConfigurator) { + addSslConfigurator("ssl-truststore-configurator@" + name, + DefaultSslTrustStoreConfigurator.class, + sslKeystoreConfigurator); + } + + private void addSslConfigurator(String idSpec, Class<?> defaultImplementation, Element configuratorElement) { + SimpleComponent configuratorComponent; + if (configuratorElement != null) { + String className = configuratorElement.getAttribute("class"); + String bundleName = configuratorElement.getAttribute("bundle"); + configuratorComponent = new SimpleComponent(new ComponentModel(idSpec, className, bundleName)); } else { - return new SimpleComponent( - new ComponentModel(idSpec, DefaultSslKeyStoreConfigurator.class.getName(), "jdisc_http_service")); + configuratorComponent = + new SimpleComponent(new ComponentModel(idSpec, defaultImplementation.getName(), "jdisc_http_service")); } + addChild(configuratorComponent); + inject(configuratorComponent); } } diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/JettyConnectorBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/JettyConnectorBuilder.java index f2012a609a7..f88c091cd37 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/JettyConnectorBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/xml/JettyConnectorBuilder.java @@ -12,8 +12,7 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a> - * @since 5.21.0 + * @author Einar M R Rosenvinge */ public class JettyConnectorBuilder extends VespaDomBuilder.DomConfigProducerBuilder<ConnectorFactory> { private static final Logger log = Logger.getLogger(JettyConnectorBuilder.class.getName()); @@ -34,7 +33,8 @@ public class JettyConnectorBuilder extends VespaDomBuilder.DomConfigProducerBuil } } Element sslKeystoreConfigurator = XML.getChild(serverSpec, "ssl-keystore-configurator"); - return new ConnectorFactory(name, port, legacyServerConfig, sslKeystoreConfigurator); + Element sslTruststoreConfigurator = XML.getChild(serverSpec, "ssl-truststore-configurator"); + return new ConnectorFactory(name, port, legacyServerConfig, sslKeystoreConfigurator, sslTruststoreConfigurator); } } diff --git a/config-model/src/main/resources/schema/containercluster.rnc b/config-model/src/main/resources/schema/containercluster.rnc index 6a90bef7bb2..95ac198adc4 100644 --- a/config-model/src/main/resources/schema/containercluster.rnc +++ b/config-model/src/main/resources/schema/containercluster.rnc @@ -62,6 +62,7 @@ HttpServer = element server { attribute port { xsd:nonNegativeInteger } & ComponentId & element ssl-keystore-configurator { BundleSpec }? & # FOR INTERNAL USE ONLY - SUBJECT TO CHANGE + element ssl-truststore-configurator { BundleSpec }? & # FOR INTERNAL USE ONLY - SUBJECT TO CHANGE GenericConfig* } diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JettyContainerModelBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JettyContainerModelBuilderTest.java index 1e24b055095..54c4aabf44c 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JettyContainerModelBuilderTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/JettyContainerModelBuilderTest.java @@ -7,6 +7,7 @@ import com.yahoo.container.bundle.BundleInstantiationSpecification; import com.yahoo.container.jdisc.FilterBindingsProvider; import com.yahoo.jdisc.http.ConnectorConfig; import com.yahoo.jdisc.http.ssl.DefaultSslKeyStoreConfigurator; +import com.yahoo.jdisc.http.ssl.DefaultSslTrustStoreConfigurator; import com.yahoo.vespa.model.container.ContainerCluster; import com.yahoo.vespa.model.container.component.SimpleComponent; import com.yahoo.vespa.model.container.http.ConnectorFactory; @@ -16,7 +17,9 @@ import org.w3c.dom.Element; import org.xml.sax.SAXException; import java.io.IOException; +import java.util.Arrays; import java.util.List; +import java.util.Set; import static com.yahoo.jdisc.http.ConnectorConfig.Ssl.KeyStoreType; import static org.hamcrest.CoreMatchers.equalTo; @@ -28,7 +31,6 @@ import static org.junit.Assert.assertThat; /** * @author einarmr - * @since 5.15 */ public class JettyContainerModelBuilderTest extends ContainerModelBuilderTestBase { @@ -190,12 +192,13 @@ public class JettyContainerModelBuilderTest extends ContainerModelBuilderTestBas } @Test - public void ssl_keystore_configurator_can_be_overriden() throws IOException, SAXException { + public void ssl_keystore_and_truststore_configurator_can_be_overriden() throws IOException, SAXException { Element clusterElem = DomBuilderTest.parse( "<jdisc id='default' version='1.0' jetty='true'>", " <http>", " <server port='9000' id='foo'>", " <ssl-keystore-configurator class='com.yahoo.MySslKeyStoreConfigurator' bundle='mybundle'/>", + " <ssl-truststore-configurator class='com.yahoo.MySslTrustStoreConfigurator' bundle='mybundle'/>", " </server>", " <server port='9001' id='bar'/>", " </http>", @@ -204,27 +207,47 @@ public class JettyContainerModelBuilderTest extends ContainerModelBuilderTestBas createModel(root, clusterElem); ContainerCluster cluster = (ContainerCluster) root.getChildren().get("default"); List<ConnectorFactory> connectorFactories = cluster.getChildrenByTypeRecursive(ConnectorFactory.class); - { ConnectorFactory firstConnector = connectorFactories.get(0); - assertThat(firstConnector.getInjectedComponentIds(), hasItem("ssl-keystore-configurator@foo")); - assertThat(firstConnector.getInjectedComponentIds().size(), equalTo(1)); - SimpleComponent sslKeystoreConfigurator = firstConnector.getChildrenByTypeRecursive(SimpleComponent.class).get(0); - BundleInstantiationSpecification spec = sslKeystoreConfigurator.model.bundleInstantiationSpec; - assertThat(spec.classId.toString(), is("com.yahoo.MySslKeyStoreConfigurator")); - assertThat(spec.bundle.toString(), is("mybundle")); + assertConnectorHasInjectedComponents(firstConnector, "ssl-keystore-configurator@foo", "ssl-truststore-configurator@foo"); + assertComponentHasClassNameAndBundle(getChildComponent(firstConnector, 0), + "com.yahoo.MySslKeyStoreConfigurator", + "mybundle"); + assertComponentHasClassNameAndBundle(getChildComponent(firstConnector, 1), + "com.yahoo.MySslTrustStoreConfigurator", + "mybundle"); } { - ConnectorFactory secondFactory = connectorFactories.get(1); - assertThat(secondFactory.getInjectedComponentIds(), hasItem("ssl-keystore-configurator@bar")); - assertThat(secondFactory.getInjectedComponentIds().size(), equalTo(1)); - SimpleComponent sslKeystoreConfigurator = secondFactory.getChildrenByTypeRecursive(SimpleComponent.class).get(0); - BundleInstantiationSpecification spec = sslKeystoreConfigurator.model.bundleInstantiationSpec; - assertThat(spec.classId.toString(), is(DefaultSslKeyStoreConfigurator.class.getName())); - assertThat(spec.bundle.toString(), is("jdisc_http_service")); + ConnectorFactory secondConnector = connectorFactories.get(1); + assertConnectorHasInjectedComponents(secondConnector, "ssl-keystore-configurator@bar", "ssl-truststore-configurator@bar"); + assertComponentHasClassNameAndBundle(getChildComponent(secondConnector, 0), + DefaultSslKeyStoreConfigurator.class.getName(), + "jdisc_http_service"); + assertComponentHasClassNameAndBundle(getChildComponent(secondConnector, 1), + DefaultSslTrustStoreConfigurator.class.getName(), + "jdisc_http_service"); } } + private static void assertConnectorHasInjectedComponents(ConnectorFactory connectorFactory, String... componentNames) { + Set<String> injectedComponentIds = connectorFactory.getInjectedComponentIds(); + assertThat(injectedComponentIds.size(), equalTo(componentNames.length)); + Arrays.stream(componentNames) + .forEach(name -> assertThat(injectedComponentIds, hasItem(name))); + } + + private static SimpleComponent getChildComponent(ConnectorFactory connectorFactory, int index) { + return connectorFactory.getChildrenByTypeRecursive(SimpleComponent.class).get(index); + } + + private static void assertComponentHasClassNameAndBundle(SimpleComponent simpleComponent, + String className, + String bundleName) { + BundleInstantiationSpecification spec = simpleComponent.model.bundleInstantiationSpec; + assertThat(spec.classId.toString(), is(className)); + assertThat(spec.bundle.toString(), is(bundleName)); + } + private void assertJettyServerInConfig() { ContainerCluster cluster = (ContainerCluster) root.getChildren().get("default"); List<JettyHttpServer> jettyServers = cluster.getChildrenByTypeRecursive(JettyHttpServer.class); diff --git a/config-model/src/test/schema-test-files/services.xml b/config-model/src/test/schema-test-files/services.xml index a02346193cc..af316c2e3a7 100644 --- a/config-model/src/test/schema-test-files/services.xml +++ b/config-model/src/test/schema-test-files/services.xml @@ -108,6 +108,7 @@ <server port="4080" id="myServer"> <ssl-keystore-configurator class="com.yahoo.MySslKeyStoreConfigurator" bundle="mybundle" /> + <ssl-truststore-configurator class="com.yahoo.MySslTrustStoreConfigurator" bundle="mybundle" /> </server> <server port="4081" id="anotherServer"> <config name="container.jdisc.config.http-server"> 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 { |