diff options
author | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2019-12-04 12:00:17 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-12-04 12:00:17 +0100 |
commit | 27a428ab037690cb220138ac478540b219496642 (patch) | |
tree | 07d97fa349cdd8b2d38614f5167c8fe43e2c5303 /jdisc_http_service | |
parent | 4f2094a18ce8ff91f1e99187dffee22a76d29eda (diff) | |
parent | 3eb2a26b3183d876ebf38fc942b1f5802e1d66f8 (diff) |
Merge pull request #11494 from vespa-engine/bjorncs/jdisc-cipher-protocol-config
Bjorncs/jdisc cipher protocol config
Diffstat (limited to 'jdisc_http_service')
5 files changed, 105 insertions, 41 deletions
diff --git a/jdisc_http_service/abi-spec.json b/jdisc_http_service/abi-spec.json index 1615cf7e686..508a6a84974 100644 --- a/jdisc_http_service/abi-spec.json +++ b/jdisc_http_service/abi-spec.json @@ -119,9 +119,16 @@ "public com.yahoo.jdisc.http.ConnectorConfig$Ssl$Builder caCertificateFile(java.lang.String)", "public com.yahoo.jdisc.http.ConnectorConfig$Ssl$Builder caCertificate(java.lang.String)", "public com.yahoo.jdisc.http.ConnectorConfig$Ssl$Builder clientAuth(com.yahoo.jdisc.http.ConnectorConfig$Ssl$ClientAuth$Enum)", + "public com.yahoo.jdisc.http.ConnectorConfig$Ssl$Builder enabledCipherSuites(java.lang.String)", + "public com.yahoo.jdisc.http.ConnectorConfig$Ssl$Builder enabledCipherSuites(java.util.Collection)", + "public com.yahoo.jdisc.http.ConnectorConfig$Ssl$Builder enabledProtocols(java.lang.String)", + "public com.yahoo.jdisc.http.ConnectorConfig$Ssl$Builder enabledProtocols(java.util.Collection)", "public com.yahoo.jdisc.http.ConnectorConfig$Ssl build()" ], - "fields": [] + "fields": [ + "public java.util.List enabledCipherSuites", + "public java.util.List enabledProtocols" + ] }, "com.yahoo.jdisc.http.ConnectorConfig$Ssl$ClientAuth$Enum": { "superClass": "java.lang.Enum", @@ -174,7 +181,11 @@ "public java.lang.String certificate()", "public java.lang.String caCertificateFile()", "public java.lang.String caCertificate()", - "public com.yahoo.jdisc.http.ConnectorConfig$Ssl$ClientAuth$Enum clientAuth()" + "public com.yahoo.jdisc.http.ConnectorConfig$Ssl$ClientAuth$Enum clientAuth()", + "public java.util.List enabledCipherSuites()", + "public java.lang.String enabledCipherSuites(int)", + "public java.util.List enabledProtocols()", + "public java.lang.String enabledProtocols(int)" ], "fields": [] }, diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/impl/ConfiguredSslContextFactoryProvider.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/impl/ConfiguredSslContextFactoryProvider.java index 48a7c246500..b2e7ba1be67 100644 --- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/impl/ConfiguredSslContextFactoryProvider.java +++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/impl/ConfiguredSslContextFactoryProvider.java @@ -2,14 +2,16 @@ package com.yahoo.jdisc.http.ssl.impl; import com.yahoo.jdisc.http.ConnectorConfig; +import com.yahoo.jdisc.http.ConnectorConfig.Ssl.ClientAuth; import com.yahoo.jdisc.http.ssl.SslContextFactoryProvider; import com.yahoo.security.KeyUtils; +import com.yahoo.security.SslContextBuilder; import com.yahoo.security.X509CertificateUtils; -import com.yahoo.security.tls.DefaultTlsContext; -import com.yahoo.security.tls.PeerAuthentication; +import com.yahoo.security.tls.AutoReloadingX509KeyManager; import com.yahoo.security.tls.TlsContext; import org.eclipse.jetty.util.ssl.SslContextFactory; +import javax.net.ssl.SSLContext; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; @@ -17,16 +19,21 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.security.PrivateKey; import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.List; import java.util.Optional; +import static com.yahoo.jdisc.http.ssl.impl.SslContextFactoryUtils.setEnabledCipherSuites; +import static com.yahoo.jdisc.http.ssl.impl.SslContextFactoryUtils.setEnabledProtocols; + /** * An implementation of {@link SslContextFactoryProvider} that uses the {@link ConnectorConfig} to construct a {@link SslContextFactory}. * * @author bjorncs */ -public class ConfiguredSslContextFactoryProvider extends TlsContextBasedProvider { +public class ConfiguredSslContextFactoryProvider implements SslContextFactoryProvider { + private volatile AutoReloadingX509KeyManager keyManager; private final ConnectorConfig connectorConfig; public ConfiguredSslContextFactoryProvider(ConnectorConfig connectorConfig) { @@ -35,17 +42,50 @@ public class ConfiguredSslContextFactoryProvider extends TlsContextBasedProvider } @Override - protected TlsContext getTlsContext(String containerId, int port) { + public SslContextFactory getInstance(String containerId, int port) { ConnectorConfig.Ssl sslConfig = connectorConfig.ssl(); if (!sslConfig.enabled()) throw new IllegalStateException(); - PrivateKey privateKey = KeyUtils.fromPemEncodedPrivateKey(getPrivateKey(sslConfig)); - List<X509Certificate> certificates = X509CertificateUtils.certificateListFromPem(getCertificate(sslConfig)); + SslContextBuilder builder = new SslContextBuilder(); + if (sslConfig.certificateFile().isBlank() || sslConfig.privateKeyFile().isBlank()) { + PrivateKey privateKey = KeyUtils.fromPemEncodedPrivateKey(getPrivateKey(sslConfig)); + List<X509Certificate> certificates = X509CertificateUtils.certificateListFromPem(getCertificate(sslConfig)); + builder.withKeyStore(privateKey, certificates); + } else { + keyManager = AutoReloadingX509KeyManager.fromPemFiles(Paths.get(sslConfig.privateKeyFile()), Paths.get(sslConfig.certificateFile())); + builder.withKeyManager(keyManager); + } List<X509Certificate> caCertificates = getCaCertificates(sslConfig) .map(X509CertificateUtils::certificateListFromPem) .orElse(List.of()); - PeerAuthentication peerAuthentication = toPeerAuthentication(sslConfig.clientAuth()); - return new DefaultTlsContext(certificates, privateKey, caCertificates, null, null, peerAuthentication); + builder.withTrustStore(caCertificates); + + SSLContext sslContext = builder.build(); + + SslContextFactory.Server factory = new SslContextFactory.Server(); + factory.setSslContext(sslContext); + + factory.setNeedClientAuth(sslConfig.clientAuth() == ClientAuth.Enum.NEED_AUTH); + factory.setWantClientAuth(sslConfig.clientAuth() == ClientAuth.Enum.WANT_AUTH); + + List<String> protocols = !sslConfig.enabledProtocols().isEmpty() + ? sslConfig.enabledProtocols() + : new ArrayList<>(TlsContext.ALLOWED_PROTOCOLS); + setEnabledProtocols(factory, sslContext, protocols); + + List<String> ciphers = !sslConfig.enabledCipherSuites().isEmpty() + ? sslConfig.enabledCipherSuites() + : new ArrayList<>(TlsContext.ALLOWED_CIPHER_SUITES); + setEnabledCipherSuites(factory, sslContext, ciphers); + + return factory; + } + + @Override + public void close() { + if (keyManager != null) { + keyManager.close(); + } } private static void validateConfig(ConnectorConfig.Ssl config) { @@ -64,19 +104,6 @@ public class ConfiguredSslContextFactoryProvider extends TlsContextBasedProvider throw new IllegalArgumentException("Specified neither private key or private key file."); } - private static PeerAuthentication toPeerAuthentication(ConnectorConfig.Ssl.ClientAuth.Enum clientAuth) { - switch (clientAuth) { - case DISABLED: - return PeerAuthentication.DISABLED; - case NEED_AUTH: - return PeerAuthentication.NEED; - case WANT_AUTH: - return PeerAuthentication.WANT; - default: - throw new IllegalArgumentException("Unknown client auth: " + clientAuth); - } - } - private static boolean hasBoth(String a, String b) { return !a.isBlank() && !b.isBlank(); } private static boolean hasNeither(String a, String b) { return a.isBlank() && b.isBlank(); } diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/impl/SslContextFactoryUtils.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/impl/SslContextFactoryUtils.java new file mode 100644 index 00000000000..a0172668cbb --- /dev/null +++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/impl/SslContextFactoryUtils.java @@ -0,0 +1,32 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jdisc.http.ssl.impl; + +import org.eclipse.jetty.util.ssl.SslContextFactory; + +import javax.net.ssl.SSLContext; +import java.util.Arrays; +import java.util.List; + +/** + * @author bjorncs + */ +class SslContextFactoryUtils { + + static void setEnabledCipherSuites(SslContextFactory factory, SSLContext sslContext, List<String> enabledCiphers) { + String[] supportedCiphers = sslContext.getSupportedSSLParameters().getCipherSuites(); + factory.setIncludeCipherSuites(enabledCiphers.toArray(String[]::new)); + factory.setExcludeCipherSuites(createExclusionList(enabledCiphers, supportedCiphers)); + } + + static void setEnabledProtocols(SslContextFactory factory, SSLContext sslContext, List<String> enabledProtocols) { + String[] supportedProtocols = sslContext.getSupportedSSLParameters().getProtocols(); + factory.setIncludeProtocols(enabledProtocols.toArray(String[]::new)); + factory.setExcludeProtocols(createExclusionList(enabledProtocols, supportedProtocols)); + } + + private static String[] createExclusionList(List<String> enabledValues, String[] supportedValues) { + return Arrays.stream(supportedValues) + .filter(supportedValue -> !enabledValues.contains(supportedValue)) + .toArray(String[]::new); + } +} diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/impl/TlsContextBasedProvider.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/impl/TlsContextBasedProvider.java index e8ae13e48be..93d4f1dca3f 100644 --- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/impl/TlsContextBasedProvider.java +++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/ssl/impl/TlsContextBasedProvider.java @@ -8,7 +8,10 @@ import org.eclipse.jetty.util.ssl.SslContextFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLParameters; -import java.util.Arrays; +import java.util.List; + +import static com.yahoo.jdisc.http.ssl.impl.SslContextFactoryUtils.setEnabledCipherSuites; +import static com.yahoo.jdisc.http.ssl.impl.SslContextFactoryUtils.setEnabledProtocols; /** * A {@link SslContextFactoryProvider} that creates {@link SslContextFactory} instances from {@link TlsContext} instances. @@ -31,24 +34,9 @@ public abstract class TlsContextBasedProvider extends AbstractComponent implemen sslContextFactory.setNeedClientAuth(parameters.getNeedClientAuth()); sslContextFactory.setWantClientAuth(parameters.getWantClientAuth()); - String[] enabledProtocols = parameters.getProtocols(); - sslContextFactory.setIncludeProtocols(enabledProtocols); - String[] supportedProtocols = sslContext.getSupportedSSLParameters().getProtocols(); - sslContextFactory.setExcludeProtocols(createExclusionList(enabledProtocols, supportedProtocols)); + setEnabledProtocols(sslContextFactory, sslContext, List.of(parameters.getProtocols())); + setEnabledCipherSuites(sslContextFactory, sslContext, List.of(parameters.getCipherSuites())); - String[] enabledCiphers = parameters.getCipherSuites(); - String[] supportedCiphers = sslContext.getSupportedSSLParameters().getCipherSuites(); - sslContextFactory.setIncludeCipherSuites(enabledCiphers); - sslContextFactory.setExcludeCipherSuites(createExclusionList(enabledCiphers, supportedCiphers)); return sslContextFactory; } - - private static String[] createExclusionList(String[] enabledValues, String[] supportedValues) { - return Arrays.stream(supportedValues) - .filter(supportedValue -> - Arrays.stream(enabledValues) - .noneMatch(enabledValue -> enabledValue.equals(supportedValue))) - .toArray(String[]::new); - } - } diff --git a/jdisc_http_service/src/main/resources/configdefinitions/jdisc.http.connector.def b/jdisc_http_service/src/main/resources/configdefinitions/jdisc.http.connector.def index 1122b1db3a9..fe79ec2ffa3 100644 --- a/jdisc_http_service/src/main/resources/configdefinitions/jdisc.http.connector.def +++ b/jdisc_http_service/src/main/resources/configdefinitions/jdisc.http.connector.def @@ -81,6 +81,12 @@ ssl.caCertificate string default="" # Client authentication mode. See SSLEngine.getNeedClientAuth()/getWantClientAuth() for details. ssl.clientAuth enum { DISABLED, WANT_AUTH, NEED_AUTH } default=DISABLED +# List of enabled cipher suites. JDisc will use Vespa default if empty. +ssl.enabledCipherSuites[] string + +# List of enabled TLS protocol versions. JDisc will use Vespa default if empty. +ssl.enabledProtocols[] string + # Enforce TLS client authentication for https requests at the http layer. # Intended to be used with connectors with optional client authentication enabled. # 401 status code is returned for requests from non-authenticated clients. |