This is an automated email from the ASF dual-hosted git repository.
nihaljain pushed a commit to branch branch-2
in repository https://gitbox.apache.org/repos/asf/hbase.git
The following commit(s) were added to refs/heads/branch-2 by this push:
new 3481aefce99 HBASE-24179 Backport fix for "Netty SASL implementation
does not wait for challenge response" to branch-2.x (#5472)
3481aefce99 is described below
commit 3481aefce99637a34d97860100beabf600b65845
Author: Nihal Jain <[email protected]>
AuthorDate: Wed Oct 25 13:47:27 2023 +0530
HBASE-24179 Backport fix for "Netty SASL implementation does not wait for
challenge response" to branch-2.x (#5472)
- Backport HBASE-23881 "Netty SASL implementation does not wait for
challenge response"
- Backport HBASE-24263 "TestDelegationToken is broken"
- Fix assertion to check for InvalidToken.class.getName() to ensure bug is
fixed
Signed-off-by: Duo Zhang <[email protected]>
---
.../hbase/security/AbstractHBaseSaslRpcClient.java | 10 +++++++--
.../security/NettyHBaseSaslRpcClientHandler.java | 26 ++++++++++++++++++----
.../ShadeSaslClientAuthenticationProvider.java | 6 +++++
.../ShadeSaslServerAuthenticationProvider.java | 1 -
.../TestShadeSaslAuthenticationProvider.java | 2 +-
.../hbase/ipc/NettyHBaseSaslRpcServerHandler.java | 2 +-
.../hbase/ipc/SimpleServerRpcConnection.java | 2 +-
7 files changed, 39 insertions(+), 10 deletions(-)
diff --git
a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/AbstractHBaseSaslRpcClient.java
b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/AbstractHBaseSaslRpcClient.java
index b2bf6a4f536..87b2287a601 100644
---
a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/AbstractHBaseSaslRpcClient.java
+++
b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/AbstractHBaseSaslRpcClient.java
@@ -83,12 +83,18 @@ public abstract class AbstractHBaseSaslRpcClient {
}
}
+ /**
+ * Computes the initial response a client sends to a server to begin the
SASL challenge/response
+ * handshake. If the client's SASL mechanism does not have an initial
response, an empty token
+ * will be returned without querying the evaluateChallenge method, as an
authentication processing
+ * must be started by client.
+ * @return The client's initial response to send the server (which may be
empty).
+ */
public byte[] getInitialResponse() throws SaslException {
if (saslClient.hasInitialResponse()) {
return saslClient.evaluateChallenge(EMPTY_TOKEN);
- } else {
- return EMPTY_TOKEN;
}
+ return EMPTY_TOKEN;
}
public boolean isComplete() {
diff --git
a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/NettyHBaseSaslRpcClientHandler.java
b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/NettyHBaseSaslRpcClientHandler.java
index 324b8ee70ed..48e631c7629 100644
---
a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/NettyHBaseSaslRpcClientHandler.java
+++
b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/NettyHBaseSaslRpcClientHandler.java
@@ -57,6 +57,8 @@ public class NettyHBaseSaslRpcClientHandler extends
SimpleChannelInboundHandler<
private final Configuration conf;
+ private final SaslClientAuthenticationProvider provider;
+
// flag to mark if Crypto AES encryption is enable
private boolean needProcessConnectionHeader = false;
@@ -71,6 +73,7 @@ public class NettyHBaseSaslRpcClientHandler extends
SimpleChannelInboundHandler<
this.saslPromise = saslPromise;
this.ugi = ugi;
this.conf = conf;
+ this.provider = provider;
this.saslRpcClient = new NettyHBaseSaslRpcClient(conf, provider, token,
serverAddr,
securityInfo, fallbackAllowed, conf.get("hbase.rpc.protection",
SaslUtil.QualityOfProtection.AUTHENTICATION.name().toLowerCase()));
@@ -87,6 +90,10 @@ public class NettyHBaseSaslRpcClientHandler extends
SimpleChannelInboundHandler<
return;
}
+ // HBASE-23881 Clearly log when the client thinks that the SASL
negotiation is complete.
+ if (LOG.isTraceEnabled()) {
+ LOG.trace("SASL negotiation for {} is complete",
provider.getSaslAuthMethod().getName());
+ }
saslRpcClient.setupSaslHandler(ctx.pipeline(), HANDLER_NAME);
removeHandlers(ctx);
@@ -125,10 +132,19 @@ public class NettyHBaseSaslRpcClientHandler extends
SimpleChannelInboundHandler<
return saslRpcClient.getInitialResponse();
}
});
- if (initialResponse != null) {
- writeResponse(ctx, initialResponse);
- }
- tryComplete(ctx);
+ assert initialResponse != null;
+ writeResponse(ctx, initialResponse);
+ // HBASE-23881 We do not want to check if the SaslClient thinks the
handshake is
+ // complete as, at this point, we've not heard a back from the server
with it's reply
+ // to our first challenge response. We should wait for at least one reply
+ // from the server before calling negotiation complete.
+ //
+ // Each SASL mechanism has its own handshake. Some mechanisms calculate
a single client buffer
+ // to be sent to the server while others have multiple exchanges to
negotiate authentication.
+ // GSSAPI(Kerberos) and DIGEST-MD5 both are examples of mechanisms which
have multiple steps.
+ // Mechanisms which have multiple steps will not return true on
`SaslClient#isComplete()`
+ // until the handshake has fully completed. Mechanisms which only send a
single buffer may
+ // return true on `isComplete()` after that initial response is
calculated.
} catch (Exception e) {
// the exception thrown by handlerAdded will not be passed to the
exceptionCaught below
// because netty will remove a handler if handlerAdded throws an
exception.
@@ -163,6 +179,8 @@ public class NettyHBaseSaslRpcClientHandler extends
SimpleChannelInboundHandler<
});
if (response != null) {
writeResponse(ctx, response);
+ } else {
+ LOG.trace("SASL challenge response was empty, not sending response to
server.");
}
tryComplete(ctx);
}
diff --git
a/hbase-examples/src/main/java/org/apache/hadoop/hbase/security/provider/example/ShadeSaslClientAuthenticationProvider.java
b/hbase-examples/src/main/java/org/apache/hadoop/hbase/security/provider/example/ShadeSaslClientAuthenticationProvider.java
index 45bf5f015bf..d0930a0f314 100644
---
a/hbase-examples/src/main/java/org/apache/hadoop/hbase/security/provider/example/ShadeSaslClientAuthenticationProvider.java
+++
b/hbase-examples/src/main/java/org/apache/hadoop/hbase/security/provider/example/ShadeSaslClientAuthenticationProvider.java
@@ -60,6 +60,12 @@ public class ShadeSaslClientAuthenticationProvider extends
ShadeSaslAuthenticati
return userInfoPB.build();
}
+ @Override
+ public boolean canRetry() {
+ // A static username/password either works or it doesn't. No kind of
relogin/retry necessary.
+ return false;
+ }
+
static class ShadeSaslClientCallbackHandler implements CallbackHandler {
private final String username;
private final char[] password;
diff --git
a/hbase-examples/src/main/java/org/apache/hadoop/hbase/security/provider/example/ShadeSaslServerAuthenticationProvider.java
b/hbase-examples/src/main/java/org/apache/hadoop/hbase/security/provider/example/ShadeSaslServerAuthenticationProvider.java
index 3454bc30a1d..9f0e70133db 100644
---
a/hbase-examples/src/main/java/org/apache/hadoop/hbase/security/provider/example/ShadeSaslServerAuthenticationProvider.java
+++
b/hbase-examples/src/main/java/org/apache/hadoop/hbase/security/provider/example/ShadeSaslServerAuthenticationProvider.java
@@ -139,7 +139,6 @@ public class ShadeSaslServerAuthenticationProvider extends
ShadeSaslAuthenticati
@Override
public void handle(Callback[] callbacks) throws InvalidToken,
UnsupportedCallbackException {
- LOG.info("SaslServerCallbackHandler called", new Exception());
NameCallback nc = null;
PasswordCallback pc = null;
AuthorizeCallback ac = null;
diff --git
a/hbase-examples/src/test/java/org/apache/hadoop/hbase/security/provider/example/TestShadeSaslAuthenticationProvider.java
b/hbase-examples/src/test/java/org/apache/hadoop/hbase/security/provider/example/TestShadeSaslAuthenticationProvider.java
index 75894d69b0a..4ee753b1d26 100644
---
a/hbase-examples/src/test/java/org/apache/hadoop/hbase/security/provider/example/TestShadeSaslAuthenticationProvider.java
+++
b/hbase-examples/src/test/java/org/apache/hadoop/hbase/security/provider/example/TestShadeSaslAuthenticationProvider.java
@@ -305,7 +305,7 @@ public class TestShadeSaslAuthenticationProvider {
rootCause.printStackTrace(new PrintWriter(writer));
String text = writer.toString();
assertTrue("Message did not contain expected text",
- text.contains("Connection reset by peer"));
+ text.contains(InvalidToken.class.getName()));
}
}
}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/NettyHBaseSaslRpcServerHandler.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/NettyHBaseSaslRpcServerHandler.java
index dd6f84daae3..47c748d6d70 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/NettyHBaseSaslRpcServerHandler.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/NettyHBaseSaslRpcServerHandler.java
@@ -89,7 +89,7 @@ class NettyHBaseSaslRpcServerHandler extends
SimpleChannelInboundHandler<ByteBuf
rpcServer.metrics.authenticationFailure();
String clientIP = this.toString();
// attempting user could be null
- RpcServer.AUDITLOG.warn("{}{}: {}", RpcServer.AUTH_FAILED_FOR, clientIP,
+ RpcServer.AUDITLOG.warn("{} {}: {}", RpcServer.AUTH_FAILED_FOR, clientIP,
conn.saslServer != null ? conn.saslServer.getAttemptingUser() :
"Unknown");
NettyFutureUtils.safeClose(ctx);
}
diff --git
a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/SimpleServerRpcConnection.java
b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/SimpleServerRpcConnection.java
index ac705d7a26f..0ab011d13c3 100644
---
a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/SimpleServerRpcConnection.java
+++
b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/SimpleServerRpcConnection.java
@@ -229,7 +229,7 @@ class SimpleServerRpcConnection extends ServerRpcConnection
{
this.rpcServer.metrics.authenticationFailure();
String clientIP = this.toString();
// attempting user could be null
- RpcServer.AUDITLOG.warn("{}{}: {}", RpcServer.AUTH_FAILED_FOR,
clientIP,
+ RpcServer.AUDITLOG.warn("{} {}: {}", RpcServer.AUTH_FAILED_FOR,
clientIP,
saslServer.getAttemptingUser());
throw e;
} finally {