[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 <chengpan@apache.org>
Signed-off-by: fwang12 <fwang12@ebay.com>
This commit is contained in:
Cheng Pan 2023-03-08 21:28:10 +08:00 committed by fwang12
parent 68b34be492
commit 17466ea41a

View File

@ -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");