JoegenUSTC opened a new issue, #11353:
URL: https://github.com/apache/gravitino/issues/11353

   ### Environment
   
   - Gravitino version: main / v0.9.x (confirmed on v1.2.0-rc8)
   - Hive Metastore version: HMS with Kerberos authentication
   - Configuration: `hive.metastore.sasl.enabled=true`, impersonation 
**disabled**
   
   ### Problem
   
   When a Hive catalog is configured with Kerberos authentication and 
impersonation is **not** enabled, connecting to HMS fails with:
   
   ```
   GSS initiate failed
   No valid credentials provided (Mechanism level: Failed to find any Kerberos 
tgt)
   ```
   
   or
   
   ```
   java.lang.NoSuchMethodException: HiveClientImpl.<init>(HiveVersion, 
Properties)
   ```
   
   Root cause analysis reveals **three independent bugs** in 
`HiveClientFactory`, all in the same code path.
   
   ---
   
   ### Bug 1 — `baseLoader` uses TCCL, causing UGI class to be loaded twice
   
   **Location**: `HiveClientFactory.java`, `createHiveClientWithBackend()`, 
lines 121 and 143
   
   **Code**:
   ```java
   // Current (buggy)
   classloader = HiveClientClassLoader.createLoader(HIVE3,
       Thread.currentThread().getContextClassLoader());  // TCCL is not stable
   ```
   
   `HiveClientClassLoader` uses `baseLoader` to determine which classes are 
"shared" (delegated upward via `isSharedClass`). This includes 
`UserGroupInformation` (UGI) and the `HiveClient` interface.
   
   Because `Thread.currentThread().getContextClassLoader()` (TCCL) is **not 
stable** across threads:
   
   - `KerberosClient.login()` runs on thread A → UGI loaded by TCCL-A → TGT 
stored in TCCL-A's UGI static state
   - HMS connection runs on thread B → UGI loaded by TCCL-B → different `Class` 
object → static state is empty → TGT not found → Kerberos handshake fails
   
   The same `UserGroupInformation` class ends up loaded by two different 
ClassLoaders, so their static state is not shared.
   
   **Fix**: Use `HiveClientFactory.class.getClassLoader()` as `baseLoader` — it 
is stable and consistent:
   ```java
   ClassLoader factoryCl = HiveClientFactory.class.getClassLoader();
   classloader = HiveClientClassLoader.createLoader(HIVE3, factoryCl);
   ```
   
   ---
   
   ### Bug 2 — `HiveVersion` enum type mismatch causes `NoSuchMethodException`
   
   **Location**: `HiveClientFactory.java`, `createHiveClientImpl()`, lines 
165–167
   
   **Code**:
   ```java
   // Current (buggy)
   Constructor<?> hiveClientImplCtor = hiveClientImplClass.getConstructor(
       HiveClientClassLoader.HiveVersion.class,  // ← system CL's HiveVersion
       Properties.class);
   ```
   
   `hiveClientImplClass` is loaded from the **isolated** 
`HiveClientClassLoader` (it is a barrier class redefined via `defineClass`). 
Its constructor expects a `HiveVersion` parameter belonging to the **isolated** 
ClassLoader's type space.
   
   Passing `HiveClientClassLoader.HiveVersion.class` (system CL) causes 
`getConstructor` to find no matching constructor → `NoSuchMethodException`.
   
   **Fix**: Load `HiveVersion` from the isolated ClassLoader before 
constructing:
   ```java
   Class<?> hiveVersionInIsolated =
       classloader.loadClass(HiveClientClassLoader.HiveVersion.class.getName());
   Object isolatedVersion =
       Enum.valueOf((Class<? extends Enum>) hiveVersionInIsolated, 
version.name());
   Constructor<?> ctor =
       hiveClientImplClass.getConstructor(hiveVersionInIsolated, 
Properties.class);
   return (HiveClient) ctor.newInstance(isolatedVersion, properties);
   ```
   
   ---
   
   ### Bug 3 — Non-impersonation Kerberos path never binds the JAAS Subject to 
the current thread
   
   **Location**: `HiveClientFactory.java`, `createHiveClientInternal()`, lines 
205–206
   
   **Code**:
   ```java
   // Current (buggy) — non-impersonation branch
   } else {
       return createHiveClientImpl(classloader.getHiveVersion(), properties, 
classloader);
   }
   ```
   
   `KerberosClient.login()` calls 
`UserGroupInformation.loginUserFromKeytab(...)`, which stores the TGT in 
`loginUser.subject`. However, the HMS Thrift client performs a SASL/GSSAPI 
handshake at connection time. GSSAPI looks for Kerberos credentials in the 
**current thread's JAAS Subject context** (`AccessControlContext`). Since 
`javax.security.auth.useSubjectCredsOnly=true` by default in the JVM, if no 
Subject is bound to the current thread, GSSAPI immediately fails.
   
   `loginUser.subject` is **not** automatically bound to the current thread. It 
must be explicitly injected via `Subject.doAs(subject, action)` or 
`ugi.doAs(action)`.
   
   The impersonation path (`createProxyHiveClientImpl`) correctly uses 
`ugi.doAs(...)` — the non-impersonation Kerberos path is missing the equivalent.
   
   **Fix**:
   ```java
   } else if (enableKerberos) {
       UserGroupInformation realUgi = kerberosClient.getRealLoginUgi();
       final HiveClientClassLoader.HiveVersion hiveVersion = 
classloader.getHiveVersion();
       return realUgi.doAs(
           (PrivilegedExceptionAction<HiveClient>)
               () -> createHiveClientImpl(hiveVersion, properties, 
classloader));
   }
   ```
   
   This is consistent with how `ImpalaEngineAdapter` and `ProxyHiveClientImpl` 
already handle the Kerberos + `doAs` pattern.
   
   ---
   
   ### Summary
   
   | # | Root cause | Effect | Fix |
   |---|-----------|--------|-----|
   | 1 | `baseLoader` uses unstable TCCL | UGI loaded twice; TGT not shared 
across threads | Use `HiveClientFactory.class.getClassLoader()` |
   | 2 | `getConstructor` passes system CL's `HiveVersion` | 
`NoSuchMethodException` on every `createHiveClientImpl` call | Load 
`HiveVersion` from isolated CL |
   | 3 | Non-impersonation Kerberos path missing `doAs` | GSSAPI can't find TGT 
→ `No valid credentials` | Wrap in `realUgi.doAs(...)` |
   
   All three bugs exist in the current `main` branch. I am happy to submit a PR 
with the fixes and corresponding tests.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to