This is an automated email from the ASF dual-hosted git repository.

smolnar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git


The following commit(s) were added to refs/heads/master by this push:
     new ebac40dd4 KNOX-2831 - Make Hadoop impersonation work across topologies 
and roles with different proxyuser configs (#660)
ebac40dd4 is described below

commit ebac40dd4992d0e7fd7bd1ebf622a041dad2679c
Author: Sandor Molnar <[email protected]>
AuthorDate: Fri Oct 28 08:50:11 2022 +0200

    KNOX-2831 - Make Hadoop impersonation work across topologies and roles with 
different proxyuser configs (#660)
---
 .../gateway/hadoopauth/HadoopAuthMessages.java     |  3 -
 .../hadoopauth/filter/HadoopAuthFilter.java        | 15 ++--
 .../gateway/service/knoxtoken/TokenResource.java   |  9 +--
 .../service/knoxtoken/TokenServiceMessages.java    |  2 -
 .../deploy/TokenServiceDeploymentContributor.java  |  4 +-
 .../knoxtoken/TokenServiceResourceTest.java        |  1 +
 .../knox/gateway/i18n/GatewaySpiMessages.java      |  3 +
 .../apache/knox/gateway/util/AuthFilterUtils.java  | 87 ++++++++++++++++------
 8 files changed, 81 insertions(+), 43 deletions(-)

diff --git 
a/gateway-provider-security-hadoopauth/src/main/java/org/apache/knox/gateway/hadoopauth/HadoopAuthMessages.java
 
b/gateway-provider-security-hadoopauth/src/main/java/org/apache/knox/gateway/hadoopauth/HadoopAuthMessages.java
index 2b493a2da..98249a398 100755
--- 
a/gateway-provider-security-hadoopauth/src/main/java/org/apache/knox/gateway/hadoopauth/HadoopAuthMessages.java
+++ 
b/gateway-provider-security-hadoopauth/src/main/java/org/apache/knox/gateway/hadoopauth/HadoopAuthMessages.java
@@ -51,7 +51,4 @@ public interface HadoopAuthMessages {
   @Message(level=MessageLevel.WARN, text="{1} alias is NOT stored on neither 
topology ({0}) nor gateway levels.")
   void noAliasStored(String cluster, String alias);
 
-  @Message(level=MessageLevel.DEBUG, text="Refreshing proxyuser config in {0} 
topology with prefix {1} and config {2}")
-  void refreshProxyuserConfig(String topology, String prefix, String 
properties);
-
 }
diff --git 
a/gateway-provider-security-hadoopauth/src/main/java/org/apache/knox/gateway/hadoopauth/filter/HadoopAuthFilter.java
 
b/gateway-provider-security-hadoopauth/src/main/java/org/apache/knox/gateway/hadoopauth/filter/HadoopAuthFilter.java
index 44c202fff..edc2d3e88 100755
--- 
a/gateway-provider-security-hadoopauth/src/main/java/org/apache/knox/gateway/hadoopauth/filter/HadoopAuthFilter.java
+++ 
b/gateway-provider-security-hadoopauth/src/main/java/org/apache/knox/gateway/hadoopauth/filter/HadoopAuthFilter.java
@@ -17,9 +17,7 @@
  */
 package org.apache.knox.gateway.hadoopauth.filter;
 
-import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.security.authorize.AuthorizationException;
-import org.apache.hadoop.security.authorize.ProxyUsers;
 import org.apache.hadoop.util.HttpExceptionUtils;
 import org.apache.knox.gateway.GatewayServer;
 import org.apache.knox.gateway.audit.api.Action;
@@ -33,6 +31,7 @@ import 
org.apache.knox.gateway.audit.log4j.audit.AuditConstants;
 import org.apache.knox.gateway.config.GatewayConfig;
 import org.apache.knox.gateway.filter.AbstractGatewayFilter;
 import org.apache.knox.gateway.hadoopauth.HadoopAuthMessages;
+import 
org.apache.knox.gateway.hadoopauth.deploy.HadoopAuthDeploymentContributor;
 import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 import 
org.apache.knox.gateway.provider.federation.jwt.filter.JWTFederationFilter;
 import org.apache.knox.gateway.security.PrimaryPrincipal;
@@ -103,6 +102,7 @@ public class HadoopAuthFilter extends
   private final Set<String> ignoreDoAs = new HashSet<>();
   private JWTFederationFilter jwtFilter;
   private Set<String> unAuthenticatedPaths = new HashSet<>(20);
+  private String topologyName;
 
   @Override
   protected Properties getConfiguration(String configPrefix, FilterConfig 
filterConfig) throws ServletException {
@@ -114,13 +114,8 @@ public class HadoopAuthFilter extends
 
   @Override
   public void init(FilterConfig filterConfig) throws ServletException {
-    final String topologyName = (String) 
filterConfig.getInitParameter("clusterName");
-    // Return a {@link Configuration} instance with the proxy user
-    // (<code>hadoop.proxyuser.*</code>) properties set using parameter 
information
-    // from the filterConfig.
-    final Configuration conf = 
AuthFilterUtils.getProxyUserConfiguration(filterConfig, PROXYUSER_PREFIX);
-    ProxyUsers.refreshSuperUserGroupsConfiguration(conf, PROXYUSER_PREFIX);
-    LOG.refreshProxyuserConfig(topologyName, PROXYUSER_PREFIX, 
conf.getPropsWithPrefix(PROXYUSER_PREFIX).toString());
+    this.topologyName = (String) filterConfig.getInitParameter("clusterName");
+    AuthFilterUtils.refreshSuperUserGroupsConfiguration(filterConfig, 
PROXYUSER_PREFIX, topologyName, HadoopAuthDeploymentContributor.NAME);
 
     Collection<String> ignoredServices = null;
 
@@ -210,7 +205,7 @@ public class HadoopAuthFilter extends
         LOG.hadoopAuthDoAsUser(doAsUser, remoteUser, request.getRemoteAddr());
         if (request.getUserPrincipal() != null) {
           try {
-            proxyRequest = AuthFilterUtils.getProxyRequest(request, doAsUser);
+            proxyRequest = AuthFilterUtils.getProxyRequest(request, doAsUser, 
topologyName, HadoopAuthDeploymentContributor.NAME);
             LOG.hadoopAuthProxyUserSuccess();
           } catch (AuthorizationException ex) {
             HttpExceptionUtils.createServletExceptionResponse(response, 
HttpServletResponse.SC_FORBIDDEN, ex);
diff --git 
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
 
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
index 5c2fd4ca8..ac3d5d0b1 100644
--- 
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
+++ 
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
@@ -61,13 +61,12 @@ import com.nimbusds.jose.crypto.MACSigner;
 import com.nimbusds.jose.util.ByteUtils;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.security.authorize.AuthorizationException;
-import org.apache.hadoop.security.authorize.ProxyUsers;
 import org.apache.knox.gateway.config.GatewayConfig;
 import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 import org.apache.knox.gateway.security.GroupPrincipal;
 import org.apache.knox.gateway.security.SubjectUtils;
+import 
org.apache.knox.gateway.service.knoxtoken.deploy.TokenServiceDeploymentContributor;
 import org.apache.knox.gateway.services.ServiceType;
 import org.apache.knox.gateway.services.GatewayServices;
 import org.apache.knox.gateway.services.ServiceLifecycleException;
@@ -325,9 +324,7 @@ public class TokenResource {
       // refreshing Hadoop ProxyUser groups config only makes sense if token 
state management is turned on
       // and impersonation is enabled
       if (impersonationEnabled) {
-        final Configuration conf = 
AuthFilterUtils.getProxyUserConfiguration(context, PROXYUSER_PREFIX);
-        ProxyUsers.refreshSuperUserGroupsConfiguration(conf, PROXYUSER_PREFIX);
-        log.refreshProxyuserConfig(topologyName, PROXYUSER_PREFIX, 
conf.getPropsWithPrefix(PROXYUSER_PREFIX).toString());
+        AuthFilterUtils.refreshSuperUserGroupsConfiguration(context, 
PROXYUSER_PREFIX, getTopologyName(), TokenServiceDeploymentContributor.ROLE);
       }
     }
     setTokenStateServiceStatusMap();
@@ -728,7 +725,7 @@ public class TokenResource {
       if (doAsUser != null && !doAsUser.equals(userName)) {
         try {
           //this call will authorize the doAs request
-          AuthFilterUtils.authorizeImpersonationRequest(request, doAsUser);
+          AuthFilterUtils.authorizeImpersonationRequest(request, doAsUser, 
getTopologyName(), TokenServiceDeploymentContributor.ROLE);
           createdBy = userName;
           userName = doAsUser;
           log.tokenImpersonationSuccess(createdBy, doAsUser);
diff --git 
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceMessages.java
 
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceMessages.java
index b0b4eb3f6..5d0254e35 100644
--- 
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceMessages.java
+++ 
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceMessages.java
@@ -96,6 +96,4 @@ public interface TokenServiceMessages {
   @Message( level = MessageLevel.DEBUG, text = "Token impersonation failed: 
{0}" )
   void tokenImpersonationFailed(@StackTrace Throwable t);
 
-  @Message(level=MessageLevel.DEBUG, text="Refreshing proxyuser config in {0} 
topology with prefix {1} and config {2}")
-  void refreshProxyuserConfig(String topology, String prefix, String 
properties);
 }
diff --git 
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/deploy/TokenServiceDeploymentContributor.java
 
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/deploy/TokenServiceDeploymentContributor.java
index 830c66084..b46ddebd6 100644
--- 
a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/deploy/TokenServiceDeploymentContributor.java
+++ 
b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/deploy/TokenServiceDeploymentContributor.java
@@ -21,9 +21,11 @@ import 
org.apache.knox.gateway.jersey.JerseyServiceDeploymentContributorBase;
 
 public class TokenServiceDeploymentContributor extends 
JerseyServiceDeploymentContributorBase {
 
+  public static final String ROLE = "KNOXTOKEN";
+
   @Override
   public String getRole() {
-    return "KNOXTOKEN";
+    return ROLE;
   }
 
   @Override
diff --git 
a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
 
b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
index 64db322e8..e61c93ad3 100644
--- 
a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
+++ 
b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
@@ -155,6 +155,7 @@ public class TokenServiceResourceTest {
     context = EasyMock.createNiceMock(ServletContext.class);
     contextExpectations.forEach((key, value) -> 
EasyMock.expect(context.getInitParameter(key)).andReturn(value).anyTimes());
     
EasyMock.expect(context.getInitParameterNames()).andReturn(Collections.enumeration(contextExpectations.keySet())).anyTimes();
+    
EasyMock.expect(context.getAttribute("org.apache.knox.gateway.gateway.cluster")).andReturn("topology1").anyTimes();
     request = EasyMock.createNiceMock(HttpServletRequest.class);
     EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
     Principal principal = EasyMock.createNiceMock(Principal.class);
diff --git 
a/gateway-spi/src/main/java/org/apache/knox/gateway/i18n/GatewaySpiMessages.java
 
b/gateway-spi/src/main/java/org/apache/knox/gateway/i18n/GatewaySpiMessages.java
index 023695fe3..48f6ca643 100644
--- 
a/gateway-spi/src/main/java/org/apache/knox/gateway/i18n/GatewaySpiMessages.java
+++ 
b/gateway-spi/src/main/java/org/apache/knox/gateway/i18n/GatewaySpiMessages.java
@@ -81,4 +81,7 @@ public interface GatewaySpiMessages {
 
   @Message(level = MessageLevel.WARN, text = "Duplicated filter param key: 
{0}")
   void duplicatedFilterParamKey(String name);
+
+  @Message(level=MessageLevel.DEBUG, text="Creating impersonation provider in 
{0} / {1} with prefix {2} and config {3}")
+  void createImpersonationProvider(String topology, String role, String 
prefix, String properties);
 }
diff --git 
a/gateway-spi/src/main/java/org/apache/knox/gateway/util/AuthFilterUtils.java 
b/gateway-spi/src/main/java/org/apache/knox/gateway/util/AuthFilterUtils.java
index aeea2287d..08b82559f 100644
--- 
a/gateway-spi/src/main/java/org/apache/knox/gateway/util/AuthFilterUtils.java
+++ 
b/gateway-spi/src/main/java/org/apache/knox/gateway/util/AuthFilterUtils.java
@@ -17,11 +17,15 @@
  */
 package org.apache.knox.gateway.util;
 
-import org.apache.commons.lang3.StringUtils;
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.security.UserGroupInformation;
-import org.apache.hadoop.security.authorize.AuthorizationException;
-import org.apache.hadoop.security.authorize.ProxyUsers;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 
 import javax.servlet.FilterConfig;
 import javax.servlet.ServletContext;
@@ -29,14 +33,22 @@ import javax.servlet.ServletRequest;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequestWrapper;
 
-import java.security.Principal;
-import java.util.Enumeration;
-import java.util.Set;
-import java.util.StringTokenizer;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.authorize.AuthorizationException;
+import org.apache.hadoop.security.authorize.DefaultImpersonationProvider;
+import org.apache.hadoop.security.authorize.ImpersonationProvider;
+import org.apache.knox.gateway.i18n.GatewaySpiMessages;
+import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 
 public class AuthFilterUtils {
   public static final String DEFAULT_AUTH_UNAUTHENTICATED_PATHS_PARAM = 
"/knoxtoken/api/v1/jwks.json";
 
+  private static final GatewaySpiMessages LOG = 
MessagesFactory.get(GatewaySpiMessages.class);
+  private static final Map<String, Map<String, ImpersonationProvider>> 
TOPOLOGY_IMPERSONATION_PROVIDERS = new ConcurrentHashMap<>();
+  private static final Lock refreshSuperUserGroupsLock = new ReentrantLock();
+
   /**
    * A helper method that checks whether request contains
    * unauthenticated path
@@ -78,21 +90,21 @@ public class AuthFilterUtils {
     }
   }
 
-  public static Configuration getProxyUserConfiguration(ServletContext 
context, String prefix) {
+  public static void refreshSuperUserGroupsConfiguration(ServletContext 
context, String prefix, String topologyName, String role) {
     if (context == null) {
       throw new IllegalArgumentException("Cannot get proxyuser configuration 
from NULL context");
     }
-    return getProxyUserConfiguration(context, null, prefix);
+    refreshSuperUserGroupsConfiguration(context, null, prefix, topologyName, 
role);
   }
 
-  public static Configuration getProxyUserConfiguration(FilterConfig 
filterConfig, String prefix) {
+  public static void refreshSuperUserGroupsConfiguration(FilterConfig 
filterConfig, String prefix, String topologyName, String role) {
     if (filterConfig == null) {
       throw new IllegalArgumentException("Cannot get proxyuser configuration 
from NULL filter config");
     }
-    return getProxyUserConfiguration(null, filterConfig, prefix);
+    refreshSuperUserGroupsConfiguration(null, filterConfig, prefix, 
topologyName, role);
   }
 
-  private static Configuration getProxyUserConfiguration(ServletContext 
context, FilterConfig filterConfig, String prefix) {
+  private static void refreshSuperUserGroupsConfiguration(ServletContext 
context, FilterConfig filterConfig, String prefix, String topologyName, String 
role) {
     final Configuration conf = new Configuration(false);
     final Enumeration<?> names = context == null ? 
filterConfig.getInitParameterNames() : context.getInitParameterNames();
     if (names != null) {
@@ -105,13 +117,27 @@ public class AuthFilterUtils {
       }
     }
 
-    return conf;
+    saveImpersonationProvider(prefix, topologyName, role, conf);
+  }
+
+  private static void saveImpersonationProvider(String prefix, String 
topologyName, String role, final Configuration conf) {
+    refreshSuperUserGroupsLock.lock();
+    try {
+      final ImpersonationProvider impersonationProvider = new 
DefaultImpersonationProvider();
+      impersonationProvider.setConf(conf);
+      impersonationProvider.init(prefix);
+      LOG.createImpersonationProvider(topologyName, role, prefix, 
conf.getPropsWithPrefix(prefix + ".").toString());
+      TOPOLOGY_IMPERSONATION_PROVIDERS.putIfAbsent(topologyName, new 
ConcurrentHashMap<String, ImpersonationProvider>());
+      TOPOLOGY_IMPERSONATION_PROVIDERS.get(topologyName).put(role, 
impersonationProvider);
+    } finally {
+      refreshSuperUserGroupsLock.unlock();
+    }
   }
 
-  public static HttpServletRequest getProxyRequest(HttpServletRequest request, 
String doAsUser) throws AuthorizationException {
+  public static HttpServletRequest getProxyRequest(HttpServletRequest request, 
String doAsUser, String topologyName, String role) throws 
AuthorizationException {
     final UserGroupInformation remoteRequestUgi = getRemoteRequestUgi(request, 
doAsUser);
     if (remoteRequestUgi != null) {
-      authorizeImpersonationRequest(request, remoteRequestUgi);
+      authorizeImpersonationRequest(request, remoteRequestUgi, topologyName, 
role);
 
       return new HttpServletRequestWrapper(request) {
         @Override
@@ -129,15 +155,34 @@ public class AuthFilterUtils {
     return null;
   }
 
-  public static void authorizeImpersonationRequest(HttpServletRequest request, 
String doAsUser) throws AuthorizationException {
+  public static void authorizeImpersonationRequest(HttpServletRequest request, 
String doAsUser, String topologyName, String role) throws 
AuthorizationException {
     final UserGroupInformation remoteRequestUgi = getRemoteRequestUgi(request, 
doAsUser);
     if (remoteRequestUgi != null) {
-      authorizeImpersonationRequest(request, remoteRequestUgi);
+      authorizeImpersonationRequest(request, remoteRequestUgi, topologyName, 
role);
     }
   }
 
-  private static void authorizeImpersonationRequest(HttpServletRequest 
request, UserGroupInformation remoteRequestUgi) throws AuthorizationException {
-    ProxyUsers.authorize(remoteRequestUgi, request.getRemoteAddr());
+  private static void authorizeImpersonationRequest(HttpServletRequest 
request, UserGroupInformation remoteRequestUgi, String topologyName, String 
role)
+      throws AuthorizationException {
+
+    final ImpersonationProvider impersonationProvider = 
getImpersonationProvider(topologyName, role);
+
+    if (impersonationProvider != null) {
+      impersonationProvider.authorize(remoteRequestUgi, 
request.getRemoteAddr());
+    } else {
+      throw new AuthorizationException("ImpersonationProvider for " + 
topologyName + " / " + role + " not found!");
+    }
+  }
+
+  private static ImpersonationProvider getImpersonationProvider(String 
topologyName, String role) {
+    refreshSuperUserGroupsLock.lock();
+    final ImpersonationProvider impersonationProvider;
+    try {
+      impersonationProvider = 
(TOPOLOGY_IMPERSONATION_PROVIDERS.getOrDefault(topologyName, 
Collections.emptyMap())).get(role);
+    } finally {
+      refreshSuperUserGroupsLock.unlock();
+    }
+    return impersonationProvider;
   }
 
   private static UserGroupInformation getRemoteRequestUgi(HttpServletRequest 
request, String doAsUser) {

Reply via email to