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

jxue pushed a commit to branch helix-1.3.x
in repository https://gitbox.apache.org/repos/asf/helix.git


The following commit(s) were added to refs/heads/helix-1.3.x by this push:
     new bd5b28654 Helix fails to connect with Kerberos enabled ZK
bd5b28654 is described below

commit bd5b286541f3bb02a13f6673c65b6f8235feb5b1
Author: Mohammad Arshad <[email protected]>
AuthorDate: Wed Feb 25 14:22:47 2026 +0530

    Helix fails to connect with Kerberos enabled ZK
---
 .../apache/helix/zookeeper/zkclient/ZkClient.java  |  67 +++++++-
 .../zookeeper/zkclient/TestZkClientKerberos.java   | 171 +++++++++++++++++++++
 2 files changed, 236 insertions(+), 2 deletions(-)

diff --git 
a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkClient.java 
b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkClient.java
index 2a06158d0..1fbc7d70e 100644
--- 
a/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkClient.java
+++ 
b/zookeeper-api/src/main/java/org/apache/helix/zookeeper/zkclient/ZkClient.java
@@ -35,6 +35,8 @@ import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.stream.Stream;
 import javax.management.JMException;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
 
 import com.google.common.annotations.VisibleForTesting;
 import org.apache.helix.zookeeper.api.client.ChildrenSubscribeResult;
@@ -76,6 +78,7 @@ import org.apache.zookeeper.Watcher.Event.EventType;
 import org.apache.zookeeper.Watcher.Event.KeeperState;
 import org.apache.zookeeper.ZooDefs;
 import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.client.ZKClientConfig;
 import org.apache.zookeeper.data.ACL;
 import org.apache.zookeeper.data.Stat;
 import org.slf4j.Logger;
