You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kyuubi.apache.org by fe...@apache.org on 2023/03/08 13:28:28 UTC

[kyuubi] branch branch-1.7 updated: [KYUUBI #4479] Restore JDBC Kerberos authentication behavior for UGI.doAs

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

feiwang pushed a commit to branch branch-1.7
in repository https://gitbox.apache.org/repos/asf/kyuubi.git


The following commit(s) were added to refs/heads/branch-1.7 by this push:
     new 3bc536995 [KYUUBI #4479] Restore JDBC Kerberos authentication behavior for UGI.doAs
3bc536995 is described below

commit 3bc536995db3387d3242c19a5d55601f94f4f59d
Author: Cheng Pan <ch...@apache.org>
AuthorDate: Wed Mar 8 21:28:10 2023 +0800

    [KYUUBI #4479] Restore JDBC Kerberos authentication behavior for UGI.doAs
    
    ### _Why are the changes needed?_
    
    A typical use case of Hadoop UGI w/ Kyuubi Hive JDBC is
    ```
    UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
    ugi.doAs(() -> {
      Connection conn = DriverManager.getConnection(
            "jdbc:kyuubi://host:10009/default;principal=kyuubi_HOST/ABC.ORG");
      ...
    });
    ```
    
    After https://github.com/apache/kyuubi/pull/3023, Kyuubi Hive JDBC implements the Kerberos authentication by using JDK directly instead of Hadoop `UserGroupInformation`, but it also introduce a breaking change for Hadoop users, including the above case. As workaround, user should add `kerberosAuthType=fromSubject` alongside w/ `principal=kyuubi_HOST/ABC.ORG` to make it work.
    
    This PR propose to restore the behavior before https://github.com/apache/kyuubi/pull/3023 by handling UGI.doAs explicitly.
    
    And this PR makes the `clientPrincipal` `clientKeytab` as the highest priority, so in below cases, `clientPrincipal` `clientKeytab` take effects instead of UGI.
    
    ```
    UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
    ugi.doAs(() -> {
      Connection conn = DriverManager.getConnection(
            "jdbc:kyuubi://host:10009/default;principal=kyuubi_HOST/ABC.ORG;" +
            "clientPrincipal=tom_HOST/ABC.ORG;clientKeytab=/path/xxx.keytab");
      ...
    });
    ```
    
    ### _How was this patch tested?_
    - [ ] Add some test cases that check the changes thoroughly including negative and positive cases if possible
    
    - [ ] Add screenshots for manual tests if appropriate
    
    - [ ] [Run test](https://kyuubi.readthedocs.io/en/master/develop_tools/testing.html#running-tests) locally before make a pull request
    
    Closes #4479 from pan3793/detect-ugi.
    
    Closes #4479
    
    0e169abc6 [Cheng Pan] nit
    19036e3d7 [Cheng Pan] reorder
    e8faf9c56 [Cheng Pan] Restore JDBC kerberos authentication behavior for UGI.doAs
    
    Authored-by: Cheng Pan <ch...@apache.org>
    Signed-off-by: fwang12 <fw...@ebay.com>
    (cherry picked from commit 17466ea41ab15766c0cc01473c18b1c56d6cbffb)
    Signed-off-by: fwang12 <fw...@ebay.com>
---
 .../apache/kyuubi/jdbc/hive/KyuubiConnection.java  | 41 ++++++++++++++--------
 1 file changed, 27 insertions(+), 14 deletions(-)

diff --git a/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/KyuubiConnection.java b/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/KyuubiConnection.java
index 0932ea565..2a485b24e 100644
--- a/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/KyuubiConnection.java
+++ b/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/KyuubiConnection.java
@@ -30,10 +30,7 @@ import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.KeyStore;
-import java.security.SecureRandom;
+import java.security.*;
 import java.sql.*;
 import java.util.*;
 import java.util.Map.Entry;
@@ -43,6 +40,7 @@ import javax.net.ssl.SSLContext;
 import javax.net.ssl.TrustManagerFactory;
 import javax.security.auth.Subject;
 import javax.security.sasl.Sasl;
+import org.apache.commons.lang3.ClassUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.hive.service.rpc.thrift.*;
 import org.apache.http.HttpRequestInterceptor;
@@ -813,11 +811,16 @@ public class KyuubiConnection implements SQLConnection, KyuubiLoggable {
     return !AUTH_SIMPLE.equalsIgnoreCase(sessConfMap.get(AUTH_TYPE));
   }
 
-  private boolean isFromSubjectAuthMode() {
-    return isSaslAuthMode()
-        && hasSessionValue(AUTH_PRINCIPAL)
-        && AUTH_KERBEROS_AUTH_TYPE_FROM_SUBJECT.equalsIgnoreCase(
-            sessConfMap.get(AUTH_KERBEROS_AUTH_TYPE));
+  private boolean isHadoopUserGroupInformationDoAs() {
+    try {
+      @SuppressWarnings("unchecked")
+      Class<? extends Principal> HadoopUserClz =
+          (Class<? extends Principal>) ClassUtils.getClass("org.apache.hadoop.security.User");
+      Subject subject = Subject.getSubject(AccessController.getContext());
+      return subject != null && !subject.getPrincipals(HadoopUserClz).isEmpty();
+    } catch (ClassNotFoundException e) {
+      return false;
+    }
   }
 
   private boolean isKeytabAuthMode() {
@@ -827,6 +830,16 @@ public class KyuubiConnection implements SQLConnection, KyuubiLoggable {
         && hasSessionValue(AUTH_KYUUBI_CLIENT_KEYTAB);
   }
 
+  private boolean isFromSubjectAuthMode() {
+    return isSaslAuthMode()
+        && hasSessionValue(AUTH_PRINCIPAL)
+        && !hasSessionValue(AUTH_KYUUBI_CLIENT_PRINCIPAL)
+        && !hasSessionValue(AUTH_KYUUBI_CLIENT_KEYTAB)
+        && (AUTH_KERBEROS_AUTH_TYPE_FROM_SUBJECT.equalsIgnoreCase(
+                sessConfMap.get(AUTH_KERBEROS_AUTH_TYPE))
+            || isHadoopUserGroupInformationDoAs());
+  }
+
   private boolean isTgtCacheAuthMode() {
     return isSaslAuthMode()
         && hasSessionValue(AUTH_PRINCIPAL)
@@ -843,15 +856,15 @@ public class KyuubiConnection implements SQLConnection, KyuubiLoggable {
   }
 
   private Subject createSubject() {
-    if (isFromSubjectAuthMode()) {
+    if (isKeytabAuthMode()) {
+      String principal = sessConfMap.get(AUTH_KYUUBI_CLIENT_PRINCIPAL);
+      String keytab = sessConfMap.get(AUTH_KYUUBI_CLIENT_KEYTAB);
+      return KerberosAuthenticationManager.getKeytabAuthentication(principal, keytab).getSubject();
+    } else if (isFromSubjectAuthMode()) {
       AccessControlContext context = AccessController.getContext();
       return Subject.getSubject(context);
     } else if (isTgtCacheAuthMode()) {
       return KerberosAuthenticationManager.getTgtCacheAuthentication().getSubject();
-    } else if (isKeytabAuthMode()) {
-      String principal = sessConfMap.get(AUTH_KYUUBI_CLIENT_PRINCIPAL);
-      String keytab = sessConfMap.get(AUTH_KYUUBI_CLIENT_KEYTAB);
-      return KerberosAuthenticationManager.getKeytabAuthentication(principal, keytab).getSubject();
     } else {
       // This should never happen
       throw new IllegalArgumentException("Unsupported auth mode");