aboutsummaryrefslogtreecommitdiffstats
path: root/jdisc_http_service/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'jdisc_http_service/src/main/java')
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java15
-rw-r--r--jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/TlsClientAuthenticationEnforcer.java78
2 files changed, 90 insertions, 3 deletions
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java
index 30a1b1d885c..2b9cb426dda 100644
--- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JettyHttpServer.java
@@ -9,6 +9,7 @@ import com.yahoo.component.provider.ComponentRegistry;
import com.yahoo.container.logging.AccessLog;
import com.yahoo.jdisc.Metric;
import com.yahoo.jdisc.application.OsgiFramework;
+import com.yahoo.jdisc.http.ConnectorConfig;
import com.yahoo.jdisc.http.ServerConfig;
import com.yahoo.jdisc.http.ServletPathsConfig;
import com.yahoo.jdisc.http.server.FilterBindings;
@@ -145,10 +146,13 @@ public class JettyHttpServer extends AbstractServerProvider {
setupJmx(server, serverConfig);
((QueuedThreadPool)server.getThreadPool()).setMaxThreads(serverConfig.maxWorkerThreads());
+ List<ConnectorConfig> connectorConfigs = new ArrayList<>();
for (ConnectorFactory connectorFactory : connectorFactories.allComponents()) {
- ServerSocketChannel preBoundChannel = getChannelFromServiceLayer(connectorFactory.getConnectorConfig().listenPort(), osgiFramework.bundleContext());
+ ConnectorConfig connectorConfig = connectorFactory.getConnectorConfig();
+ connectorConfigs.add(connectorConfig);
+ ServerSocketChannel preBoundChannel = getChannelFromServiceLayer(connectorConfig.listenPort(), osgiFramework.bundleContext());
server.addConnector(connectorFactory.createConnector(metric, server, preBoundChannel));
- listenedPorts.add(connectorFactory.getConnectorConfig().listenPort());
+ listenedPorts.add(connectorConfig.listenPort());
}
janitor = newJanitor(threadFactory);
@@ -168,6 +172,7 @@ public class JettyHttpServer extends AbstractServerProvider {
getHandlerCollection(
serverConfig,
servletPathsConfig,
+ connectorConfigs,
jdiscServlet,
servletHolders,
jDiscFilterInvokerFilter));
@@ -217,6 +222,7 @@ public class JettyHttpServer extends AbstractServerProvider {
private HandlerCollection getHandlerCollection(
ServerConfig serverConfig,
ServletPathsConfig servletPathsConfig,
+ List<ConnectorConfig> connectorConfigs,
ServletHolder jdiscServlet,
ComponentRegistry<ServletHolder> servletHolders,
FilterHolder jDiscFilterInvokerFilter) {
@@ -231,8 +237,11 @@ public class JettyHttpServer extends AbstractServerProvider {
servletContextHandler.addServlet(jdiscServlet, "/*");
+ var authEnforcer = new TlsClientAuthenticationEnforcer(connectorConfigs);
+ authEnforcer.setHandler(servletContextHandler);
+
GzipHandler gzipHandler = newGzipHandler(serverConfig);
- gzipHandler.setHandler(servletContextHandler);
+ gzipHandler.setHandler(authEnforcer);
HttpResponseStatisticsCollector statisticsCollector = new HttpResponseStatisticsCollector();
statisticsCollector.setHandler(gzipHandler);
diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/TlsClientAuthenticationEnforcer.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/TlsClientAuthenticationEnforcer.java
new file mode 100644
index 00000000000..546741b3322
--- /dev/null
+++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/TlsClientAuthenticationEnforcer.java
@@ -0,0 +1,78 @@
+// 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.server.jetty;
+
+import com.yahoo.jdisc.Response;
+import com.yahoo.jdisc.http.ConnectorConfig;
+import com.yahoo.jdisc.http.servlet.ServletRequest;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.HandlerWrapper;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A Jetty handler that enforces TLS client authentication with configurable white list.
+ *
+ * @author bjorncs
+ */
+class TlsClientAuthenticationEnforcer extends HandlerWrapper {
+
+ private final Map<Integer, List<String>> portToWhitelistedPathsMapping;
+
+ TlsClientAuthenticationEnforcer(List<ConnectorConfig> connectorConfigs) {
+ portToWhitelistedPathsMapping = createWhitelistMapping(connectorConfigs);
+ }
+
+ @Override
+ public void handle(String target, Request request, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException, ServletException {
+ if (isHttpsRequest(request)
+ && !isRequestToWhitelistedBinding(servletRequest)
+ && !isClientAuthenticated(servletRequest)) {
+ servletResponse.sendError(Response.Status.UNAUTHORIZED, "Client did not present a x509 certificate.");
+ } else {
+ _handler.handle(target, request, servletRequest, servletResponse);
+ }
+ }
+
+ private static Map<Integer, List<String>> createWhitelistMapping(List<ConnectorConfig> connectorConfigs) {
+ var mapping = new HashMap<Integer, List<String>>();
+ for (ConnectorConfig connectorConfig : connectorConfigs) {
+ var enforcerConfig = connectorConfig.tlsClientAuthEnforcer();
+ if (enforcerConfig.enable()) {
+ mapping.put(connectorConfig.listenPort(), enforcerConfig.pathWhitelist());
+ }
+ }
+ return mapping;
+ }
+
+ private boolean isHttpsRequest(Request request) {
+ return request.getDispatcherType() == DispatcherType.REQUEST && request.getScheme().equalsIgnoreCase("https");
+ }
+
+ private boolean isRequestToWhitelistedBinding(HttpServletRequest servletRequest) {
+ int localPort = servletRequest.getLocalPort();
+ List<String> whiteListedPaths = getWhitelistedPathsForPort(localPort);
+ if (whiteListedPaths == null) {
+ return true; // enforcer not enabled
+ }
+ // Note: Same path definition as HttpRequestFactory.getUri()
+ return whiteListedPaths.contains(servletRequest.getRequestURI());
+ }
+
+ private List<String> getWhitelistedPathsForPort(int localPort) {
+ if (portToWhitelistedPathsMapping.containsKey(0) && portToWhitelistedPathsMapping.size() == 1) {
+ return portToWhitelistedPathsMapping.get(0); // for unit tests which uses 0 for listen port
+ }
+ return portToWhitelistedPathsMapping.get(localPort);
+ }
+
+ private boolean isClientAuthenticated(HttpServletRequest servletRequest) {
+ return servletRequest.getAttribute(ServletRequest.SERVLET_REQUEST_X509CERT) != null;
+ }
+}