From 5c70258c793e5b2aa1fec3c78fb91eb75a482ca6 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 29 Dec 2025 12:45:53 +0900 Subject: [PATCH 01/16] HBASE-29144 Add a failing test case to reproduce the reported issue Caused by: org.apache.hadoop.hbase.client.RetriesExhaustedException: Failed contacting masters after 1 attempts. Exceptions: java.io.IOException: Call to address=192.168.35.34:57912 failed on local exception: java.io.IOException: Authentication provider class org.apache.hadoop.hbase.security.provider.SimpleSaslClientAuthenticationProvider returned a null SaslClient --- .../client/TestRpcConnectionRegistry.java | 10 +- .../TestRpcConnectionRegistrySecure.java | 112 ++++++++++++++++++ 2 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRpcConnectionRegistrySecure.java diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRpcConnectionRegistry.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRpcConnectionRegistry.java index d33cc943355c..cb294861c37b 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRpcConnectionRegistry.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRpcConnectionRegistry.java @@ -60,12 +60,11 @@ public class TestRpcConnectionRegistry { public static final HBaseClassTestRule CLASS_RULE = HBaseClassTestRule.forClass(TestRpcConnectionRegistry.class); - private static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); + protected static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); private RpcConnectionRegistry registry; - @BeforeClass - public static void setUpBeforeClass() throws Exception { + protected static void startMiniCluster() throws Exception { // allow refresh immediately so we will switch to use region servers soon. UTIL.getConfiguration().setLong(RpcConnectionRegistry.INITIAL_REFRESH_DELAY_SECS, 1); UTIL.getConfiguration().setLong(RpcConnectionRegistry.PERIODIC_REFRESH_INTERVAL_SECS, 1); @@ -75,6 +74,11 @@ public static void setUpBeforeClass() throws Exception { HBaseTestingUtil.setReplicas(UTIL.getAdmin(), TableName.META_TABLE_NAME, 3); } + @BeforeClass + public static void setUpBeforeClass() throws Exception { + startMiniCluster(); + } + @AfterClass public static void tearDownAfterClass() throws Exception { UTIL.shutdownMiniCluster(); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRpcConnectionRegistrySecure.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRpcConnectionRegistrySecure.java new file mode 100644 index 000000000000..7082e6e616a3 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRpcConnectionRegistrySecure.java @@ -0,0 +1,112 @@ +/* + * 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. + */ +package org.apache.hadoop.hbase.client; + +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.security.PrivilegedExceptionAction; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.TableNameTestRule; +import org.apache.hadoop.hbase.security.HBaseKerberosUtils; +import org.apache.hadoop.hbase.security.access.SecureTestUtil; +import org.apache.hadoop.hbase.security.provider.SaslClientAuthenticationProviders; +import org.apache.hadoop.hbase.testclassification.ClientTests; +import org.apache.hadoop.hbase.testclassification.MediumTests; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.minikdc.MiniKdc; +import org.apache.hadoop.security.UserGroupInformation; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +@Category({ MediumTests.class, ClientTests.class }) +public class TestRpcConnectionRegistrySecure extends TestRpcConnectionRegistry { + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestRpcConnectionRegistrySecure.class); + + private static final String SERVER_PRINCIPAL = "hbase/localhost"; + + private static File KEYTAB_FILE; + private static MiniKdc KDC; + + @Rule + public final TableNameTestRule name = new TableNameTestRule(); + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + KEYTAB_FILE = new File(UTIL.getDataTestDir("keytab").toUri().getPath()); + KDC = UTIL.setupMiniKdc(KEYTAB_FILE); + KDC.createPrincipal(KEYTAB_FILE, SERVER_PRINCIPAL); + + final Configuration conf = UTIL.getConfiguration(); + SecureTestUtil.enableSecurity(conf); + HBaseKerberosUtils.setSecuredConfiguration(conf, SERVER_PRINCIPAL + '@' + KDC.getRealm(), null); + + startMiniCluster(); + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + UTIL.shutdownMiniCluster(); + if (KDC != null) { + KDC.stop(); + } + KEYTAB_FILE.delete(); + } + + @Test + public void testSecureRpc() throws Exception { + UserGroupInformation.loginUserFromKeytab(SERVER_PRINCIPAL, KEYTAB_FILE.toString()); + final UserGroupInformation ugi = UserGroupInformation.getLoginUser(); + + // This is required because SaslClientAuthenticationProviders is already initialized + // in the process of starting up the mini cluster. + SaslClientAuthenticationProviders.reset(); + + // Make sure that existing connection are not reused. + UTIL.invalidateConnection(); + + assertTrue(ugi.doAs((PrivilegedExceptionAction) this::doTableTest)); + } + + private boolean doTableTest() throws IOException { + final int count = 10; + final byte[] cf = Bytes.toBytes("f"); + final byte[] cq = Bytes.toBytes("q"); + final TableDescriptor desc = TableDescriptorBuilder.newBuilder(name.getTableName()).build(); + int scannedRows = 0; + try (Table table = UTIL.createTable(desc, new byte[][] { cf }, UTIL.getConfiguration())) { + for (int i = 0; i < count; i++) { + table.put(new Put(Bytes.toBytes(i)).addColumn(cf, cq, "0".getBytes())); + } + for (Result result : table.getScanner(new Scan())) { + scannedRows++; + } + } + UTIL.deleteTableIfAny(name.getTableName()); + return scannedRows == count; + } +} From 86b570885e86427e4936e6916ca07ab6230b68b4 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 29 Dec 2025 12:47:57 +0900 Subject: [PATCH 02/16] HBASE-29144 Fix by removing the check as suggested by @NihalJain --- .../hbase/security/provider/BuiltInProviderSelector.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/BuiltInProviderSelector.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/BuiltInProviderSelector.java index 2c9968f6f71b..2217491fb605 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/BuiltInProviderSelector.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/BuiltInProviderSelector.java @@ -101,11 +101,6 @@ public void configure(Configuration conf, requireNonNull(clusterId, "Null clusterId was given"); requireNonNull(user, "Null user was given"); - // Superfluous: we don't do SIMPLE auth over SASL, but we should to simplify. - if (!User.isHBaseSecurityEnabled(conf)) { - return new Pair<>(simpleAuth, null); - } - final Text clusterIdAsText = new Text(clusterId); // Must be digest auth, look for a token. From 1926013f98ecd090b5c870fb0c5ac49d29c7b28e Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 29 Dec 2025 12:49:16 +0900 Subject: [PATCH 03/16] Revert "HBASE-29144 Fix it by removing a check as suggested by @NihalJain" This reverts commit 8b46519ca6dbc2a05a5b083e182383a070763daa. --- .../hbase/security/provider/BuiltInProviderSelector.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/BuiltInProviderSelector.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/BuiltInProviderSelector.java index 2217491fb605..2c9968f6f71b 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/BuiltInProviderSelector.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/BuiltInProviderSelector.java @@ -101,6 +101,11 @@ public void configure(Configuration conf, requireNonNull(clusterId, "Null clusterId was given"); requireNonNull(user, "Null user was given"); + // Superfluous: we don't do SIMPLE auth over SASL, but we should to simplify. + if (!User.isHBaseSecurityEnabled(conf)) { + return new Pair<>(simpleAuth, null); + } + final Text clusterIdAsText = new Text(clusterId); // Must be digest auth, look for a token. From 548e86d84c68c771449558f8ec640cbee5922347 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 29 Dec 2025 16:06:00 +0900 Subject: [PATCH 04/16] HBASE-29144 Make test fail by removing noAuthConf --- .../hbase/client/ConnectionRegistryRpcStubHolder.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionRegistryRpcStubHolder.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionRegistryRpcStubHolder.java index 3dbcfbe8e6bf..1846440b8eaa 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionRegistryRpcStubHolder.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionRegistryRpcStubHolder.java @@ -55,9 +55,6 @@ class ConnectionRegistryRpcStubHolder implements Closeable { private final Configuration conf; - // used for getting cluster id - private final Configuration noAuthConf; - private final User user; private final RpcControllerFactory rpcControllerFactory; @@ -75,12 +72,6 @@ class ConnectionRegistryRpcStubHolder implements Closeable { ConnectionRegistryRpcStubHolder(Configuration conf, User user, RpcControllerFactory rpcControllerFactory, Set bootstrapNodes) { this.conf = conf; - if (User.isHBaseSecurityEnabled(conf)) { - this.noAuthConf = new Configuration(conf); - this.noAuthConf.set(User.HBASE_SECURITY_CONF_KEY, "simple"); - } else { - this.noAuthConf = conf; - } this.user = user; this.rpcControllerFactory = rpcControllerFactory; this.bootstrapNodes = Collections.unmodifiableSet(bootstrapNodes); @@ -107,7 +98,7 @@ private ImmutableMap createStubs(RpcCli new CompletableFuture<>(); addr2StubFuture = future; FutureUtils.addListener( - new ClusterIdFetcher(noAuthConf, user, rpcControllerFactory, bootstrapNodes).fetchClusterId(), + new ClusterIdFetcher(conf, user, rpcControllerFactory, bootstrapNodes).fetchClusterId(), (clusterId, error) -> { synchronized (ConnectionRegistryRpcStubHolder.this) { if (error != null) { From e057371e72e438d7a055a9d694d99ad8d204457c Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 29 Dec 2025 16:06:57 +0900 Subject: [PATCH 05/16] HBASE-29144 Avoid using SASL when retrieving the cluster ID --- .../main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java index dbdb0e2037f8..8b1de701722c 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java @@ -131,7 +131,8 @@ protected RpcConnection(Configuration conf, HashedWheelTimer timeoutTimer, Conne this.connectionAttributes = connectionAttributes; User ticket = remoteId.getTicket(); this.securityInfo = SecurityInfo.getInfo(remoteId.getServiceName()); - this.useSasl = isSecurityEnabled; + // Avoid using SASL when retrieving the cluster ID + this.useSasl = isSecurityEnabled && !HConstants.CLUSTER_ID_DEFAULT.equals(clusterId); // Choose the correct Token and AuthenticationProvider for this client to use SaslClientAuthenticationProviders providers = From f03bb32d1baf035f0318e3907e98e0ca38e8c187 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 5 Jan 2026 09:44:50 +0900 Subject: [PATCH 06/16] Revert "HBASE-29144 Avoid using SASL when retrieving the cluster ID" This reverts commit e057371e72e438d7a055a9d694d99ad8d204457c. --- .../main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java index 8b1de701722c..dbdb0e2037f8 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java @@ -131,8 +131,7 @@ protected RpcConnection(Configuration conf, HashedWheelTimer timeoutTimer, Conne this.connectionAttributes = connectionAttributes; User ticket = remoteId.getTicket(); this.securityInfo = SecurityInfo.getInfo(remoteId.getServiceName()); - // Avoid using SASL when retrieving the cluster ID - this.useSasl = isSecurityEnabled && !HConstants.CLUSTER_ID_DEFAULT.equals(clusterId); + this.useSasl = isSecurityEnabled; // Choose the correct Token and AuthenticationProvider for this client to use SaslClientAuthenticationProviders providers = From e221a16ec4e903f9a8ef4bdc82141e7accd7af6f Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 5 Jan 2026 10:02:28 +0900 Subject: [PATCH 07/16] Revert "HBASE-29144 Make test fail by removing noAuthConf" This reverts commit 548e86d84c68c771449558f8ec640cbee5922347. --- .../hbase/client/ConnectionRegistryRpcStubHolder.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionRegistryRpcStubHolder.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionRegistryRpcStubHolder.java index 1846440b8eaa..3dbcfbe8e6bf 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionRegistryRpcStubHolder.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/ConnectionRegistryRpcStubHolder.java @@ -55,6 +55,9 @@ class ConnectionRegistryRpcStubHolder implements Closeable { private final Configuration conf; + // used for getting cluster id + private final Configuration noAuthConf; + private final User user; private final RpcControllerFactory rpcControllerFactory; @@ -72,6 +75,12 @@ class ConnectionRegistryRpcStubHolder implements Closeable { ConnectionRegistryRpcStubHolder(Configuration conf, User user, RpcControllerFactory rpcControllerFactory, Set bootstrapNodes) { this.conf = conf; + if (User.isHBaseSecurityEnabled(conf)) { + this.noAuthConf = new Configuration(conf); + this.noAuthConf.set(User.HBASE_SECURITY_CONF_KEY, "simple"); + } else { + this.noAuthConf = conf; + } this.user = user; this.rpcControllerFactory = rpcControllerFactory; this.bootstrapNodes = Collections.unmodifiableSet(bootstrapNodes); @@ -98,7 +107,7 @@ private ImmutableMap createStubs(RpcCli new CompletableFuture<>(); addr2StubFuture = future; FutureUtils.addListener( - new ClusterIdFetcher(conf, user, rpcControllerFactory, bootstrapNodes).fetchClusterId(), + new ClusterIdFetcher(noAuthConf, user, rpcControllerFactory, bootstrapNodes).fetchClusterId(), (clusterId, error) -> { synchronized (ConnectionRegistryRpcStubHolder.this) { if (error != null) { From 130453d573d6c8ac2e79345061aa4567546ac7e2 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 5 Jan 2026 10:12:00 +0900 Subject: [PATCH 08/16] HBASE-29144 Avoid using cached configuration when selecting AuthenticationProvider --- .../apache/hadoop/hbase/ipc/RpcConnection.java | 2 +- .../provider/AuthenticationProviderSelector.java | 15 +++++++++++++-- .../provider/BuiltInProviderSelector.java | 6 ++++++ .../SaslClientAuthenticationProviders.java | 4 ++-- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java index dbdb0e2037f8..062040468206 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java @@ -138,7 +138,7 @@ protected RpcConnection(Configuration conf, HashedWheelTimer timeoutTimer, Conne SaslClientAuthenticationProviders.getInstance(conf); Pair> pair; if (useSasl && securityInfo != null) { - pair = providers.selectProvider(clusterId, ticket); + pair = providers.selectProvider(conf, clusterId, ticket); if (pair == null) { if (LOG.isTraceEnabled()) { LOG.trace("Found no valid authentication method from providers={} with tokens={}", diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/AuthenticationProviderSelector.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/AuthenticationProviderSelector.java index cdd1fdb381f6..46420233a908 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/AuthenticationProviderSelector.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/AuthenticationProviderSelector.java @@ -40,9 +40,20 @@ void configure(Configuration conf, Collection availableProviders); /** - * Chooses the authentication provider which should be used given the provided client context from - * the authentication providers passed in via {@link #configure(Configuration, Collection)}. + * Chooses the authentication provider which should be used given the provided client context, + * using a cached {@link Configuration}, from the authentication providers passed in via + * {@link #configure(Configuration, Collection)}. */ Pair> selectProvider(String clusterId, User user); + + /** + * Chooses the authentication provider which should be used given the provided client context and + * the supplied {@link Configuration}, from the authentication providers passed in via + * {@link #configure(Configuration, Collection)}. + */ + default Pair> + selectProvider(Configuration conf, String clusterId, User user) { + return selectProvider(clusterId, user); + } } diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/BuiltInProviderSelector.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/BuiltInProviderSelector.java index 2c9968f6f71b..ae6dc55870dd 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/BuiltInProviderSelector.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/BuiltInProviderSelector.java @@ -98,6 +98,12 @@ public void configure(Configuration conf, @Override public Pair> selectProvider(String clusterId, User user) { + return selectProvider(conf, clusterId, user); + } + + @Override + public Pair> + selectProvider(Configuration conf, String clusterId, User user) { requireNonNull(clusterId, "Null clusterId was given"); requireNonNull(user, "Null user was given"); diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java index a78ff3386a46..6c220e3bd0b3 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java @@ -205,8 +205,8 @@ static SaslClientAuthenticationProviders instantiate(Configuration conf) { * identifier and the user. */ public Pair> - selectProvider(String clusterId, User clientUser) { - return selector.selectProvider(clusterId, clientUser); + selectProvider(Configuration conf, String clusterId, User clientUser) { + return selector.selectProvider(conf, clusterId, clientUser); } @Override From ecc9c1c204855d99fdea846629c994602c9eb4fc Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 5 Jan 2026 15:48:26 +0900 Subject: [PATCH 09/16] HBASE-29144 Fall back to legacy version of the method --- .../org/apache/hadoop/hbase/ipc/RpcConnection.java | 4 ++++ .../provider/SaslClientAuthenticationProviders.java | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java index 062040468206..f88b68a2938e 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java @@ -139,6 +139,10 @@ protected RpcConnection(Configuration conf, HashedWheelTimer timeoutTimer, Conne Pair> pair; if (useSasl && securityInfo != null) { pair = providers.selectProvider(conf, clusterId, ticket); + if (pair == null) { + LOG.trace("Falling back to selectProvider using the cached configuration"); + pair = providers.selectProvider(clusterId, ticket); + } if (pair == null) { if (LOG.isTraceEnabled()) { LOG.trace("Found no valid authentication method from providers={} with tokens={}", diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java index 6c220e3bd0b3..48863cb56252 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java @@ -202,13 +202,22 @@ static SaslClientAuthenticationProviders instantiate(Configuration conf) { /** * Chooses the best authentication provider and corresponding token given the HBase cluster - * identifier and the user. + * identifier, the user, and the supplied {@link Configuration}. */ public Pair> selectProvider(Configuration conf, String clusterId, User clientUser) { return selector.selectProvider(conf, clusterId, clientUser); } + /** + * Chooses the best authentication provider and corresponding token given the HBase cluster + * identifier and the user. This version was kept for backward compatibility. + */ + public Pair> + selectProvider(String clusterId, User clientUser) { + return selector.selectProvider(clusterId, clientUser); + } + @Override public String toString() { return providers.stream().map((p) -> p.getClass().getName()) From 6a2cbe75aa9a185a25f398f970f019aeb233eaa2 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 5 Jan 2026 18:20:37 +0900 Subject: [PATCH 10/16] Revert "HBASE-29144 Fall back to legacy version of the method" This reverts commit ecc9c1c204855d99fdea846629c994602c9eb4fc. --- .../org/apache/hadoop/hbase/ipc/RpcConnection.java | 4 ---- .../provider/SaslClientAuthenticationProviders.java | 11 +---------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java index f88b68a2938e..062040468206 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java @@ -139,10 +139,6 @@ protected RpcConnection(Configuration conf, HashedWheelTimer timeoutTimer, Conne Pair> pair; if (useSasl && securityInfo != null) { pair = providers.selectProvider(conf, clusterId, ticket); - if (pair == null) { - LOG.trace("Falling back to selectProvider using the cached configuration"); - pair = providers.selectProvider(clusterId, ticket); - } if (pair == null) { if (LOG.isTraceEnabled()) { LOG.trace("Found no valid authentication method from providers={} with tokens={}", diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java index 48863cb56252..6c220e3bd0b3 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java @@ -202,22 +202,13 @@ static SaslClientAuthenticationProviders instantiate(Configuration conf) { /** * Chooses the best authentication provider and corresponding token given the HBase cluster - * identifier, the user, and the supplied {@link Configuration}. + * identifier and the user. */ public Pair> selectProvider(Configuration conf, String clusterId, User clientUser) { return selector.selectProvider(conf, clusterId, clientUser); } - /** - * Chooses the best authentication provider and corresponding token given the HBase cluster - * identifier and the user. This version was kept for backward compatibility. - */ - public Pair> - selectProvider(String clusterId, User clientUser) { - return selector.selectProvider(clusterId, clientUser); - } - @Override public String toString() { return providers.stream().map((p) -> p.getClass().getName()) From d08bc0f98dc8add3eee7cd35fb1debd6863875af Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 5 Jan 2026 18:20:40 +0900 Subject: [PATCH 11/16] Revert "HBASE-29144 Avoid using cached configuration when selecting AuthenticationProvider" This reverts commit 130453d573d6c8ac2e79345061aa4567546ac7e2. --- .../apache/hadoop/hbase/ipc/RpcConnection.java | 2 +- .../provider/AuthenticationProviderSelector.java | 15 ++------------- .../provider/BuiltInProviderSelector.java | 6 ------ .../SaslClientAuthenticationProviders.java | 4 ++-- 4 files changed, 5 insertions(+), 22 deletions(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java index 062040468206..dbdb0e2037f8 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java @@ -138,7 +138,7 @@ protected RpcConnection(Configuration conf, HashedWheelTimer timeoutTimer, Conne SaslClientAuthenticationProviders.getInstance(conf); Pair> pair; if (useSasl && securityInfo != null) { - pair = providers.selectProvider(conf, clusterId, ticket); + pair = providers.selectProvider(clusterId, ticket); if (pair == null) { if (LOG.isTraceEnabled()) { LOG.trace("Found no valid authentication method from providers={} with tokens={}", diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/AuthenticationProviderSelector.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/AuthenticationProviderSelector.java index 46420233a908..cdd1fdb381f6 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/AuthenticationProviderSelector.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/AuthenticationProviderSelector.java @@ -40,20 +40,9 @@ void configure(Configuration conf, Collection availableProviders); /** - * Chooses the authentication provider which should be used given the provided client context, - * using a cached {@link Configuration}, from the authentication providers passed in via - * {@link #configure(Configuration, Collection)}. + * Chooses the authentication provider which should be used given the provided client context from + * the authentication providers passed in via {@link #configure(Configuration, Collection)}. */ Pair> selectProvider(String clusterId, User user); - - /** - * Chooses the authentication provider which should be used given the provided client context and - * the supplied {@link Configuration}, from the authentication providers passed in via - * {@link #configure(Configuration, Collection)}. - */ - default Pair> - selectProvider(Configuration conf, String clusterId, User user) { - return selectProvider(clusterId, user); - } } diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/BuiltInProviderSelector.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/BuiltInProviderSelector.java index ae6dc55870dd..2c9968f6f71b 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/BuiltInProviderSelector.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/BuiltInProviderSelector.java @@ -98,12 +98,6 @@ public void configure(Configuration conf, @Override public Pair> selectProvider(String clusterId, User user) { - return selectProvider(conf, clusterId, user); - } - - @Override - public Pair> - selectProvider(Configuration conf, String clusterId, User user) { requireNonNull(clusterId, "Null clusterId was given"); requireNonNull(user, "Null user was given"); diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java index 6c220e3bd0b3..a78ff3386a46 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java @@ -205,8 +205,8 @@ static SaslClientAuthenticationProviders instantiate(Configuration conf) { * identifier and the user. */ public Pair> - selectProvider(Configuration conf, String clusterId, User clientUser) { - return selector.selectProvider(conf, clusterId, clientUser); + selectProvider(String clusterId, User clientUser) { + return selector.selectProvider(clusterId, clientUser); } @Override From 2954a44abf1d8b23fb254467547fe6198f422dec Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 5 Jan 2026 18:39:01 +0900 Subject: [PATCH 12/16] Revert "HBASE-29144 Add a failing test case to reproduce the reported issue" This reverts commit 5c70258c793e5b2aa1fec3c78fb91eb75a482ca6. --- .../client/TestRpcConnectionRegistry.java | 10 +- .../TestRpcConnectionRegistrySecure.java | 112 ------------------ 2 files changed, 3 insertions(+), 119 deletions(-) delete mode 100644 hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRpcConnectionRegistrySecure.java diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRpcConnectionRegistry.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRpcConnectionRegistry.java index cb294861c37b..d33cc943355c 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRpcConnectionRegistry.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRpcConnectionRegistry.java @@ -60,11 +60,12 @@ public class TestRpcConnectionRegistry { public static final HBaseClassTestRule CLASS_RULE = HBaseClassTestRule.forClass(TestRpcConnectionRegistry.class); - protected static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); + private static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); private RpcConnectionRegistry registry; - protected static void startMiniCluster() throws Exception { + @BeforeClass + public static void setUpBeforeClass() throws Exception { // allow refresh immediately so we will switch to use region servers soon. UTIL.getConfiguration().setLong(RpcConnectionRegistry.INITIAL_REFRESH_DELAY_SECS, 1); UTIL.getConfiguration().setLong(RpcConnectionRegistry.PERIODIC_REFRESH_INTERVAL_SECS, 1); @@ -74,11 +75,6 @@ protected static void startMiniCluster() throws Exception { HBaseTestingUtil.setReplicas(UTIL.getAdmin(), TableName.META_TABLE_NAME, 3); } - @BeforeClass - public static void setUpBeforeClass() throws Exception { - startMiniCluster(); - } - @AfterClass public static void tearDownAfterClass() throws Exception { UTIL.shutdownMiniCluster(); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRpcConnectionRegistrySecure.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRpcConnectionRegistrySecure.java deleted file mode 100644 index 7082e6e616a3..000000000000 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestRpcConnectionRegistrySecure.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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. - */ -package org.apache.hadoop.hbase.client; - -import static org.junit.Assert.assertTrue; - -import java.io.File; -import java.io.IOException; -import java.security.PrivilegedExceptionAction; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.hbase.HBaseClassTestRule; -import org.apache.hadoop.hbase.TableNameTestRule; -import org.apache.hadoop.hbase.security.HBaseKerberosUtils; -import org.apache.hadoop.hbase.security.access.SecureTestUtil; -import org.apache.hadoop.hbase.security.provider.SaslClientAuthenticationProviders; -import org.apache.hadoop.hbase.testclassification.ClientTests; -import org.apache.hadoop.hbase.testclassification.MediumTests; -import org.apache.hadoop.hbase.util.Bytes; -import org.apache.hadoop.minikdc.MiniKdc; -import org.apache.hadoop.security.UserGroupInformation; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -@Category({ MediumTests.class, ClientTests.class }) -public class TestRpcConnectionRegistrySecure extends TestRpcConnectionRegistry { - - @ClassRule - public static final HBaseClassTestRule CLASS_RULE = - HBaseClassTestRule.forClass(TestRpcConnectionRegistrySecure.class); - - private static final String SERVER_PRINCIPAL = "hbase/localhost"; - - private static File KEYTAB_FILE; - private static MiniKdc KDC; - - @Rule - public final TableNameTestRule name = new TableNameTestRule(); - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - KEYTAB_FILE = new File(UTIL.getDataTestDir("keytab").toUri().getPath()); - KDC = UTIL.setupMiniKdc(KEYTAB_FILE); - KDC.createPrincipal(KEYTAB_FILE, SERVER_PRINCIPAL); - - final Configuration conf = UTIL.getConfiguration(); - SecureTestUtil.enableSecurity(conf); - HBaseKerberosUtils.setSecuredConfiguration(conf, SERVER_PRINCIPAL + '@' + KDC.getRealm(), null); - - startMiniCluster(); - } - - @AfterClass - public static void tearDownAfterClass() throws Exception { - UTIL.shutdownMiniCluster(); - if (KDC != null) { - KDC.stop(); - } - KEYTAB_FILE.delete(); - } - - @Test - public void testSecureRpc() throws Exception { - UserGroupInformation.loginUserFromKeytab(SERVER_PRINCIPAL, KEYTAB_FILE.toString()); - final UserGroupInformation ugi = UserGroupInformation.getLoginUser(); - - // This is required because SaslClientAuthenticationProviders is already initialized - // in the process of starting up the mini cluster. - SaslClientAuthenticationProviders.reset(); - - // Make sure that existing connection are not reused. - UTIL.invalidateConnection(); - - assertTrue(ugi.doAs((PrivilegedExceptionAction) this::doTableTest)); - } - - private boolean doTableTest() throws IOException { - final int count = 10; - final byte[] cf = Bytes.toBytes("f"); - final byte[] cq = Bytes.toBytes("q"); - final TableDescriptor desc = TableDescriptorBuilder.newBuilder(name.getTableName()).build(); - int scannedRows = 0; - try (Table table = UTIL.createTable(desc, new byte[][] { cf }, UTIL.getConfiguration())) { - for (int i = 0; i < count; i++) { - table.put(new Put(Bytes.toBytes(i)).addColumn(cf, cq, "0".getBytes())); - } - for (Result result : table.getScanner(new Scan())) { - scannedRows++; - } - } - UTIL.deleteTableIfAny(name.getTableName()); - return scannedRows == count; - } -} From c9a015b94f8ebb47b51c58526623fb5646cc4ce0 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 5 Jan 2026 18:39:15 +0900 Subject: [PATCH 13/16] HBASE-29144 Add a failing test case to reproduce the reported issue --- ...riteWithDifferentConnectionRegistries.java | 14 ++-- ...thDifferentConnectionRegistriesSecure.java | 82 +++++++++++++++++++ 2 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestBasicReadWriteWithDifferentConnectionRegistriesSecure.java diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestBasicReadWriteWithDifferentConnectionRegistries.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestBasicReadWriteWithDifferentConnectionRegistries.java index 5746ffa67f6c..1a0c3cacf63f 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestBasicReadWriteWithDifferentConnectionRegistries.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestBasicReadWriteWithDifferentConnectionRegistries.java @@ -62,7 +62,7 @@ public class TestBasicReadWriteWithDifferentConnectionRegistries { private static final Logger LOG = LoggerFactory.getLogger(TestBasicReadWriteWithDifferentConnectionRegistries.class); - private static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); + protected static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); public enum RegistryImpl { ZK, @@ -100,11 +100,15 @@ public static void tearDownAfterClass() throws Exception { UTIL.shutdownMiniCluster(); } + protected Connection createConnectionFromUri(URI uri) throws Exception { + return ConnectionFactory.createConnection(uri); + } + @Before public void setUp() throws Exception { switch (impl) { case ZK: { - Configuration conf = HBaseConfiguration.create(); + Configuration conf = HBaseConfiguration.create(UTIL.getConfiguration()); conf.setClass(HConstants.CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY, ZKConnectionRegistry.class, ConnectionRegistry.class); String quorum = UTIL.getZkCluster().getAddress().toString(); @@ -116,7 +120,7 @@ public void setUp() throws Exception { break; } case RPC: { - Configuration conf = HBaseConfiguration.create(); + Configuration conf = HBaseConfiguration.create(UTIL.getConfiguration()); conf.setClass(HConstants.CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY, RpcConnectionRegistry.class, ConnectionRegistry.class); String bootstrapServers = @@ -131,14 +135,14 @@ public void setUp() throws Exception { String path = UTIL.getConfiguration().get(HConstants.ZOOKEEPER_ZNODE_PARENT); URI connectionUri = new URI("hbase+zk://" + quorum + path); LOG.info("connect to cluster through connection url: {}", connectionUri); - conn = ConnectionFactory.createConnection(connectionUri); + conn = createConnectionFromUri(connectionUri); break; } case RPC_URI: { URI connectionUri = new URI("hbase+rpc://" + UTIL.getMiniHBaseCluster().getMaster().getServerName().getAddress().toString()); LOG.info("connect to cluster through connection url: {}", connectionUri); - conn = ConnectionFactory.createConnection(connectionUri); + conn = createConnectionFromUri(connectionUri); break; } default: diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestBasicReadWriteWithDifferentConnectionRegistriesSecure.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestBasicReadWriteWithDifferentConnectionRegistriesSecure.java new file mode 100644 index 000000000000..cdef74db260b --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestBasicReadWriteWithDifferentConnectionRegistriesSecure.java @@ -0,0 +1,82 @@ +/* + * 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. + */ +package org.apache.hadoop.hbase.client; + +import java.io.File; +import java.net.URI; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HBaseClassTestRule; +import org.apache.hadoop.hbase.security.HBaseKerberosUtils; +import org.apache.hadoop.hbase.security.access.SecureTestUtil; +import org.apache.hadoop.hbase.security.provider.SaslClientAuthenticationProviders; +import org.apache.hadoop.hbase.testclassification.ClientTests; +import org.apache.hadoop.hbase.testclassification.MediumTests; +import org.apache.hadoop.minikdc.MiniKdc; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.experimental.categories.Category; + +@Category({ MediumTests.class, ClientTests.class }) +public class TestBasicReadWriteWithDifferentConnectionRegistriesSecure + extends TestBasicReadWriteWithDifferentConnectionRegistries { + + @ClassRule + public static final HBaseClassTestRule CLASS_RULE = + HBaseClassTestRule.forClass(TestBasicReadWriteWithDifferentConnectionRegistriesSecure.class); + + private static final String SERVER_PRINCIPAL = "hbase/localhost"; + + private static File KEYTAB_FILE; + private static MiniKdc KDC; + + @Override + protected Connection createConnectionFromUri(URI uri) throws Exception { + return ConnectionFactory.createConnection(uri, UTIL.getConfiguration()); + } + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + KEYTAB_FILE = new File(UTIL.getDataTestDir("keytab").toUri().getPath()); + KDC = UTIL.setupMiniKdc(KEYTAB_FILE); + KDC.createPrincipal(KEYTAB_FILE, SERVER_PRINCIPAL); + + final Configuration conf = UTIL.getConfiguration(); + SecureTestUtil.enableSecurity(conf); + HBaseKerberosUtils.setSecuredConfiguration(conf, SERVER_PRINCIPAL + '@' + KDC.getRealm(), null); + + UTIL.startMiniCluster(); + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + UTIL.shutdownMiniCluster(); + if (KDC != null) { + KDC.stop(); + } + KEYTAB_FILE.delete(); + } + + @Override + @Before + public void setUp() throws Exception { + SaslClientAuthenticationProviders.reset(); + super.setUp(); + } +} From c44416a6bfef1897509275e45977b5eb521d6141 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 5 Jan 2026 19:14:41 +0900 Subject: [PATCH 14/16] HBASE-29144 SaslClientAuthenticationProviders should not be a singleton --- .../hadoop/hbase/ipc/AbstractRpcClient.java | 3 +++ .../hbase/ipc/BlockingRpcConnection.java | 3 ++- .../hadoop/hbase/ipc/NettyRpcConnection.java | 3 ++- .../hadoop/hbase/ipc/RpcConnection.java | 11 +++++----- .../SaslClientAuthenticationProviders.java | 21 ++----------------- ...TestSaslClientAuthenticationProviders.java | 12 +++-------- .../mapreduce/TestTableMapReduceUtil.java | 7 ------- ...thDifferentConnectionRegistriesSecure.java | 9 -------- 8 files changed, 17 insertions(+), 52 deletions(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/AbstractRpcClient.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/AbstractRpcClient.java index 3742eb8118a1..a8c405700da7 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/AbstractRpcClient.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/AbstractRpcClient.java @@ -43,6 +43,7 @@ import org.apache.hadoop.hbase.net.Address; import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.security.UserProvider; +import org.apache.hadoop.hbase.security.provider.SaslClientAuthenticationProviders; import org.apache.hadoop.hbase.trace.TraceUtil; import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; import org.apache.hadoop.hbase.util.PoolMap; @@ -112,6 +113,7 @@ public abstract class AbstractRpcClient implements RpcC protected final String clusterId; protected final SocketAddress localAddr; protected final MetricsConnection metrics; + protected final SaslClientAuthenticationProviders authenticationProviders; protected final UserProvider userProvider; protected final CellBlockBuilder cellBlockBuilder; @@ -180,6 +182,7 @@ public AbstractRpcClient(Configuration conf, String clusterId, SocketAddress loc this.readTO = conf.getInt(SOCKET_TIMEOUT_READ, DEFAULT_SOCKET_TIMEOUT_READ); this.writeTO = conf.getInt(SOCKET_TIMEOUT_WRITE, DEFAULT_SOCKET_TIMEOUT_WRITE); this.metrics = metrics; + this.authenticationProviders = SaslClientAuthenticationProviders.getInstance(conf); this.maxConcurrentCallsPerServer = conf.getInt(HConstants.HBASE_CLIENT_PERSERVER_REQUESTS_THRESHOLD, HConstants.DEFAULT_HBASE_CLIENT_PERSERVER_REQUESTS_THRESHOLD); diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/BlockingRpcConnection.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/BlockingRpcConnection.java index 4b3d2de466b7..d07258a6f49f 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/BlockingRpcConnection.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/BlockingRpcConnection.java @@ -213,7 +213,8 @@ public void cleanup(IOException e) { BlockingRpcConnection(BlockingRpcClient rpcClient, ConnectionId remoteId) throws IOException { super(rpcClient.conf, AbstractRpcClient.WHEEL_TIMER, remoteId, rpcClient.clusterId, rpcClient.userProvider.isHBaseSecurityEnabled(), rpcClient.codec, rpcClient.compressor, - rpcClient.cellBlockBuilder, rpcClient.metrics, rpcClient.connectionAttributes); + rpcClient.cellBlockBuilder, rpcClient.metrics, rpcClient.authenticationProviders, + rpcClient.connectionAttributes); this.rpcClient = rpcClient; this.connectionHeaderPreamble = getConnectionHeaderPreamble(); ConnectionHeader header = getConnectionHeader(); diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/NettyRpcConnection.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/NettyRpcConnection.java index 85f7c0a3e61a..de7a62726079 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/NettyRpcConnection.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/NettyRpcConnection.java @@ -107,7 +107,8 @@ class NettyRpcConnection extends RpcConnection { NettyRpcConnection(NettyRpcClient rpcClient, ConnectionId remoteId) throws IOException { super(rpcClient.conf, AbstractRpcClient.WHEEL_TIMER, remoteId, rpcClient.clusterId, rpcClient.userProvider.isHBaseSecurityEnabled(), rpcClient.codec, rpcClient.compressor, - rpcClient.cellBlockBuilder, rpcClient.metrics, rpcClient.connectionAttributes); + rpcClient.cellBlockBuilder, rpcClient.metrics, rpcClient.authenticationProviders, + rpcClient.connectionAttributes); this.rpcClient = rpcClient; this.eventLoop = rpcClient.group.next(); byte[] connectionHeaderPreamble = getConnectionHeaderPreamble(); diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java index dbdb0e2037f8..c95de3ca88a4 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/RpcConnection.java @@ -121,6 +121,7 @@ abstract class RpcConnection { protected RpcConnection(Configuration conf, HashedWheelTimer timeoutTimer, ConnectionId remoteId, String clusterId, boolean isSecurityEnabled, Codec codec, CompressionCodec compressor, CellBlockBuilder cellBlockBuilder, MetricsConnection metrics, + SaslClientAuthenticationProviders authenticationProviders, Map connectionAttributes) throws IOException { this.timeoutTimer = timeoutTimer; this.codec = codec; @@ -133,22 +134,20 @@ protected RpcConnection(Configuration conf, HashedWheelTimer timeoutTimer, Conne this.securityInfo = SecurityInfo.getInfo(remoteId.getServiceName()); this.useSasl = isSecurityEnabled; - // Choose the correct Token and AuthenticationProvider for this client to use - SaslClientAuthenticationProviders providers = - SaslClientAuthenticationProviders.getInstance(conf); + // Choose the correct Token for this client to use Pair> pair; if (useSasl && securityInfo != null) { - pair = providers.selectProvider(clusterId, ticket); + pair = authenticationProviders.selectProvider(clusterId, ticket); if (pair == null) { if (LOG.isTraceEnabled()) { LOG.trace("Found no valid authentication method from providers={} with tokens={}", - providers.toString(), ticket.getTokens()); + authenticationProviders.toString(), ticket.getTokens()); } throw new RuntimeException("Found no valid authentication method from options"); } } else if (!useSasl) { // Hack, while SIMPLE doesn't go via SASL. - pair = providers.getSimpleProvider(); + pair = authenticationProviders.getSimpleProvider(); } else { throw new RuntimeException("Could not compute valid client authentication provider"); } diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java index a78ff3386a46..5611506579de 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java @@ -23,7 +23,6 @@ import java.util.HashMap; import java.util.Optional; import java.util.ServiceLoader; -import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseInterfaceAudience; @@ -48,9 +47,6 @@ public final class SaslClientAuthenticationProviders { public static final String SELECTOR_KEY = "hbase.client.sasl.provider.class"; public static final String EXTRA_PROVIDERS_KEY = "hbase.client.sasl.provider.extras"; - private static final AtomicReference providersRef = - new AtomicReference<>(); - private final Collection providers; private final AuthenticationProviderSelector selector; @@ -68,23 +64,10 @@ public int getNumRegisteredProviders() { } /** - * Returns a singleton instance of {@link SaslClientAuthenticationProviders}. + * Returns an instance of {@link SaslClientAuthenticationProviders}. */ public static synchronized SaslClientAuthenticationProviders getInstance(Configuration conf) { - SaslClientAuthenticationProviders providers = providersRef.get(); - if (providers == null) { - providers = instantiate(conf); - providersRef.set(providers); - } - - return providers; - } - - /** - * Removes the cached singleton instance of {@link SaslClientAuthenticationProviders}. - */ - public static synchronized void reset() { - providersRef.set(null); + return instantiate(conf); } /** diff --git a/hbase-client/src/test/java/org/apache/hadoop/hbase/security/provider/TestSaslClientAuthenticationProviders.java b/hbase-client/src/test/java/org/apache/hadoop/hbase/security/provider/TestSaslClientAuthenticationProviders.java index 029c880600b4..5945593ace19 100644 --- a/hbase-client/src/test/java/org/apache/hadoop/hbase/security/provider/TestSaslClientAuthenticationProviders.java +++ b/hbase-client/src/test/java/org/apache/hadoop/hbase/security/provider/TestSaslClientAuthenticationProviders.java @@ -68,20 +68,14 @@ public void testCannotAddTheSameProviderTwice() { } @Test - public void testInstanceIsCached() { + public void testInstanceIsNotCached() { Configuration conf = HBaseConfiguration.create(); SaslClientAuthenticationProviders providers1 = SaslClientAuthenticationProviders.getInstance(conf); SaslClientAuthenticationProviders providers2 = SaslClientAuthenticationProviders.getInstance(conf); - assertSame(providers1, providers2); - - SaslClientAuthenticationProviders.reset(); - - SaslClientAuthenticationProviders providers3 = - SaslClientAuthenticationProviders.getInstance(conf); - assertNotSame(providers1, providers3); - assertEquals(providers1.getNumRegisteredProviders(), providers3.getNumRegisteredProviders()); + assertNotSame(providers1, providers2); + assertEquals(providers1.getNumRegisteredProviders(), providers2.getNumRegisteredProviders()); } @Test(expected = RuntimeException.class) diff --git a/hbase-mapreduce/src/test/java/org/apache/hadoop/hbase/mapreduce/TestTableMapReduceUtil.java b/hbase-mapreduce/src/test/java/org/apache/hadoop/hbase/mapreduce/TestTableMapReduceUtil.java index 17a94c999566..32f8d635ab4f 100644 --- a/hbase-mapreduce/src/test/java/org/apache/hadoop/hbase/mapreduce/TestTableMapReduceUtil.java +++ b/hbase-mapreduce/src/test/java/org/apache/hadoop/hbase/mapreduce/TestTableMapReduceUtil.java @@ -30,7 +30,6 @@ import org.apache.hadoop.hbase.HBaseClassTestRule; import org.apache.hadoop.hbase.HBaseTestingUtil; import org.apache.hadoop.hbase.client.Scan; -import org.apache.hadoop.hbase.security.provider.SaslClientAuthenticationProviders; import org.apache.hadoop.hbase.security.token.AuthenticationTokenIdentifier; import org.apache.hadoop.hbase.testclassification.MapReduceTests; import org.apache.hadoop.hbase.testclassification.MediumTests; @@ -44,7 +43,6 @@ import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.token.Token; import org.apache.hadoop.security.token.TokenIdentifier; -import org.junit.After; import org.junit.ClassRule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -60,11 +58,6 @@ public class TestTableMapReduceUtil { public static final HBaseClassTestRule CLASS_RULE = HBaseClassTestRule.forClass(TestTableMapReduceUtil.class); - @After - public void after() { - SaslClientAuthenticationProviders.reset(); - } - /* * initTableSnapshotMapperJob is tested in {@link TestTableSnapshotInputFormat} because the method * depends on an online cluster. diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestBasicReadWriteWithDifferentConnectionRegistriesSecure.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestBasicReadWriteWithDifferentConnectionRegistriesSecure.java index cdef74db260b..e069b47e7ab6 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestBasicReadWriteWithDifferentConnectionRegistriesSecure.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestBasicReadWriteWithDifferentConnectionRegistriesSecure.java @@ -23,12 +23,10 @@ import org.apache.hadoop.hbase.HBaseClassTestRule; import org.apache.hadoop.hbase.security.HBaseKerberosUtils; import org.apache.hadoop.hbase.security.access.SecureTestUtil; -import org.apache.hadoop.hbase.security.provider.SaslClientAuthenticationProviders; import org.apache.hadoop.hbase.testclassification.ClientTests; import org.apache.hadoop.hbase.testclassification.MediumTests; import org.apache.hadoop.minikdc.MiniKdc; import org.junit.AfterClass; -import org.junit.Before; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.experimental.categories.Category; @@ -72,11 +70,4 @@ public static void tearDownAfterClass() throws Exception { } KEYTAB_FILE.delete(); } - - @Override - @Before - public void setUp() throws Exception { - SaslClientAuthenticationProviders.reset(); - super.setUp(); - } } From e461eeb7ed34683a4562d53c2bb794d602b51fb5 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 5 Jan 2026 19:24:27 +0900 Subject: [PATCH 15/16] HBASE-29144 Remove unnecessary indirection --- .../provider/SaslClientAuthenticationProviders.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java index 5611506579de..ec96c89ef152 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/SaslClientAuthenticationProviders.java @@ -63,13 +63,6 @@ public int getNumRegisteredProviders() { return providers.size(); } - /** - * Returns an instance of {@link SaslClientAuthenticationProviders}. - */ - public static synchronized SaslClientAuthenticationProviders getInstance(Configuration conf) { - return instantiate(conf); - } - /** * Adds the given {@code provider} to the set, only if an equivalent provider does not already * exist in the set. @@ -148,7 +141,7 @@ static void addExplicitProviders(Configuration conf, * Instantiates all client authentication providers and returns an instance of * {@link SaslClientAuthenticationProviders}. */ - static SaslClientAuthenticationProviders instantiate(Configuration conf) { + public static SaslClientAuthenticationProviders getInstance(Configuration conf) { ServiceLoader loader = ServiceLoader.load(SaslClientAuthenticationProvider.class, SaslClientAuthenticationProviders.class.getClassLoader()); From 181b4ac8666c7c6955976f75958aabee1d53d616 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 5 Jan 2026 19:26:04 +0900 Subject: [PATCH 16/16] HBASE-29144 Update javadoc --- .../security/provider/AuthenticationProviderSelector.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/AuthenticationProviderSelector.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/AuthenticationProviderSelector.java index cdd1fdb381f6..72884da91123 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/AuthenticationProviderSelector.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/provider/AuthenticationProviderSelector.java @@ -33,8 +33,7 @@ public interface AuthenticationProviderSelector { /** * Initializes the implementation with configuration and a set of providers available. This method - * should be called exactly once per implementation prior to calling - * {@link #selectProvider(String, User)}. + * should be called prior to calling {@link #selectProvider(String, User)}. */ void configure(Configuration conf, Collection availableProviders);