summaryrefslogtreecommitdiffstats
path: root/security-utils
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@verizonmedia.com>2019-04-05 01:13:07 +0200
committerBjørn Christian Seime <bjorncs@verizonmedia.com>2019-04-05 01:13:07 +0200
commit358c5690065056af8eb840c4f1b2ce6a9311d7f1 (patch)
tree875b18944bc5a4de88f6a5cfbb5ef3762b02d691 /security-utils
parent1811c8f1d2d11089d34999df976de40bfb8a15e6 (diff)
Revert "Remove TlsAwareHttpClientBuilder"
This reverts commit e962344ba28b9f84028a129a24c92b40fdc076b8.
Diffstat (limited to 'security-utils')
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityUtils.java9
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/https/TlsAwareHttpClient.java101
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/https/TlsAwareHttpClientBuilder.java97
-rw-r--r--security-utils/src/main/java/com/yahoo/security/tls/https/TlsAwareHttpRequest.java103
4 files changed, 310 insertions, 0 deletions
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityUtils.java b/security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityUtils.java
index f5f9182fc4e..2ea1e1efe83 100644
--- a/security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityUtils.java
+++ b/security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityUtils.java
@@ -1,6 +1,9 @@
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.security.tls;
+import com.yahoo.security.tls.https.TlsAwareHttpClientBuilder;
+
+import java.net.http.HttpClient;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
@@ -48,6 +51,12 @@ public class TransportSecurityUtils {
.map(configFile -> new ReloadingTlsContext(configFile, getInsecureAuthorizationMode()));
}
+ public static HttpClient.Builder createHttpClientBuilder(String userAgent) {
+ return createTlsContext()
+ .map(tlsContext -> new TlsAwareHttpClientBuilder(tlsContext, userAgent))
+ .orElseGet(() -> new TlsAwareHttpClientBuilder(userAgent));
+ }
+
private static Optional<String> getEnvironmentVariable(String environmentVariable) {
return Optional.ofNullable(System.getenv(environmentVariable))
.filter(var -> !var.isEmpty());
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/https/TlsAwareHttpClient.java b/security-utils/src/main/java/com/yahoo/security/tls/https/TlsAwareHttpClient.java
new file mode 100644
index 00000000000..2911b77707a
--- /dev/null
+++ b/security-utils/src/main/java/com/yahoo/security/tls/https/TlsAwareHttpClient.java
@@ -0,0 +1,101 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.security.tls.https;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import java.io.IOException;
+import java.net.Authenticator;
+import java.net.CookieHandler;
+import java.net.ProxySelector;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+
+/**
+ * A {@link HttpClient} that uses either http or https based on the global Vespa TLS configuration.
+ *
+ * @author bjorncs
+ */
+class TlsAwareHttpClient extends HttpClient {
+
+ private final HttpClient wrappedClient;
+ private final String userAgent;
+
+ TlsAwareHttpClient(HttpClient wrappedClient, String userAgent) {
+ this.wrappedClient = wrappedClient;
+ this.userAgent = userAgent;
+ }
+
+ @Override
+ public Optional<CookieHandler> cookieHandler() {
+ return wrappedClient.cookieHandler();
+ }
+
+ @Override
+ public Optional<Duration> connectTimeout() {
+ return wrappedClient.connectTimeout();
+ }
+
+ @Override
+ public Redirect followRedirects() {
+ return wrappedClient.followRedirects();
+ }
+
+ @Override
+ public Optional<ProxySelector> proxy() {
+ return wrappedClient.proxy();
+ }
+
+ @Override
+ public SSLContext sslContext() {
+ return wrappedClient.sslContext();
+ }
+
+ @Override
+ public SSLParameters sslParameters() {
+ return wrappedClient.sslParameters();
+ }
+
+ @Override
+ public Optional<Authenticator> authenticator() {
+ return wrappedClient.authenticator();
+ }
+
+ @Override
+ public Version version() {
+ return wrappedClient.version();
+ }
+
+ @Override
+ public Optional<Executor> executor() {
+ return wrappedClient.executor();
+ }
+
+ @Override
+ public <T> HttpResponse<T> send(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler) throws IOException, InterruptedException {
+ return wrappedClient.send(wrapRequest(request), responseBodyHandler);
+ }
+
+ @Override
+ public <T> CompletableFuture<HttpResponse<T>> sendAsync(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler) {
+ return wrappedClient.sendAsync(wrapRequest(request), responseBodyHandler);
+ }
+
+ @Override
+ public <T> CompletableFuture<HttpResponse<T>> sendAsync(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler, HttpResponse.PushPromiseHandler<T> pushPromiseHandler) {
+ return wrappedClient.sendAsync(wrapRequest(request), responseBodyHandler, pushPromiseHandler);
+ }
+
+ @Override
+ public String toString() {
+ return wrappedClient.toString();
+ }
+
+ private HttpRequest wrapRequest(HttpRequest request) {
+ return new TlsAwareHttpRequest(request, userAgent);
+ }
+}
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/https/TlsAwareHttpClientBuilder.java b/security-utils/src/main/java/com/yahoo/security/tls/https/TlsAwareHttpClientBuilder.java
new file mode 100644
index 00000000000..5a375cf663f
--- /dev/null
+++ b/security-utils/src/main/java/com/yahoo/security/tls/https/TlsAwareHttpClientBuilder.java
@@ -0,0 +1,97 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.security.tls.https;
+
+import com.yahoo.security.tls.TlsContext;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import java.net.Authenticator;
+import java.net.CookieHandler;
+import java.net.ProxySelector;
+import java.net.http.HttpClient;
+import java.time.Duration;
+import java.util.concurrent.Executor;
+
+/**
+ * A client builder for {@link HttpClient} which uses {@link TlsContext} for TLS configuration.
+ * Intended for internal Vespa communication only.
+ *
+ * @author bjorncs
+ */
+public class TlsAwareHttpClientBuilder implements HttpClient.Builder {
+
+ private final HttpClient.Builder wrappedBuilder;
+ private final String userAgent;
+
+ public TlsAwareHttpClientBuilder(String userAgent) {
+ this(null, userAgent);
+ }
+
+ public TlsAwareHttpClientBuilder(TlsContext tlsContext, String userAgent) {
+ this.wrappedBuilder = tlsContext != null ?
+ HttpClient.newBuilder().sslContext(tlsContext.context()).sslParameters(tlsContext.parameters()) :
+ HttpClient.newBuilder();
+ this.userAgent = userAgent;
+ }
+
+ @Override
+ public HttpClient.Builder cookieHandler(CookieHandler cookieHandler) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public HttpClient.Builder connectTimeout(Duration duration) {
+ wrappedBuilder.connectTimeout(duration);
+ return this;
+ }
+
+ @Override
+ public HttpClient.Builder sslContext(SSLContext sslContext) {
+ throw new UnsupportedOperationException("SSLContext is given from tls context");
+ }
+
+ @Override
+ public HttpClient.Builder sslParameters(SSLParameters sslParameters) {
+ throw new UnsupportedOperationException("SSLParameters is given from tls context");
+ }
+
+ @Override
+ public HttpClient.Builder executor(Executor executor) {
+ wrappedBuilder.executor(executor);
+ return this;
+ }
+
+ @Override
+ public HttpClient.Builder followRedirects(HttpClient.Redirect policy) {
+ wrappedBuilder.followRedirects(policy);
+ return this;
+ }
+
+ @Override
+ public HttpClient.Builder version(HttpClient.Version version) {
+ wrappedBuilder.version(version);
+ return this;
+ }
+
+ @Override
+ public HttpClient.Builder priority(int priority) {
+ wrappedBuilder.priority(priority);
+ return this;
+ }
+
+ @Override
+ public HttpClient.Builder proxy(ProxySelector proxySelector) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public HttpClient.Builder authenticator(Authenticator authenticator) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public HttpClient build() {
+ // TODO Stop wrapping the client once TLS is mandatory
+ return new TlsAwareHttpClient(wrappedBuilder.build(), userAgent);
+ }
+}
diff --git a/security-utils/src/main/java/com/yahoo/security/tls/https/TlsAwareHttpRequest.java b/security-utils/src/main/java/com/yahoo/security/tls/https/TlsAwareHttpRequest.java
new file mode 100644
index 00000000000..bbdd8af791f
--- /dev/null
+++ b/security-utils/src/main/java/com/yahoo/security/tls/https/TlsAwareHttpRequest.java
@@ -0,0 +1,103 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.security.tls.https;
+
+import com.yahoo.security.tls.MixedMode;
+import com.yahoo.security.tls.TransportSecurityUtils;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.http.HttpClient;
+import java.net.http.HttpHeaders;
+import java.net.http.HttpRequest;
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * A {@link HttpRequest} where the scheme is either http or https based on the global Vespa TLS configuration.
+ *
+ * @author bjorncs
+ */
+class TlsAwareHttpRequest extends HttpRequest {
+
+ private final URI rewrittenUri;
+ private final HttpRequest wrappedRequest;
+ private final HttpHeaders rewrittenHeaders;
+
+ TlsAwareHttpRequest(HttpRequest wrappedRequest, String userAgent) {
+ this.wrappedRequest = wrappedRequest;
+ this.rewrittenUri = rewriteUri(wrappedRequest.uri());
+ this.rewrittenHeaders = rewriteHeaders(wrappedRequest, userAgent);
+ }
+
+ @Override
+ public Optional<BodyPublisher> bodyPublisher() {
+ return wrappedRequest.bodyPublisher();
+ }
+
+ @Override
+ public String method() {
+ return wrappedRequest.method();
+ }
+
+ @Override
+ public Optional<Duration> timeout() {
+ return wrappedRequest.timeout();
+ }
+
+ @Override
+ public boolean expectContinue() {
+ return wrappedRequest.expectContinue();
+ }
+
+ @Override
+ public URI uri() {
+ return rewrittenUri;
+ }
+
+ @Override
+ public Optional<HttpClient.Version> version() {
+ return wrappedRequest.version();
+ }
+
+ @Override
+ public HttpHeaders headers() {
+ return rewrittenHeaders;
+ }
+
+ private static URI rewriteUri(URI uri) {
+ if (!uri.getScheme().equals("http")) {
+ return uri;
+ }
+ String rewrittenScheme =
+ TransportSecurityUtils.getConfigFile().isPresent() && TransportSecurityUtils.getInsecureMixedMode() != MixedMode.PLAINTEXT_CLIENT_MIXED_SERVER ?
+ "https" :
+ "http";
+ int port = uri.getPort();
+ int rewrittenPort = port != -1 ? port : (rewrittenScheme.equals("http") ? 80 : 443);
+ try {
+ return new URI(rewrittenScheme, uri.getUserInfo(), uri.getHost(), rewrittenPort, uri.getPath(), uri.getQuery(), uri.getFragment());
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static HttpHeaders rewriteHeaders(HttpRequest request, String userAgent) {
+ HttpHeaders headers = request.headers();
+ if (headers.firstValue("User-Agent").isPresent()) {
+ return headers;
+ }
+ HashMap<String, List<String>> rewrittenHeaders = new HashMap<>(headers.map());
+ rewrittenHeaders.put("User-Agent", List.of(userAgent));
+ return HttpHeaders.of(rewrittenHeaders, (ignored1, ignored2) -> true);
+ }
+
+ @Override
+ public String toString() {
+ return "TlsAwareHttpRequest{" +
+ "rewrittenUri=" + rewrittenUri +
+ ", wrappedRequest=" + wrappedRequest +
+ '}';
+ }
+}