From 25d90380c726d3d2e1d7e9e09aac8ca65ed1cf1c Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 15 Feb 2021 22:14:39 +0100 Subject: [PATCH] Fixes #5973 - Proxy client TLS authentication example. Examples, in form of test cases for a proxy that uses TLS client authentication both towards the remote client and towards the server. Signed-off-by: Simone Bordet --- .../io/ssl/SslClientConnectionFactory.java | 27 +- .../jetty/proxy/ClientAuthProxyTest.java | 549 ++++++++++++++++++ .../resources/client_auth/client_keystore.p12 | Bin 0 -> 7953 bytes .../resources/client_auth/proxy_keystore.p12 | Bin 0 -> 7933 bytes .../resources/client_auth/server_keystore.p12 | Bin 0 -> 2565 bytes 5 files changed, 575 insertions(+), 1 deletion(-) create mode 100644 jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ClientAuthProxyTest.java create mode 100644 jetty-proxy/src/test/resources/client_auth/client_keystore.p12 create mode 100644 jetty-proxy/src/test/resources/client_auth/proxy_keystore.p12 create mode 100644 jetty-proxy/src/test/resources/client_auth/server_keystore.p12 diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java index 588794d2da96..ea7dd5f3094f 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java @@ -34,6 +34,9 @@ import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.ssl.SslContextFactory; +/** + *

A ClientConnectionFactory that creates client-side {@link SslConnection} instances.

