Skip to content

Commit adf8d9b

Browse files
committed
HBASE-28377 Fallback to simple is broken for blocking rpc client (#5690)
Signed-off-by: Bryan Beaudreault <[email protected]> (cherry picked from commit 7bc07a6)
1 parent a40b9fa commit adf8d9b

File tree

3 files changed

+90
-21
lines changed

3 files changed

+90
-21
lines changed

hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/BlockingRpcConnection.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,7 @@ public Boolean run() throws IOException {
546546
// fall back to simple auth because server told us so.
547547
// do not change authMethod and useSasl here, we should start from secure when
548548
// reconnecting because regionserver may change its sasl config after restart.
549+
saslRpcClient = null;
549550
}
550551
}
551552
createStreams(inStream, outStream);

hbase-client/src/main/java/org/apache/hadoop/hbase/security/HBaseSaslRpcClient.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import javax.security.sasl.SaslException;
3333
import org.apache.hadoop.conf.Configuration;
3434
import org.apache.hadoop.hbase.io.crypto.aes.CryptoAES;
35+
import org.apache.hadoop.hbase.ipc.FallbackDisallowedException;
3536
import org.apache.hadoop.hbase.security.provider.SaslClientAuthenticationProvider;
3637
import org.apache.hadoop.io.WritableUtils;
3738
import org.apache.hadoop.ipc.RemoteException;
@@ -107,12 +108,9 @@ public boolean saslConnect(InputStream inS, OutputStream outS) throws IOExceptio
107108
int len = inStream.readInt();
108109
if (len == SaslUtil.SWITCH_TO_SIMPLE_AUTH) {
109110
if (!fallbackAllowed) {
110-
throw new IOException("Server asks us to fall back to SIMPLE auth, "
111-
+ "but this client is configured to only allow secure connections.");
112-
}
113-
if (LOG.isDebugEnabled()) {
114-
LOG.debug("Server asks us to fall back to simple auth.");
111+
throw new FallbackDisallowedException();
115112
}
113+
LOG.debug("Server asks us to fall back to simple auth.");
116114
dispose();
117115
return false;
118116
}

hbase-server/src/test/java/org/apache/hadoop/hbase/security/AbstractTestSecureIPC.java

Lines changed: 86 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,22 @@
2424
import static org.apache.hadoop.hbase.security.HBaseKerberosUtils.loginKerberosPrincipal;
2525
import static org.apache.hadoop.hbase.security.HBaseKerberosUtils.setSecuredConfiguration;
2626
import static org.apache.hadoop.hbase.security.provider.SaslClientAuthenticationProviders.SELECTOR_KEY;
27+
import static org.hamcrest.MatcherAssert.assertThat;
28+
import static org.hamcrest.Matchers.either;
29+
import static org.hamcrest.Matchers.instanceOf;
2730
import static org.junit.Assert.assertEquals;
2831
import static org.junit.Assert.assertNotSame;
2932
import static org.junit.Assert.assertSame;
3033
import static org.junit.Assert.assertThrows;
3134
import static org.junit.Assert.fail;
3235

36+
import java.io.EOFException;
3337
import java.io.File;
3438
import java.io.IOException;
3539
import java.lang.reflect.Field;
3640
import java.net.InetAddress;
3741
import java.net.InetSocketAddress;
42+
import java.security.PrivilegedExceptionAction;
3843
import java.util.ArrayList;
3944
import java.util.Collections;
4045
import java.util.Map;
@@ -44,12 +49,13 @@
4449
import org.apache.hadoop.conf.Configuration;
4550
import org.apache.hadoop.hbase.HBaseTestingUtility;
4651
import org.apache.hadoop.hbase.HConstants;
52+
import org.apache.hadoop.hbase.exceptions.ConnectionClosedException;
53+
import org.apache.hadoop.hbase.ipc.FallbackDisallowedException;
4754
import org.apache.hadoop.hbase.ipc.FifoRpcScheduler;
4855
import org.apache.hadoop.hbase.ipc.RpcClient;
4956
import org.apache.hadoop.hbase.ipc.RpcClientFactory;
5057
import org.apache.hadoop.hbase.ipc.RpcServer;
5158
import org.apache.hadoop.hbase.ipc.RpcServerFactory;
52-
import org.apache.hadoop.hbase.ipc.RpcServerInterface;
5359
import org.apache.hadoop.hbase.security.provider.AuthenticationProviderSelector;
5460
import org.apache.hadoop.hbase.security.provider.BuiltInProviderSelector;
5561
import org.apache.hadoop.hbase.security.provider.SaslAuthMethod;
@@ -95,6 +101,7 @@ protected static void initKDCAndConf() throws Exception {
95101
// set a smaller timeout and retry to speed up tests
96102
TEST_UTIL.getConfiguration().setInt(RpcClient.SOCKET_TIMEOUT_READ, 2000);
97103
TEST_UTIL.getConfiguration().setInt("hbase.security.relogin.maxretries", 1);
104+
TEST_UTIL.getConfiguration().setInt("hbase.security.relogin.maxbackoff", 100);
98105
}
99106