@@ -1986,7 +1989,7 @@ public class ZkClient implements Watcher {
 
     acquireEventLock();
     try {
-      if (!waitForKeeperState(KeeperState.SyncConnected, timeout, timeUnit)) {
+      if (!waitUntilConnected(timeout, timeUnit)) {
         throw new ZkTimeoutException("Waiting to be connected to ZK server has 
timed out.");
       }
       // Reading session ID before unlocking event lock is critical to 
guarantee the established
@@ -1998,7 +2001,8 @@ public class ZkClient implements Watcher {
   }
 
   public boolean waitUntilConnected(long time, TimeUnit timeUnit) throws 
ZkInterruptedException {
-    return waitForKeeperState(KeeperState.SyncConnected, time, timeUnit);
+    KeeperState expectedState = isKerberosAuthEnabled() ? 
KeeperState.SaslAuthenticated : KeeperState.SyncConnected;
+    return waitForKeeperState(expectedState, time, timeUnit);
   }
 
   public boolean waitForKeeperState(KeeperState keeperState, long time, 
TimeUnit timeUnit)
@@ -3153,4 +3157,63 @@ public class ZkClient implements Watcher {
           "Can not subscribe one time watcher when ZkClient is using 
PersistWatcher");
     }
   }
+
+  /**
+   * Checks if Kerberos authentication is enabled for the ZooKeeper connection.
+   * This method performs the following checks:
+   * 1. Verifies if SASL client is enabled via ZKClientConfig
+   * 2. Reads the JAAS client context name from zookeeper.sasl.clientconfig 
property
+   * 3. Checks if the JAAS configuration contains Krb5LoginModule
+   *
+   * @return true if Kerberos authentication is enabled, false otherwise
+   */
+  private boolean isKerberosAuthEnabled() {
+    try {
+      // Get ZooKeeper instance from connection
+      ZkConnection zkConnection = (ZkConnection) getConnection();
+      if (zkConnection == null) {
+        return false;
+      }
+
+      ZooKeeper zk = zkConnection.getZookeeper();
+      if (zk == null) {
+        return false;
+      }
+
+      // Step 1: Check if SASL is enabled via ZKClientConfig
+      ZKClientConfig zkClientConfig = zk.getClientConfig();
+      if (zkClientConfig == null || !zkClientConfig.isSaslClientEnabled()) {
+        return false;
+      }
+
+      // Step 2: Get JAAS client context name using ZooKeeper property
+      String clientContextName = 
zkClientConfig.getProperty(ZKClientConfig.LOGIN_CONTEXT_NAME_KEY,
+          ZKClientConfig.LOGIN_CONTEXT_NAME_KEY_DEFAULT);
+
+      // Step 3: Check JAAS configuration for Krb5LoginModule
+      Configuration jaasConfig = Configuration.getConfiguration();
+      if (jaasConfig == null) {
+        return false;
+      }
+
+      AppConfigurationEntry[] entries = 
jaasConfig.getAppConfigurationEntry(clientContextName);
+      if (entries == null) {
+        return false;
+      }
+
+      // Step 4: Check if any login module contains "Krb5LoginModule"
+      for (AppConfigurationEntry entry : entries) {
+        if (entry.getLoginModuleName().contains("Krb5LoginModule")) {
+          LOG.debug("zkclient {}, Kerberos authentication is enabled with 
login module: {}", _uid,
+              entry.getLoginModuleName());
+          return true;
+        }
+      }
+
+      return false;
+    } catch (Exception e) {
+      LOG.warn("zkclient {}, Failed to determine if Kerberos is enabled, 
assuming false", _uid, e);
+      return false; // Safe default - fall back to non-Kerberos behavior
+    }
+  }
 }
diff --git 
a/zookeeper-api/src/test/java/org/apache/helix/zookeeper/zkclient/TestZkClientKerberos.java
 
b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/zkclient/TestZkClientKerberos.java
new file mode 100644
index 000000000..8048c2609
--- /dev/null
+++ 
b/zookeeper-api/src/test/java/org/apache/helix/zookeeper/zkclient/TestZkClientKerberos.java
@@ -0,0 +1,171 @@
+package org.apache.helix.zookeeper.zkclient;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+
+import org.apache.helix.zookeeper.impl.ZkTestBase;
+import org.apache.helix.zookeeper.impl.client.ZkClient;
+import org.apache.helix.zookeeper.zkclient.serialize.BasicZkSerializer;
+import org.apache.helix.zookeeper.zkclient.serialize.SerializableSerializer;
+import org.apache.zookeeper.client.ZKClientConfig;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ * Integration tests for Kerberos authentication detection in ZkClient
+ */
+public class TestZkClientKerberos extends ZkTestBase {
+  
+  private ZkClient _zkClient;
+  private Configuration _originalJaasConfig;
+
+  @BeforeMethod
+  public void setUp() {
+    // Save original JAAS configuration
+    _originalJaasConfig = Configuration.getConfiguration();
+  }
+
+  @AfterMethod
+  public void tearDown() {
+    // Restore original JAAS configuration
+    if (_originalJaasConfig != null) {
+      Configuration.setConfiguration(_originalJaasConfig);
+    }
+    
+    if (_zkClient != null && !_zkClient.isClosed()) {
+      _zkClient.close();
+    }
+  }
+
+  /**
+   * Test isKerberosAuthEnabled returns false when SASL is disabled
+   */
+  @Test
+  public void testIsKerberosAuthEnabled_WhenSaslDisabled() throws Exception {
+    // Create ZkClient with SASL disabled (default configuration)
+    ZkClient.Builder builder = new ZkClient.Builder();
+    builder.setZkServer(ZkTestBase.ZK_ADDR)
+        .setMonitorRootPathOnly(false);
+    _zkClient = builder.build();
+    _zkClient.setZkSerializer(new BasicZkSerializer(new 
SerializableSerializer()));
+    
+    // Use reflection to call private isKerberosAuthEnabled method
+    boolean result = invokeIsKerberosAuthEnabled(_zkClient);
+    
+    // Verify
+    Assert.assertFalse(result, "isKerberosAuthEnabled should return false when 
SASL is disabled");
+  }
+
+  /**
+   * Test isKerberosAuthEnabled returns false when SASL is enabled but without 
Kerberos
+   */
+  @Test
+  public void testIsKerberosAuthEnabled_WhenSaslEnabledWithoutKerberos() 
throws Exception {
+    // Setup JAAS configuration with non-Kerberos module
+    Configuration jaasConfig = new Configuration() {
+      @Override
+      public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+        if (ZKClientConfig.LOGIN_CONTEXT_NAME_KEY_DEFAULT.equals(name)) {
+          return new AppConfigurationEntry[] {
+              new AppConfigurationEntry(
+                  "com.example.PlainLoginModule",
+                  AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
+                  new HashMap<>()
+              )
+          };
+        }
+        return null;
+      }
+    };
+    Configuration.setConfiguration(jaasConfig);
+    
+    // Enable SASL via system property
+    System.setProperty("zookeeper.sasl.client", "true");
+    
+    try {
+      // Create ZkClient
+      ZkClient.Builder builder = new ZkClient.Builder();
+      builder.setZkServer(ZkTestBase.ZK_ADDR)
+          .setMonitorRootPathOnly(false);
+      _zkClient = builder.build();
+      _zkClient.setZkSerializer(new BasicZkSerializer(new 
SerializableSerializer()));
+      
+      // Use reflection to call private isKerberosAuthEnabled method
+      boolean result = invokeIsKerberosAuthEnabled(_zkClient);
+      
+      // Verify
+      Assert.assertFalse(result, "isKerberosAuthEnabled should return false 
when Kerberos is not configured");
+    } finally {
+      System.clearProperty("zookeeper.sasl.client");
+    }
+  }
+
+
+  /**
+   * Test isKerberosAuthEnabled returns false when JAAS configuration is null
+   */
+  @Test
+  public void testIsKerberosAuthEnabled_WhenJaasConfigNull() throws Exception {
+    // Setup JAAS configuration that returns null
+    Configuration jaasConfig = new Configuration() {
+      @Override
+      public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+        return null;
+      }
+    };
+    Configuration.setConfiguration(jaasConfig);
+    
+    // Enable SASL via system property
+    System.setProperty("zookeeper.sasl.client", "true");
+    
+    try {
+      // Create ZkClient
+      ZkClient.Builder builder = new ZkClient.Builder();
+      builder.setZkServer(ZkTestBase.ZK_ADDR)
+          .setMonitorRootPathOnly(false);
+      _zkClient = builder.build();
+      _zkClient.setZkSerializer(new BasicZkSerializer(new 
SerializableSerializer()));
+      
+      // Use reflection to call private isKerberosAuthEnabled method
+      boolean result = invokeIsKerberosAuthEnabled(_zkClient);
+      
+      // Verify
+      Assert.assertFalse(result, "isKerberosAuthEnabled should return false 
when JAAS config is null");
+    } finally {
+      System.clearProperty("zookeeper.sasl.client");
+    }
+  }
+
+  /**
+   * Use reflection to invoke private isKerberosAuthEnabled method
+   */
+  private boolean invokeIsKerberosAuthEnabled(ZkClient zkClient) throws 
Exception {
+    // The method is in the base class 
org.apache.helix.zookeeper.zkclient.ZkClient
+    Method method = 
org.apache.helix.zookeeper.zkclient.ZkClient.class.getDeclaredMethod("isKerberosAuthEnabled");
+    method.setAccessible(true);
+    return (Boolean) method.invoke(zkClient);
+  }
+}

Reply via email to