+ */ public class SslClientConnectionFactory implements ClientConnectionFactory { public static final String SSL_CONTEXT_FACTORY_CONTEXT_KEY = "ssl.context.factory"; @@ -120,7 +123,10 @@ public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map context) return ClientConnectionFactory.super.customize(connection, context); } + /** + *

A factory for {@link SSLEngine} objects.

+ *

Typically implemented by {@link SslContextFactory.Client} + * to support more flexible creation of SSLEngine instances.

+ */ + public interface SslEngineFactory + { + /** + *

Creates a new {@link SSLEngine} instance for the given peer host and port, + * and with the given context to help the creation of the SSLEngine.

+ * + * @param host the peer host + * @param port the peer port + * @param context the {@link ClientConnectionFactory} context + * @return a new SSLEngine instance + */ + public SSLEngine newSslEngine(String host, int port, Map context); + } + private class HTTPSHandshakeListener implements SslHandshakeListener { private final Map context; diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ClientAuthProxyTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ClientAuthProxyTest.java new file mode 100644 index 000000000000..d6601e3700a8 --- /dev/null +++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ClientAuthProxyTest.java @@ -0,0 +1,549 @@ +// +// ======================================================================== +// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.proxy; + +import java.io.IOException; +import java.security.KeyStore; +import java.security.Principal; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLSession; +import javax.net.ssl.X509ExtendedKeyManager; +import javax.security.auth.x500.X500Principal; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpClientTransport; +import org.eclipse.jetty.client.HttpDestination; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; +import org.eclipse.jetty.http.HttpScheme; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.io.ClientConnectionFactory; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + *

Tests for client and proxy authentication using certificates.

+ *

There are 3 KeyStores:

+ *
+ *
{@code client_keystore.p12}
+ *
{@code server} -> the server certificate with CN=server
+ *
{@code user1_client} -> the client certificate for user1, signed with the server certificate
+ *
{@code user2_client} -> the client certificate for user2, signed with the server certificate
+ *
+ *
+ *
{@code proxy_keystore.p12}
+ *
{@code proxy} -> the proxy domain private key and certificate with CN=proxy
+ *
{@code server} -> the server domain certificate with CN=server
+ *
{@code user1_proxy} -> the proxy client certificate for user1, signed with the server certificate
+ *
{@code user2_proxy} -> the proxy client certificate for user2, signed with the server certificate
+ *
+ *
+ *
{@code server_keystore.p12}
+ *
{@code server} -> the server domain private key and certificate with CN=server, + * with extension ca:true to sign client and proxy certificates.
+ *
+ *

In this way, a remote client can connect to the proxy and be authenticated, + * and the proxy can connect to the server on behalf of that remote client, since + * the proxy has a certificate correspondent to the one of the remote client.

+ *

The main problem is to make sure that the {@code HttpClient} in the proxy uses different connections + * to connect to the same server, and that those connections are authenticated via TLS client certificate + * with the correct certificate, avoiding that requests made by {@code user2} are sent over connections + * that are authenticated with {@code user1} certificates.

+ */ +public class ClientAuthProxyTest +{ + private Server server; + private ServerConnector serverConnector; + private Server proxy; + private ServerConnector proxyConnector; + private HttpClient client; + + private void startServer(Handler handler) throws Exception + { + QueuedThreadPool serverThreads = new QueuedThreadPool(); + serverThreads.setName("server"); + server = new Server(serverThreads); + + HttpConfiguration httpConfig = new HttpConfiguration(); + httpConfig.addCustomizer(new SecureRequestCustomizer()); + HttpConnectionFactory http = new HttpConnectionFactory(httpConfig); + + SslContextFactory.Server serverTLS = new SslContextFactory.Server(); + serverTLS.setNeedClientAuth(true); + // The KeyStore is also a TrustStore. + serverTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/server_keystore.p12").getAbsolutePath()); + serverTLS.setKeyStorePassword("storepwd"); + serverTLS.setKeyStoreType("PKCS12"); + + SslConnectionFactory ssl = new SslConnectionFactory(serverTLS, http.getProtocol()); + + serverConnector = new ServerConnector(server, 1, 1, ssl, http); + server.addConnector(serverConnector); + + server.setHandler(handler); + + server.start(); + System.err.println("SERVER = localhost:" + serverConnector.getLocalPort()); + } + + private void startServer() throws Exception + { + startServer(new EmptyServerHandler() + { + @Override + protected void service(String target, org.eclipse.jetty.server.Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException + { + X509Certificate[] certificates = (X509Certificate[])request.getAttribute(SecureRequestCustomizer.JAVAX_SERVLET_REQUEST_X_509_CERTIFICATE); + Assertions.assertNotNull(certificates); + X509Certificate certificate = certificates[0]; + X500Principal principal = certificate.getSubjectX500Principal(); + ServletOutputStream output = response.getOutputStream(); + output.println(principal.toString()); + output.println(request.getRemotePort()); + } + }); + } + + private void startProxy(AbstractProxyServlet servlet) throws Exception + { + QueuedThreadPool proxyThreads = new QueuedThreadPool(); + proxyThreads.setName("proxy"); + proxy = new Server(); + + HttpConfiguration httpConfig = new HttpConfiguration(); + httpConfig.addCustomizer(new SecureRequestCustomizer()); + HttpConnectionFactory http = new HttpConnectionFactory(httpConfig); + + SslContextFactory.Server proxyTLS = new SslContextFactory.Server(); + proxyTLS.setNeedClientAuth(true); + // The KeyStore is also a TrustStore. + proxyTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/proxy_keystore.p12").getAbsolutePath()); + proxyTLS.setKeyStorePassword("storepwd"); + proxyTLS.setKeyStoreType("PKCS12"); + + SslConnectionFactory ssl = new SslConnectionFactory(proxyTLS, http.getProtocol()); + + proxyConnector = new ServerConnector(proxy, 1, 1, ssl, http); + proxy.addConnector(proxyConnector); + + ServletContextHandler context = new ServletContextHandler(proxy, "/"); + context.addServlet(new ServletHolder(servlet), "/*"); + proxy.setHandler(context); + + proxy.start(); + System.err.println("PROXY = localhost:" + proxyConnector.getLocalPort()); + } + + private void startClient() throws Exception + { + SslContextFactory.Client clientTLS = new SslContextFactory.Client(); + // Disable TLS-level hostname verification. + clientTLS.setEndpointIdentificationAlgorithm(null); + clientTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/client_keystore.p12").getAbsolutePath()); + clientTLS.setKeyStorePassword("storepwd"); + clientTLS.setKeyStoreType("PKCS12"); + client = new HttpClient(clientTLS); + QueuedThreadPool clientThreads = new QueuedThreadPool(); + clientThreads.setName("client"); + client.setExecutor(clientThreads); + client.start(); + } + + @AfterEach + public void dispose() throws Exception + { + LifeCycle.stop(client); + LifeCycle.stop(proxy); + LifeCycle.stop(server); + } + + private static String retrieveUser(HttpServletRequest request) + { + X509Certificate[] certificates = (X509Certificate[])request.getAttribute(SecureRequestCustomizer.JAVAX_SERVLET_REQUEST_X_509_CERTIFICATE); + String clientName = certificates[0].getSubjectX500Principal().getName(); + Matcher matcher = Pattern.compile("CN=([^,]+)").matcher(clientName); + if (matcher.find()) + { + // Retain only "userN". + return matcher.group(1).split("_")[0]; + } + return null; + } + + @Test + public void testClientAuthProxyingWithMultipleHttpClients() throws Exception + { + // Using a different HttpClient (with a different SslContextFactory.Client) + // per user works, but there is a lot of duplicated state in the HttpClients: + // Executors and Schedulers (although they can be shared), but also CookieManagers + // ProtocolHandlers, etc. + // The proxy has different SslContextFactory.Client statically configured + // for different users. + + startServer(); + startProxy(new AsyncProxyServlet() + { + private final Map httpClients = new ConcurrentHashMap<>(); + + @Override + protected Request newProxyRequest(HttpServletRequest request, String rewrittenTarget) + { + String user = retrieveUser(request); + HttpClient httpClient = getOrCreateHttpClient(user); + Request proxyRequest = httpClient.newRequest(rewrittenTarget) + .method(request.getMethod()) + .attribute(CLIENT_REQUEST_ATTRIBUTE, request); + // Send the request to the server. + proxyRequest.port(serverConnector.getLocalPort()); + // No need to tag the request when using different HttpClients. + return proxyRequest; + } + + private HttpClient getOrCreateHttpClient(String user) + { + if (user == null) + return getHttpClient(); + return httpClients.computeIfAbsent(user, key -> + { + SslContextFactory.Client clientTLS = new SslContextFactory.Client(); + // Disable TLS-level hostname verification for this test. + clientTLS.setEndpointIdentificationAlgorithm(null); + clientTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/proxy_keystore.p12").getAbsolutePath()); + clientTLS.setKeyStorePassword("storepwd"); + clientTLS.setKeyStoreType("PKCS12"); + clientTLS.setCertAlias(key + "_proxy"); + // TODO: httpClients should share Executor and Scheduler at least. + HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP(1), clientTLS); + LifeCycle.start(httpClient); + return httpClient; + }); + } + }); + startClient(); + + testRequestsFromRemoteClients(); + } + + @Test + public void testClientAuthProxyingWithMultipleServerSubDomains() throws Exception + { + // Another idea is to use multiple subdomains for the server, + // such as user1.server.com, user2.server.com, with the server + // providing a *.server.com certificate. + // The proxy must pick the right alias dynamically based on the + // remote client request. + // For this test we use 127.0.0.N addresses. + + startServer(); + startProxy(new AsyncProxyServlet() + { + private final AtomicInteger userIds = new AtomicInteger(); + private final Map subDomains = new ConcurrentHashMap<>(); + + @Override + protected Request newProxyRequest(HttpServletRequest request, String rewrittenTarget) + { + String user = retrieveUser(request); + // Obviously not fool proof, but for the 2 users in this test it does the job. + String subDomain = subDomains.computeIfAbsent(user, key -> "127.0.0." + userIds.incrementAndGet()); + Request proxyRequest = super.newProxyRequest(request, rewrittenTarget); + proxyRequest.host(subDomain).port(serverConnector.getLocalPort()); + // Tag the request. + proxyRequest.tag(new AliasTLSTag(user)); + return proxyRequest; + } + + @Override + protected HttpClient newHttpClient() + { + SslContextFactory.Client clientTLS = new SslContextFactory.Client() + { + @Override + protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception + { + KeyManager[] keyManagers = super.getKeyManagers(keyStore); + for (int i = 0; i < keyManagers.length; i++) + { + keyManagers[i] = new ProxyAliasX509ExtendedKeyManager(keyManagers[i]); + } + return keyManagers; + } + }; + // Disable TLS-level hostname verification for this test. + clientTLS.setEndpointIdentificationAlgorithm(null); + clientTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/proxy_keystore.p12").getAbsolutePath()); + clientTLS.setKeyStorePassword("storepwd"); + clientTLS.setKeyStoreType("PKCS12"); + return new HttpClient(new HttpClientTransportOverHTTP(1), clientTLS); + } + }); + startClient(); + + testRequestsFromRemoteClients(); + } + + @Test + public void testClientAuthProxyingWithSSLSessionResumptionDisabled() throws Exception + { + // To user the same HttpClient and server hostName, we need to disable + // SSLSession caching, which is only possible by creating SSLEngine + // without peer host information. + // This is more CPU intensive because TLS sessions can never be resumed. + + startServer(); + startProxy(new AsyncProxyServlet() + { + @Override + protected Request newProxyRequest(HttpServletRequest request, String rewrittenTarget) + { + String user = retrieveUser(request); + Request proxyRequest = super.newProxyRequest(request, rewrittenTarget); + proxyRequest.port(serverConnector.getLocalPort()); + // Tag the request. + proxyRequest.tag(new AliasTLSTag(user)); + return proxyRequest; + } + + @Override + protected HttpClient newHttpClient() + { + SslContextFactory.Client clientTLS = new SslContextFactory.Client() + { + @Override + public SSLEngine newSSLEngine(String host, int port) + { + // This disable TLS session resumption and requires + // endpointIdentificationAlgorithm=null because the TLS implementation + // does not have the peer host to verify the server certificate. + return newSSLEngine(); + } + + @Override + protected KeyManager[] getKeyManagers(KeyStore keyStore) throws Exception + { + KeyManager[] keyManagers = super.getKeyManagers(keyStore); + for (int i = 0; i < keyManagers.length; i++) + { + keyManagers[i] = new ProxyAliasX509ExtendedKeyManager(keyManagers[i]); + } + return keyManagers; + } + }; + // Disable hostname verification is required. + clientTLS.setEndpointIdentificationAlgorithm(null); + clientTLS.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/proxy_keystore.p12").getAbsolutePath()); + clientTLS.setKeyStorePassword("storepwd"); + clientTLS.setKeyStoreType("PKCS12"); + return new HttpClient(new HttpClientTransportOverHTTP(1), clientTLS); + + } + }); + startClient(); + + testRequestsFromRemoteClients(); + } + + @Test + public void testClientAuthProxyingWithCompositeSslContextFactory() throws Exception + { + // The idea here is to have a composite SslContextFactory that holds one for each user. + // It requires a change in SslClientConnectionFactory to "sniff" for the composite. + + startServer(); + startProxy(new AsyncProxyServlet() + { + @Override + protected Request newProxyRequest(HttpServletRequest request, String rewrittenTarget) + { + String user = retrieveUser(request); + Request proxyRequest = super.newProxyRequest(request, rewrittenTarget); + proxyRequest.port(serverConnector.getLocalPort()); + proxyRequest.tag(user); + return proxyRequest; + } + + @Override + protected HttpClient newHttpClient() + { + ProxyAliasClientSslContextFactory clientTLS = configure(new ProxyAliasClientSslContextFactory(), null); + // Statically add SslContextFactory.Client instances per each user. + clientTLS.factories.put("user1", configure(new SslContextFactory.Client(), "user1")); + clientTLS.factories.put("user2", configure(new SslContextFactory.Client(), "user2")); + return new HttpClient(new HttpClientTransportOverHTTP(1), clientTLS); + } + + private T configure(T tls, String user) + { + // Disable TLS-level hostname verification for this test. + tls.setEndpointIdentificationAlgorithm(null); + tls.setKeyStorePath(MavenTestingUtils.getTestResourceFile("client_auth/proxy_keystore.p12").getAbsolutePath()); + tls.setKeyStorePassword("storepwd"); + tls.setKeyStoreType("PKCS12"); + if (user != null) + { + tls.setCertAlias(user + "_proxy"); + LifeCycle.start(tls); + } + return tls; + } + }); + startClient(); + + testRequestsFromRemoteClients(); + } + + private void testRequestsFromRemoteClients() throws Exception + { + // User1 makes a request to the proxy using its own certificate. + SslContextFactory clientTLS = client.getSslContextFactory(); + clientTLS.reload(ssl -> ssl.setCertAlias("user1_client")); + ContentResponse response = client.newRequest("localhost", proxyConnector.getLocalPort()) + .scheme(HttpScheme.HTTPS.asString()) + .timeout(5, TimeUnit.SECONDS) + .tag("user1") + .send(); + Assertions.assertEquals(HttpStatus.OK_200, response.getStatus()); + String[] parts = response.getContentAsString().split("\n"); + String proxyClientSubject1 = parts[0]; + String proxyClientPort1 = parts[1]; + + // User2 makes a request to the proxy using its own certificate. + clientTLS.reload(ssl -> ssl.setCertAlias("user2_client")); + response = client.newRequest("localhost", proxyConnector.getLocalPort()) + .scheme(HttpScheme.HTTPS.asString()) + .timeout(5, TimeUnit.SECONDS) + .tag("user2") + .send(); + Assertions.assertEquals(HttpStatus.OK_200, response.getStatus()); + parts = response.getContentAsString().split("\n"); + String proxyClientSubject2 = parts[0]; + String proxyClientPort2 = parts[1]; + + Assertions.assertNotEquals(proxyClientSubject1, proxyClientSubject2); + Assertions.assertNotEquals(proxyClientPort1, proxyClientPort2); + } + + private static class AliasTLSTag implements ClientConnectionFactory.Decorator + { + private final String user; + + private AliasTLSTag(String user) + { + this.user = user; + } + + @Override + public ClientConnectionFactory apply(ClientConnectionFactory factory) + { + return (endPoint, context) -> + { + Connection connection = factory.newConnection(endPoint, context); + SSLEngine sslEngine = (SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY); + sslEngine.getSession().putValue("user", user); + return connection; + }; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null || getClass() != obj.getClass()) + return false; + AliasTLSTag that = (AliasTLSTag)obj; + return user.equals(that.user); + } + + @Override + public int hashCode() + { + return Objects.hash(user); + } + } + + private static class ProxyAliasX509ExtendedKeyManager extends SslContextFactory.X509ExtendedKeyManagerWrapper + { + private ProxyAliasX509ExtendedKeyManager(KeyManager keyManager) + { + super((X509ExtendedKeyManager)keyManager); + } + + @Override + public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine) + { + for (String keyType : keyTypes) + { + String[] aliases = getClientAliases(keyType, issuers); + if (aliases != null) + { + SSLSession sslSession = engine.getSession(); + String user = (String)sslSession.getValue("user"); + String alias = user + "_proxy"; + if (Arrays.asList(aliases).contains(alias)) + return alias; + } + } + return super.chooseEngineClientAlias(keyTypes, issuers, engine); + } + } + + private static class ProxyAliasClientSslContextFactory extends SslContextFactory.Client implements SslClientConnectionFactory.SslEngineFactory + { + private final Map factories = new ConcurrentHashMap<>(); + + @Override + public SSLEngine newSslEngine(String host, int port, Map context) + { + HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY); + String user = (String)destination.getOrigin().getTag(); + return factories.compute(user, (key, value) -> value != null ? value : this).newSSLEngine(host, port); + } + } +} diff --git a/jetty-proxy/src/test/resources/client_auth/client_keystore.p12 b/jetty-proxy/src/test/resources/client_auth/client_keystore.p12 new file mode 100644 index 0000000000000000000000000000000000000000..cf324d7ad20fe4f53c35eed084361675006c3923 GIT binary patch literal 7953 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 MHhp