From 8073f6df7bc0e7defd44d00c8789105c5c55945a Mon Sep 17 00:00:00 2001 From: bonampak <14160522+bonampak@users.noreply.github.com> Date: Fri, 24 Jan 2025 21:39:07 +0100 Subject: [PATCH 1/2] KNOX-3094: Update CM API swagger and okhttp dependencies --- gateway-discovery-cm/pom.xml | 8 +-- .../discovery/cm/DiscoveryApiClient.java | 64 ++++++++----------- .../auth/DoAsQueryParameterInterceptor.java | 45 +++++++++++++ .../cm/auth/SpnegoAuthInterceptor.java | 21 +++--- .../ClouderaManagerServiceDiscoveryTest.java | 2 +- .../util/TruststoreSSLContextUtils.java | 25 ++++++++ pom.xml | 8 +-- 7 files changed, 109 insertions(+), 64 deletions(-) create mode 100644 gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/auth/DoAsQueryParameterInterceptor.java diff --git a/gateway-discovery-cm/pom.xml b/gateway-discovery-cm/pom.xml index 2cb5ddb691..d3e4cd66a9 100644 --- a/gateway-discovery-cm/pom.xml +++ b/gateway-discovery-cm/pom.xml @@ -66,15 +66,9 @@ com.cloudera.api.swagger cloudera-manager-api-swagger - - - io.swagger - swagger-annotations - - - com.squareup.okhttp + com.squareup.okhttp3 okhttp diff --git a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/DiscoveryApiClient.java b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/DiscoveryApiClient.java index 5a74a9cc84..965d53cc18 100644 --- a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/DiscoveryApiClient.java +++ b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/DiscoveryApiClient.java @@ -26,14 +26,13 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; +import javax.net.ssl.X509TrustManager; import javax.security.auth.Subject; import com.cloudera.api.swagger.client.ApiClient; -import com.cloudera.api.swagger.client.Pair; -import com.cloudera.api.swagger.client.auth.Authentication; -import com.cloudera.api.swagger.client.auth.HttpBasicAuth; -import com.squareup.okhttp.ConnectionSpec; -import com.squareup.okhttp.OkHttpClient; +import okhttp3.ConnectionSpec; +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; import org.apache.knox.gateway.config.ConfigurationException; import org.apache.knox.gateway.config.GatewayConfig; import org.apache.knox.gateway.i18n.messages.MessagesFactory; @@ -41,6 +40,7 @@ import org.apache.knox.gateway.services.security.AliasServiceException; import org.apache.knox.gateway.topology.discovery.ServiceDiscoveryConfig; import org.apache.knox.gateway.topology.discovery.cm.auth.AuthUtils; +import org.apache.knox.gateway.topology.discovery.cm.auth.DoAsQueryParameterInterceptor; import org.apache.knox.gateway.topology.discovery.cm.auth.SpnegoAuthInterceptor; import org.apache.knox.gateway.util.TruststoreSSLContextUtils; @@ -126,55 +126,39 @@ private void configure(GatewayConfig gatewayConfig, AliasService aliasService, K setUsername(username); setPassword(password); - if (isKerberos) { + if (isKerberos()) { // If there is a Kerberos subject, then add the SPNEGO auth interceptor Subject subject = AuthUtils.getKerberosSubject(); if (subject != null) { - SpnegoAuthInterceptor spnegoInterceptor = new SpnegoAuthInterceptor(subject); - getHttpClient().interceptors().add(spnegoInterceptor); + addInterceptor(new SpnegoAuthInterceptor(subject)); } + addInterceptor(new DoAsQueryParameterInterceptor(username)); } configureTimeouts(gatewayConfig); configureSsl(gatewayConfig, trustStore); } - private void configureTimeouts(GatewayConfig config) { - OkHttpClient client = getHttpClient(); - client.setConnectTimeout(config.getServiceDiscoveryConnectTimeoutMillis(), TimeUnit.MILLISECONDS); - client.setReadTimeout(config.getServiceDiscoveryReadTimeoutMillis(), TimeUnit.MILLISECONDS); - client.setWriteTimeout(config.getServiceDiscoveryWriteTimeoutMillis(), TimeUnit.MILLISECONDS); - log.discoveryClientTimeout(client.getConnectTimeout(), client.getReadTimeout(), client.getWriteTimeout()); + private void addInterceptor(Interceptor interceptor) { + OkHttpClient newClient = getHttpClient().newBuilder().addInterceptor(interceptor).build(); + setHttpClient(newClient); } - @Override - public String buildUrl(String path, List queryParams) { - // If kerberos is enabled, then for every request, we're going to include a doAs query param - if (isKerberos()) { - String user = getUsername(); - if (user != null) { - queryParams.add(new Pair("doAs", user)); - } - } - return super.buildUrl(path, queryParams); - } - - /** - * @return The username set from the discovery configuration when this instance was initialized. - */ - private String getUsername() { - String username = null; - Authentication basicAuth = getAuthentication("basic"); - if (basicAuth instanceof HttpBasicAuth) { - username = ((HttpBasicAuth) basicAuth).getUsername(); - } - return username; + private void configureTimeouts(GatewayConfig config) { + OkHttpClient.Builder builder = getHttpClient().newBuilder(); + builder.connectTimeout(config.getServiceDiscoveryConnectTimeoutMillis(), TimeUnit.MILLISECONDS); + builder.readTimeout(config.getServiceDiscoveryReadTimeoutMillis(), TimeUnit.MILLISECONDS); + builder.writeTimeout(config.getServiceDiscoveryWriteTimeoutMillis(), TimeUnit.MILLISECONDS); + OkHttpClient client = builder.build(); + setHttpClient(client); + log.discoveryClientTimeout(client.connectTimeoutMillis(), client.readTimeoutMillis(), client.writeTimeoutMillis()); } private void configureSsl(GatewayConfig gatewayConfig, KeyStore trustStore) { final SSLContext truststoreSSLContext = TruststoreSSLContextUtils.getTruststoreSSLContext(trustStore); + final X509TrustManager trustManager = TruststoreSSLContextUtils.getTrustManager(trustStore); - if (truststoreSSLContext != null) { + if (truststoreSSLContext != null && trustManager != null) { final ConnectionSpec.Builder connectionSpecBuilder = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS); final List includedSslCiphers = gatewayConfig.getIncludedSSLCiphers(); if (includedSslCiphers == null || includedSslCiphers.isEmpty()) { @@ -188,8 +172,10 @@ private void configureSsl(GatewayConfig gatewayConfig, KeyStore trustStore) { } else { connectionSpecBuilder.tlsVersions(includedSslProtocols.toArray(new String[0])); } - getHttpClient().setConnectionSpecs(Arrays.asList(connectionSpecBuilder.build())); - getHttpClient().setSslSocketFactory(truststoreSSLContext.getSocketFactory()); + OkHttpClient.Builder builder = getHttpClient().newBuilder(); + builder.connectionSpecs(Arrays.asList(connectionSpecBuilder.build())); + builder.sslSocketFactory(truststoreSSLContext.getSocketFactory(), trustManager); + setHttpClient(builder.build()); } else { log.failedToConfigureTruststore(); } diff --git a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/auth/DoAsQueryParameterInterceptor.java b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/auth/DoAsQueryParameterInterceptor.java new file mode 100644 index 0000000000..041f0b421e --- /dev/null +++ b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/auth/DoAsQueryParameterInterceptor.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.knox.gateway.topology.discovery.cm.auth; + +import okhttp3.HttpUrl; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; + +import java.io.IOException; + +public class DoAsQueryParameterInterceptor implements Interceptor { + + private final String userName; + private static final String DO_AS_PRINCIPAL_PARAM = "doAs"; + + public DoAsQueryParameterInterceptor(String userName) { + this.userName = userName; + } + + @Override + public Response intercept(Interceptor.Chain chain) throws IOException { + HttpUrl url = chain.request().url().newBuilder() + .addQueryParameter(DO_AS_PRINCIPAL_PARAM, userName) + .build(); + Request request = chain.request().newBuilder() + .url(url) + .build(); + return chain.proceed(request); + } +} diff --git a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/auth/SpnegoAuthInterceptor.java b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/auth/SpnegoAuthInterceptor.java index 0122239c69..4adf33c61c 100644 --- a/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/auth/SpnegoAuthInterceptor.java +++ b/gateway-discovery-cm/src/main/java/org/apache/knox/gateway/topology/discovery/cm/auth/SpnegoAuthInterceptor.java @@ -16,10 +16,11 @@ */ package org.apache.knox.gateway.topology.discovery.cm.auth; -import com.squareup.okhttp.Authenticator; -import com.squareup.okhttp.Interceptor; -import com.squareup.okhttp.Request; -import com.squareup.okhttp.Response; +import okhttp3.Authenticator; +import okhttp3.Interceptor; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.Route; import org.ietf.jgss.GSSContext; import org.ietf.jgss.GSSCredential; @@ -32,7 +33,6 @@ import java.io.IOException; import java.net.InetAddress; -import java.net.Proxy; import java.net.UnknownHostException; import java.security.Principal; import java.security.PrivilegedActionException; @@ -102,7 +102,7 @@ private static boolean isNegotiate(String value) { } @Override - public Request authenticate(Proxy proxy, Response response) throws IOException { + public Request authenticate(Route route, Response response) throws IOException { // If already attempted or not challenged for Kerberos, then skip this attempt if (response.request().headers(AUTHORIZATION).stream().anyMatch(SpnegoAuthInterceptor::isNegotiate) || response.headers(WWW_AUTHENTICATE).stream().noneMatch(SpnegoAuthInterceptor::isNegotiate)) { @@ -112,13 +112,8 @@ public Request authenticate(Proxy proxy, Response response) throws IOException { return authenticate(response.request()); } - @Override - public Request authenticateProxy(Proxy proxy, Response response) throws IOException { - return null; // Not needed - } - private Request authenticate(Request request) { - String principal = defineServicePrincipal(remoteServiceName, request.url().getHost(), useCanonicalHostname); + String principal = defineServicePrincipal(remoteServiceName, request.url().host(), useCanonicalHostname); byte[] token = generateToken(principal); String credential = format(Locale.getDefault(), "%s %s", NEGOTIATE, Base64.getEncoder().encodeToString(token)); @@ -245,4 +240,4 @@ public boolean needsRefresh() throws GSSException { } } -} \ No newline at end of file +} diff --git a/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryTest.java b/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryTest.java index 9d98643ba7..0007e75ad3 100644 --- a/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryTest.java +++ b/gateway-discovery-cm/src/test/java/org/apache/knox/gateway/topology/discovery/cm/ClouderaManagerServiceDiscoveryTest.java @@ -30,7 +30,7 @@ import com.cloudera.api.swagger.model.ApiService; import com.cloudera.api.swagger.model.ApiServiceConfig; import com.cloudera.api.swagger.model.ApiServiceList; -import com.squareup.okhttp.Call; +import okhttp3.Call; import org.apache.knox.gateway.config.GatewayConfig; import org.apache.knox.gateway.services.security.AliasService; import org.apache.knox.gateway.topology.discovery.ServiceDiscovery; diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/util/TruststoreSSLContextUtils.java b/gateway-spi/src/main/java/org/apache/knox/gateway/util/TruststoreSSLContextUtils.java index 7fadf5ed2a..87b7dcfa46 100644 --- a/gateway-spi/src/main/java/org/apache/knox/gateway/util/TruststoreSSLContextUtils.java +++ b/gateway-spi/src/main/java/org/apache/knox/gateway/util/TruststoreSSLContextUtils.java @@ -20,8 +20,12 @@ import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; +import java.util.Arrays; import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.ssl.SSLContexts; @@ -48,4 +52,25 @@ public static SSLContext getTruststoreSSLContext(KeyStore truststore) { return sslContext; } + public static X509TrustManager getTrustManager(KeyStore truststore) { + try { + if (truststore != null) { + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(truststore); + TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); + if (trustManagers != null) { + for (TrustManager tm : trustManagers) { + if (tm instanceof X509TrustManager) { + return (X509TrustManager) tm; + } + } + } + throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers)); + } + } catch (KeyStoreException | NoSuchAlgorithmException | IllegalStateException e) { + LOGGER.failedToLoadTruststore(e.getMessage(), e); + } + return null; + } + } diff --git a/pom.xml b/pom.xml index 6b2c1a3458..e5d570c251 100644 --- a/pom.xml +++ b/pom.xml @@ -174,7 +174,7 @@ 1.4 3.3.0 8.38 - 7.8.1 + 7.13.1 ${cloudera-manager.version} 1.9.4 1.4 @@ -242,6 +242,7 @@ 2.9.0 2.4.9 4.13.2 + 1.9.10 1.5 1.11 2.17.1 @@ -256,7 +257,7 @@ 4.1.77.Final 9.37.3 v14.15.0 - 2.7.5 + 4.12.0 3.4.5 4.5.6 42.4.4 @@ -1570,11 +1571,10 @@ - com.squareup.okhttp + com.squareup.okhttp3 okhttp ${okhttp.version} - com.google.code.gson gson From c0f5f720635c8cf7ea0a06e1b67901b4b91de3a7 Mon Sep 17 00:00:00 2001 From: bonampak <14160522+bonampak@users.noreply.github.com> Date: Sat, 15 Feb 2025 22:38:48 +0100 Subject: [PATCH 2/2] KNOX-3094: Update gson-fire, gson and pin okio to 3.6.0. Resolve dependency convergence for org.jetbrains:annotations. --- pom.xml | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e5d570c251..59008308ea 100644 --- a/pom.xml +++ b/pom.xml @@ -208,7 +208,8 @@ 1.11.0 1.0.0 2.3.3 - 2.8.9 + 2.10.1 + 1.9.0 3.0.7 28.2-jre 3.2.4 @@ -263,6 +264,7 @@ 42.4.4 8.0.28 3.3.0 + 3.6.0 3.16.3 2.0.9 0.0.11.1 @@ -1575,6 +1577,32 @@ okhttp ${okhttp.version} + + com.squareup.okhttp3 + logging-interceptor + ${okhttp.version} + + + com.squareup.okio + okio + ${okio.version} + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin-stdlib.version} + + + org.jetbrains + annotations + + + + + io.gsonfire + gson-fire + ${gsonfire.version} + com.google.code.gson gson