From 024f2cb9f461227af1eeee459a015e529e5719e2 Mon Sep 17 00:00:00 2001 From: Fei Wang Date: Mon, 15 Jan 2024 11:48:45 +0800 Subject: [PATCH] [KYUUBI #5961] Support to specify client kerberosAuthType as fromTicketCache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # :mag: Description The use case: - 1. the user code running in flink cluster and wrapped by transparent `ugi.doAs` - 2. the current UserGroupInfomation is the proxy user not login/real user - 3. user specify url with kyuubiClientTicketCache but does not work - 4. the proxy UserGroupInfomation is used and then throw GSS issue. ``` Caused by: org.apache.kyuubi.shade.org.apache.thrift.transport.TTransportException: GSS initiate failed at org.apache.kyuubi.shade.org.apache.thrift.transport.TSaslTransport.sendAndThrowMessage(TSaslTransport.java:232) at org.apache.kyuubi.shade.org.apache.thrift.transport.TSaslTransport.open(TSaslTransport.java:316) at org.apache.kyuubi.shade.org.apache.thrift.transport.TSaslClientTransport.open(TSaslClientTransport.java:37) at org.apache.kyuubi.jdbc.hive.auth.TSubjectTransport.lambda$open$0(TSubjectTransport.java:47) at java.security.AccessController.doPrivileged(Native Method) at javax.security.auth.Subject.doAs(Subject.java:422) at org.apache.kyuubi.jdbc.hive.auth.TSubjectTransport.open(TSubjectTransport.java:42) at org.apache.kyuubi.jdbc.hive.KyuubiConnection.openTransport(KyuubiConnection.java:458) at org.apache.kyuubi.jdbc.hive.KyuubiConnection.(KyuubiConnection.java:207) ... 22 more ``` The root cause is that, for this case, the result of `isHadoopUserGroupInformationDoAs` is true. So, `isFromSubjectAuthMode` is true. In this pr, I want to specify the kerberosAuthType to fromTicketCache, and do not check `isHadoopUserGroupInformationDoAs`. After this pr, customer can specify `kerberosAuthType=fromTicketCache` to leverage ticket cache prefer than `ugi.doAs`. ## Issue References ๐Ÿ”— This pull request fixes # ## Describe Your Solution ๐Ÿ”ง Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. ## Types of changes :bookmark: - [ ] Bugfix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) ## Test Plan ๐Ÿงช #### Behavior Without This Pull Request :coffin: #### Behavior With This Pull Request :tada: #### Related Unit Tests --- # Checklist ๐Ÿ“ - [ ] This patch was not authored or co-authored using [Generative Tooling](https://www.apache.org/legal/generative-tooling.html) **Be nice. Be informative.** Closes #5961 from turboFei/warn_ticket_cache. Closes #5961 6e065005d [Fei Wang] exception c01a99e24 [Fei Wang] refine 098a37b2d [Fei Wang] do not infer fromSubject if fromTicketCache && ugi 042fa2249 [Fei Wang] warn Authored-by: Fei Wang Signed-off-by: Cheng Pan (cherry picked from commit 54086b0b1f2f9f372d5aa3ad66254bae69ae4e8b) Signed-off-by: Cheng Pan --- .../jdbc/hive/JdbcConnectionParams.java | 2 + .../kyuubi/jdbc/hive/KyuubiConnection.java | 64 +++++++++++++++---- .../hive/auth/KerberosAuthentication.java | 4 ++ 3 files changed, 58 insertions(+), 12 deletions(-) diff --git a/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/JdbcConnectionParams.java b/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/JdbcConnectionParams.java index d3c77a77f5d..d6ebfbb01a5 100644 --- a/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/JdbcConnectionParams.java +++ b/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/JdbcConnectionParams.java @@ -52,7 +52,9 @@ public class JdbcConnectionParams { public static final String AUTH_KYUUBI_CLIENT_TICKET_CACHE = "kyuubiClientTicketCache"; public static final String AUTH_PASSWD = "password"; public static final String AUTH_KERBEROS_AUTH_TYPE = "kerberosAuthType"; + public static final String AUTH_KERBEROS_AUTH_TYPE_FROM_KEYTAB = "fromKeytab"; public static final String AUTH_KERBEROS_AUTH_TYPE_FROM_SUBJECT = "fromSubject"; + public static final String AUTH_KERBEROS_AUTH_TYPE_FROM_TICKET_CACHE = "fromTicketCache"; public static final String ANONYMOUS_USER = "anonymous"; public static final String ANONYMOUS_PASSWD = "anonymous"; public static final String USE_SSL = "ssl"; 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 077def43b9c..e186f9aa8e8 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 @@ -843,28 +843,68 @@ private boolean isHadoopUserGroupInformationDoAs() { } } + private boolean isForciblyFromKeytabAuthMode() { + return AUTH_KERBEROS_AUTH_TYPE_FROM_KEYTAB.equalsIgnoreCase( + sessConfMap.get(AUTH_KERBEROS_AUTH_TYPE)); + } + + private boolean isForciblyFromSubjectAuthMode() { + return AUTH_KERBEROS_AUTH_TYPE_FROM_SUBJECT.equalsIgnoreCase( + sessConfMap.get(AUTH_KERBEROS_AUTH_TYPE)); + } + + private boolean isForciblyTgtCacheAuthMode() { + return AUTH_KERBEROS_AUTH_TYPE_FROM_TICKET_CACHE.equalsIgnoreCase( + sessConfMap.get(AUTH_KERBEROS_AUTH_TYPE)); + } + private boolean isKeytabAuthMode() { - return isSaslAuthMode() - && hasSessionValue(AUTH_PRINCIPAL) + // handle explicit cases first + if (isForciblyFromSubjectAuthMode() || isForciblyTgtCacheAuthMode()) { + return false; + } + if (isKerberosAuthMode() && isForciblyFromKeytabAuthMode()) { + return true; + } + if (isKerberosAuthMode() + && hasSessionValue(AUTH_KYUUBI_CLIENT_KEYTAB) + && !hasSessionValue(AUTH_KYUUBI_CLIENT_PRINCIPAL)) { + throw new IllegalArgumentException( + AUTH_KYUUBI_CLIENT_KEYTAB + + " is set but " + + AUTH_KYUUBI_CLIENT_PRINCIPAL + + " is not set"); + } + // handle implicit cases then + return isKerberosAuthMode() && hasSessionValue(AUTH_KYUUBI_CLIENT_PRINCIPAL) && hasSessionValue(AUTH_KYUUBI_CLIENT_KEYTAB); } private boolean isFromSubjectAuthMode() { - return isSaslAuthMode() - && hasSessionValue(AUTH_PRINCIPAL) - && !hasSessionValue(AUTH_KYUUBI_CLIENT_PRINCIPAL) + // handle explicit cases first + if (isForciblyFromKeytabAuthMode() || isForciblyTgtCacheAuthMode()) { + return false; + } + if (isKerberosAuthMode() && isForciblyFromSubjectAuthMode()) { + return true; + } + // handle implicit cases then + return isKerberosAuthMode() && !hasSessionValue(AUTH_KYUUBI_CLIENT_KEYTAB) - && (AUTH_KERBEROS_AUTH_TYPE_FROM_SUBJECT.equalsIgnoreCase( - sessConfMap.get(AUTH_KERBEROS_AUTH_TYPE)) - || isHadoopUserGroupInformationDoAs()); + && isHadoopUserGroupInformationDoAs(); } private boolean isTgtCacheAuthMode() { - return isSaslAuthMode() - && hasSessionValue(AUTH_PRINCIPAL) - && !hasSessionValue(AUTH_KYUUBI_CLIENT_PRINCIPAL) - && !hasSessionValue(AUTH_KYUUBI_CLIENT_KEYTAB); + // handle explicit cases first + if (isForciblyFromKeytabAuthMode() || isForciblyFromSubjectAuthMode()) { + return false; + } + if (isKerberosAuthMode() && isForciblyTgtCacheAuthMode()) { + return true; + } + // handle implicit cases then + return isKerberosAuthMode() && !hasSessionValue(AUTH_KYUUBI_CLIENT_KEYTAB); } private boolean isPlainSaslAuthMode() { diff --git a/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/auth/KerberosAuthentication.java b/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/auth/KerberosAuthentication.java index a137fbb9946..77456943755 100644 --- a/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/auth/KerberosAuthentication.java +++ b/kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/auth/KerberosAuthentication.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.net.InetAddress; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Map; @@ -106,6 +107,9 @@ private static Configuration createLoginFromTgtCacheConfiguration(String ticketC if (StringUtils.isBlank(ticketCache)) { ticketCache = System.getenv("KRB5CCNAME"); } + if (!Files.exists(Paths.get(ticketCache))) { + LOG.warn("TicketCache {} does not exist", ticketCache); + } if (StringUtils.isNotBlank(ticketCache)) { optionsBuilder.put("ticketCache", ticketCache); }