This is an automated email from the ASF dual-hosted git repository.
jxue pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/helix.git
The following commit(s) were added to refs/heads/master by this push:
new 89511bd35 Helix fails to connect with Kerberos enabled ZK
89511bd35 is described below
commit 89511bd3549abaccf65c491ad0b487a9b2c7decf
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 1b227b432..6a0fc9b5d 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
@@ -40,6 +40,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;
@@ -81,6 +83,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;
@@ -2078,7 +2081,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
@@ -2090,7 +2093,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)
@@ -3258,4 +3262,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);
+ }
+}