100107
protected static void stopKDC() throws InterruptedException {
@@ -237,7 +244,7 @@ public String getTokenKind() {
237244
}
238245

239246
@Test
240-
public void testRpcFallbackToSimpleAuth() throws Exception {
247+
public void testRpcServerFallbackToSimpleAuth() throws Exception {
241248
String clientUsername = "testuser";
242249
UserGroupInformation clientUgi =
243250
UserGroupInformation.createUserForTesting(clientUsername, new String[] { clientUsername });
@@ -252,6 +259,59 @@ public void testRpcFallbackToSimpleAuth() throws Exception {
252259
callRpcService(User.create(clientUgi));
253260
}
254261

262+
@Test
263+
public void testRpcServerDisallowFallbackToSimpleAuth() throws Exception {
264+
String clientUsername = "testuser";
265+
UserGroupInformation clientUgi =
266+
UserGroupInformation.createUserForTesting(clientUsername, new String[] { clientUsername });
267+
268+
// check that the client user is insecure
269+
assertNotSame(ugi, clientUgi);
270+
assertEquals(AuthenticationMethod.SIMPLE, clientUgi.getAuthenticationMethod());
271+
assertEquals(clientUsername, clientUgi.getUserName());
272+
273+
clientConf.set(User.HBASE_SECURITY_CONF_KEY, "simple");
274+
serverConf.setBoolean(RpcServer.FALLBACK_TO_INSECURE_CLIENT_AUTH, false);
275+
IOException error =
276+
assertThrows(IOException.class, () -> callRpcService(User.create(clientUgi)));
277+
// server just closes the connection, so we could get broken pipe, or EOF, or connection closed
278+
if (error.getMessage() == null || !error.getMessage().contains("Broken pipe")) {
279+
assertThat(error,
280+
either(instanceOf(EOFException.class)).or(instanceOf(ConnectionClosedException.class)));
281+
}
282+
}
283+
284+
@Test
285+
public void testRpcClientFallbackToSimpleAuth() throws Exception {
286+
String serverUsername = "testuser";
287+
UserGroupInformation serverUgi =
288+
UserGroupInformation.createUserForTesting(serverUsername, new String[] { serverUsername });
289+
// check that the server user is insecure
290+
assertNotSame(ugi, serverUgi);
291+
assertEquals(AuthenticationMethod.SIMPLE, serverUgi.getAuthenticationMethod());
292+
assertEquals(serverUsername, serverUgi.getUserName());
293+
294+
serverConf.set(User.HBASE_SECURITY_CONF_KEY, "simple");
295+
clientConf.setBoolean(RpcClient.IPC_CLIENT_FALLBACK_TO_SIMPLE_AUTH_ALLOWED_KEY, true);
296+
callRpcService(User.create(serverUgi), User.create(ugi));
297+
}
298+
299+
@Test
300+
public void testRpcClientDisallowFallbackToSimpleAuth() throws Exception {
301+
String serverUsername = "testuser";
302+
UserGroupInformation serverUgi =
303+
UserGroupInformation.createUserForTesting(serverUsername, new String[] { serverUsername });
304+
// check that the server user is insecure
305+
assertNotSame(ugi, serverUgi);
306+
assertEquals(AuthenticationMethod.SIMPLE, serverUgi.getAuthenticationMethod());
307+
assertEquals(serverUsername, serverUgi.getUserName());
308+
309+
serverConf.set(User.HBASE_SECURITY_CONF_KEY, "simple");
310+
clientConf.setBoolean(RpcClient.IPC_CLIENT_FALLBACK_TO_SIMPLE_AUTH_ALLOWED_KEY, false);
311+
assertThrows(FallbackDisallowedException.class,
312+
() -> callRpcService(User.create(serverUgi), User.create(ugi)));
313+
}
314+
255315
private void setRpcProtection(String clientProtection, String serverProtection) {
256316
clientConf.set("hbase.rpc.protection", clientProtection);
257317
serverConf.set("hbase.rpc.protection", serverProtection);
@@ -263,25 +323,25 @@ private void setRpcProtection(String clientProtection, String serverProtection)
263323
@Test
264324
public void testSaslWithCommonQop() throws Exception {
265325
setRpcProtection("privacy,authentication", "authentication");
266-
callRpcService(User.create(ugi));
326+
callRpcService();
267327

268328
setRpcProtection("authentication", "privacy,authentication");
269-
callRpcService(User.create(ugi));
329+
callRpcService();
270330

271331
setRpcProtection("integrity,authentication", "privacy,authentication");
272-
callRpcService(User.create(ugi));
332+
callRpcService();
273333

274334
setRpcProtection("integrity,authentication", "integrity,authentication");
275-
callRpcService(User.create(ugi));
335+
callRpcService();
276336

277337
setRpcProtection("privacy,authentication", "privacy,authentication");
278-
callRpcService(User.create(ugi));
338+
callRpcService();
279339
}
280340

281341
@Test
282342
public void testSaslNoCommonQop() throws Exception {
283343
setRpcProtection("integrity", "privacy");
284-
SaslException se = assertThrows(SaslException.class, () -> callRpcService(User.create(ugi)));
344+
SaslException se = assertThrows(SaslException.class, () -> callRpcService());
285345
assertEquals("No common protection layer between client and server", se.getMessage());
286346
}
287347

@@ -292,7 +352,7 @@ public void testSaslNoCommonQop() throws Exception {
292352
public void testSaslWithCryptoAES() throws Exception {
293353
setRpcProtection("privacy", "privacy");
294354
setCryptoAES("true", "true");
295-
callRpcService(User.create(ugi));
355+
callRpcService();
296356
}
297357

298358
/**
@@ -303,11 +363,11 @@ public void testDifferentConfWithCryptoAES() throws Exception {
303363
setRpcProtection("privacy", "privacy");
304364

305365
setCryptoAES("false", "true");
306-
callRpcService(User.create(ugi));
366+
callRpcService();
307367

308368
setCryptoAES("true", "false");
309369
try {
310-
callRpcService(User.create(ugi));
370+
callRpcService();
311371
fail("The exception should be thrown out for the rpc timeout.");
312372
} catch (Exception e) {
313373
// ignore the expected exception
@@ -323,18 +383,20 @@ private void setCryptoAES(String clientCryptoAES, String serverCryptoAES) {
323383
* Sets up a RPC Server and a Client. Does a RPC checks the result. If an exception is thrown from
324384
* the stub, this function will throw root cause of that exception.
325385
*/
326-
private void callRpcService(User clientUser) throws Exception {
386+
private void callRpcService(User serverUser, User clientUser) throws Exception {
327387
SecurityInfo securityInfoMock = Mockito.mock(SecurityInfo.class);
328388
Mockito.when(securityInfoMock.getServerPrincipal())
329389
.thenReturn(HBaseKerberosUtils.KRB_PRINCIPAL);
330390
SecurityInfo.addInfo("TestProtobufRpcProto", securityInfoMock);
331391

332392
InetSocketAddress isa = new InetSocketAddress(HOST, 0);
333393

334-
RpcServerInterface rpcServer = RpcServerFactory.createRpcServer(null, "AbstractTestSecureIPC",
335-
Lists
336-
.newArrayList(new RpcServer.BlockingServiceAndInterface((BlockingService) SERVICE, null)),
337-
isa, serverConf, new FifoRpcScheduler(serverConf, 1));
394+
RpcServer rpcServer = serverUser.getUGI()
395+
.doAs((PrivilegedExceptionAction<
396+
RpcServer>) () -> RpcServerFactory.createRpcServer(null, "AbstractTestSecureIPC",
397+
Lists.newArrayList(
398+
new RpcServer.BlockingServiceAndInterface((BlockingService) SERVICE, null)),
399+
isa, serverConf, new FifoRpcScheduler(serverConf, 1)));
338400
rpcServer.start();
339401
try (RpcClient rpcClient =
340402
RpcClientFactory.createClient(clientConf, HConstants.DEFAULT_CLUSTER_ID.toString())) {
@@ -364,6 +426,14 @@ public void uncaughtException(Thread th, Throwable ex) {
364426
}
365427
}
366428

429+
private void callRpcService(User clientUser) throws Exception {
430+
callRpcService(User.create(ugi), clientUser);
431+
}
432+
433+
private void callRpcService() throws Exception {
434+
callRpcService(User.create(ugi));
435+
}
436+
367437
public static class TestThread extends Thread {
368438
private final BlockingInterface stub;
369439

0 commit comments

Comments
 (0)