aboutsummaryrefslogtreecommitdiffstats
path: root/http-utils
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@yahooinc.com>2022-12-19 16:04:48 +0100
committerBjørn Christian Seime <bjorncs@yahooinc.com>2023-01-06 11:33:59 +0100
commit6e162af9a091d2ac1c229281c47349e46d6c8239 (patch)
tree7acb73d5a41283608bd07d96e3db7b8b56f87eca /http-utils
parent7d839355259eca823da9396c1ed15b43f7c98768 (diff)
Ensure that HTTPS clients only use allowed ciphers and protocol versions
Diffstat (limited to 'http-utils')
-rw-r--r--http-utils/src/main/java/ai/vespa/util/http/AcceptAllHostnamesVerifier.java21
-rw-r--r--http-utils/src/main/java/ai/vespa/util/http/hc4/SslConnectionSocketFactory.java54
-rw-r--r--http-utils/src/main/java/ai/vespa/util/http/hc4/VespaHttpClientBuilder.java7
-rw-r--r--http-utils/src/main/java/ai/vespa/util/http/hc5/DefaultHttpClientBuilder.java15
-rw-r--r--http-utils/src/main/java/ai/vespa/util/http/hc5/SslConnectionSocketFactory.java57
-rw-r--r--http-utils/src/main/java/ai/vespa/util/http/hc5/VespaHttpClientBuilder.java7
6 files changed, 140 insertions, 21 deletions
diff --git a/http-utils/src/main/java/ai/vespa/util/http/AcceptAllHostnamesVerifier.java b/http-utils/src/main/java/ai/vespa/util/http/AcceptAllHostnamesVerifier.java
new file mode 100644
index 00000000000..77d718bccb3
--- /dev/null
+++ b/http-utils/src/main/java/ai/vespa/util/http/AcceptAllHostnamesVerifier.java
@@ -0,0 +1,21 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.util.http;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLSession;
+
+/**
+ * @author bjorncs
+ */
+public class AcceptAllHostnamesVerifier implements HostnameVerifier {
+
+ private static final AcceptAllHostnamesVerifier INSTANCE = new AcceptAllHostnamesVerifier();
+
+ public static AcceptAllHostnamesVerifier instance() { return INSTANCE; }
+
+ private AcceptAllHostnamesVerifier() {}
+
+ @Override public boolean verify(String hostname, SSLSession session) { return true; }
+
+}
+
diff --git a/http-utils/src/main/java/ai/vespa/util/http/hc4/SslConnectionSocketFactory.java b/http-utils/src/main/java/ai/vespa/util/http/hc4/SslConnectionSocketFactory.java
new file mode 100644
index 00000000000..16449a72524
--- /dev/null
+++ b/http-utils/src/main/java/ai/vespa/util/http/hc4/SslConnectionSocketFactory.java
@@ -0,0 +1,54 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.util.http.hc4;
+
+import ai.vespa.util.http.AcceptAllHostnamesVerifier;
+import com.yahoo.security.tls.TlsContext;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import java.util.Collection;
+
+import static com.yahoo.security.tls.TlsContext.getAllowedCipherSuites;
+import static com.yahoo.security.tls.TlsContext.getAllowedProtocols;
+
+/**
+ * Provides {@link SSLConnectionSocketFactory} that applies protocol restrictions from {@link TlsContext}.
+ *
+ * @author bjorncs
+ */
+public class SslConnectionSocketFactory {
+ private SslConnectionSocketFactory() {}
+
+ public static SSLConnectionSocketFactory of(SSLContext ctx, HostnameVerifier verifier) {
+ return new SSLConnectionSocketFactory(ctx, protocols(ctx), cipherSuites(ctx), verifier);
+ }
+
+ public static SSLConnectionSocketFactory of(SSLContext ctx) { return of(ctx, defaultVerifier()); }
+
+ public static SSLConnectionSocketFactory of(TlsContext ctx, HostnameVerifier verifier) {
+ return new SSLConnectionSocketFactory(
+ ctx.context(), ctx.parameters().getProtocols(), ctx.parameters().getCipherSuites(), verifier);
+ }
+
+ public static SSLConnectionSocketFactory of(SSLSocketFactory fac, HostnameVerifier verifier) {
+ return new SSLConnectionSocketFactory(fac, protocols(), cipherSuites(), verifier);
+ }
+
+ public static SSLConnectionSocketFactory of() {
+ return new SSLConnectionSocketFactory(TlsContext.defaultSslContext(), protocols(), cipherSuites(), defaultVerifier());
+ }
+
+ public static SSLConnectionSocketFactory of(TlsContext ctx) { return of(ctx, defaultVerifier()); }
+
+ public static HostnameVerifier defaultVerifier() { return SSLConnectionSocketFactory.getDefaultHostnameVerifier(); }
+
+ public static HostnameVerifier noopVerifier() { return AcceptAllHostnamesVerifier.instance(); }
+
+ private static String[] cipherSuites(SSLContext ctx) { return array(getAllowedCipherSuites(ctx)); }
+ private static String[] protocols(SSLContext ctx) { return array(getAllowedProtocols(ctx)); }
+ private static String[] cipherSuites() { return array(getAllowedCipherSuites()); }
+ private static String[] protocols() { return array(getAllowedProtocols()); }
+ private static String[] array(Collection<String> c) { return c.toArray(String[]::new); }
+}
diff --git a/http-utils/src/main/java/ai/vespa/util/http/hc4/VespaHttpClientBuilder.java b/http-utils/src/main/java/ai/vespa/util/http/hc4/VespaHttpClientBuilder.java
index 953abcb04bc..af01b123a27 100644
--- a/http-utils/src/main/java/ai/vespa/util/http/hc4/VespaHttpClientBuilder.java
+++ b/http-utils/src/main/java/ai/vespa/util/http/hc4/VespaHttpClientBuilder.java
@@ -29,6 +29,8 @@ import java.net.InetAddress;
import java.util.logging.Level;
import java.util.logging.Logger;
+import static ai.vespa.util.http.hc4.SslConnectionSocketFactory.noopVerifier;
+
/**
* Http client builder for internal Vespa communications over http/https.
*
@@ -101,9 +103,8 @@ public class VespaHttpClientBuilder {
}
}
- private static SSLConnectionSocketFactory createSslSocketFactory(TlsContext tlsContext) {
- SSLParameters parameters = tlsContext.parameters();
- return new SSLConnectionSocketFactory(tlsContext.context(), parameters.getProtocols(), parameters.getCipherSuites(), new NoopHostnameVerifier());
+ private static SSLConnectionSocketFactory createSslSocketFactory(TlsContext ctx) {
+ return SslConnectionSocketFactory.of(ctx, noopVerifier());
}
private static Registry<ConnectionSocketFactory> createRegistry(SSLConnectionSocketFactory sslSocketFactory) {
diff --git a/http-utils/src/main/java/ai/vespa/util/http/hc5/DefaultHttpClientBuilder.java b/http-utils/src/main/java/ai/vespa/util/http/hc5/DefaultHttpClientBuilder.java
index 8866d67fd60..8575bc16ee8 100644
--- a/http-utils/src/main/java/ai/vespa/util/http/hc5/DefaultHttpClientBuilder.java
+++ b/http-utils/src/main/java/ai/vespa/util/http/hc5/DefaultHttpClientBuilder.java
@@ -1,19 +1,12 @@
package ai.vespa.util.http.hc5;
-import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
-import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.DefaultHostnameVerifier;
-import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
-import org.apache.hc.core5.http.ContentType;
-import org.apache.hc.core5.http.HttpHeaders;
-import org.apache.hc.core5.util.Timeout;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.time.Duration;
-import java.util.Map;
import java.util.function.Supplier;
/**
@@ -34,14 +27,12 @@ public class DefaultHttpClientBuilder {
/** Creates an HTTP client builder with the given SSL context, and using the provided timeouts for requests where config is not overridden. */
public static HttpClientBuilder create(Supplier<SSLContext> sslContext, HostnameVerifier verifier, String userAgent) {
+ SSLContext ctx = sslContext.get();
+ var factory = ctx == null ? SslConnectionSocketFactory.of(verifier) : SslConnectionSocketFactory.of(ctx, verifier);
return HttpClientBuilder.create()
.setConnectionManager(PoolingHttpClientConnectionManagerBuilder
.create()
- .setSSLSocketFactory(SSLConnectionSocketFactoryBuilder
- .create()
- .setSslContext(sslContext.get())
- .setHostnameVerifier(verifier)
- .build())
+ .setSSLSocketFactory(factory)
.build())
.setUserAgent(userAgent)
.disableCookieManagement()
diff --git a/http-utils/src/main/java/ai/vespa/util/http/hc5/SslConnectionSocketFactory.java b/http-utils/src/main/java/ai/vespa/util/http/hc5/SslConnectionSocketFactory.java
new file mode 100644
index 00000000000..7ba408c260b
--- /dev/null
+++ b/http-utils/src/main/java/ai/vespa/util/http/hc5/SslConnectionSocketFactory.java
@@ -0,0 +1,57 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.util.http.hc5;
+
+import ai.vespa.util.http.AcceptAllHostnamesVerifier;
+import com.yahoo.security.tls.TlsContext;
+import org.apache.hc.client5.http.ssl.HttpsSupport;
+import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+
+import java.util.Collection;
+
+import static com.yahoo.security.tls.TlsContext.getAllowedCipherSuites;
+import static com.yahoo.security.tls.TlsContext.getAllowedProtocols;
+
+/**
+ * Provides {@link SSLConnectionSocketFactory} that applies protocol restrictions from {@link TlsContext}.
+ *
+ * @author bjorncs
+ */
+public class SslConnectionSocketFactory {
+ private SslConnectionSocketFactory() {}
+
+ public static SSLConnectionSocketFactory of(SSLContext ctx, HostnameVerifier verifier) {
+ return new SSLConnectionSocketFactory(ctx, protocols(ctx), cipherSuites(ctx), verifier);
+ }
+
+ public static SSLConnectionSocketFactory of(SSLContext ctx) { return of(ctx, defaultVerifier()); }
+
+ public static SSLConnectionSocketFactory of(TlsContext ctx, HostnameVerifier verifier) {
+ return new SSLConnectionSocketFactory(
+ ctx.context(), ctx.parameters().getProtocols(), ctx.parameters().getCipherSuites(), verifier);
+ }
+
+ public static SSLConnectionSocketFactory of(TlsContext ctx) { return of(ctx, defaultVerifier()); }
+
+ public static SSLConnectionSocketFactory of(SSLSocketFactory fac, HostnameVerifier verifier) {
+ return new SSLConnectionSocketFactory(fac, protocols(), cipherSuites(), verifier);
+ }
+
+ public static SSLConnectionSocketFactory of(HostnameVerifier verifier) {
+ return of(TlsContext.defaultSslContext(), verifier);
+ }
+
+ public static HostnameVerifier defaultVerifier() { return HttpsSupport.getDefaultHostnameVerifier(); }
+
+ public static HostnameVerifier noopVerifier() { return AcceptAllHostnamesVerifier.instance(); }
+
+ private static String[] cipherSuites(SSLContext ctx) { return array(getAllowedCipherSuites(ctx)); }
+ private static String[] protocols(SSLContext ctx) { return array(getAllowedProtocols(ctx)); }
+ private static String[] cipherSuites() { return array(getAllowedCipherSuites()); }
+ private static String[] protocols() { return array(getAllowedProtocols()); }
+ private static String[] array(Collection<String> c) { return c.toArray(String[]::new); }
+
+}
diff --git a/http-utils/src/main/java/ai/vespa/util/http/hc5/VespaHttpClientBuilder.java b/http-utils/src/main/java/ai/vespa/util/http/hc5/VespaHttpClientBuilder.java
index 52f7ad9b56b..a33c4c119c2 100644
--- a/http-utils/src/main/java/ai/vespa/util/http/hc5/VespaHttpClientBuilder.java
+++ b/http-utils/src/main/java/ai/vespa/util/http/hc5/VespaHttpClientBuilder.java
@@ -12,7 +12,6 @@ import org.apache.hc.core5.http.config.Registry;
import org.apache.hc.core5.http.config.RegistryBuilder;
import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.SSLParameters;
import static com.yahoo.security.tls.MixedMode.PLAINTEXT_CLIENT_MIXED_SERVER;
import static com.yahoo.security.tls.TransportSecurityUtils.getInsecureMixedMode;
@@ -65,11 +64,7 @@ public class VespaHttpClientBuilder {
private static void addSslSocketFactory(HttpClientBuilder builder, HttpClientConnectionManagerFactory connectionManagerFactory,
HostnameVerifier hostnameVerifier) {
getSystemTlsContext().ifPresent(tlsContext -> {
- SSLParameters parameters = tlsContext.parameters();
- SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(tlsContext.context(),
- parameters.getProtocols(),
- parameters.getCipherSuites(),
- hostnameVerifier);
+ SSLConnectionSocketFactory socketFactory = SslConnectionSocketFactory.of(tlsContext, hostnameVerifier);
builder.setConnectionManager(connectionManagerFactory.create(createRegistry(socketFactory)));
// Workaround that allows re-using https connections, see https://stackoverflow.com/a/42112034/1615280 for details.
// Proper solution would be to add a request interceptor that adds a x500 principal as user token,