Repository navigation
Fix ZkClient timeout after SASL DIGEST-MD5 authentication - #3185
adityaanikam wants to merge 5 commits into
Conversation
isKerberosAuthEnabled (added by a prior fix for Kerberos-enabled ZooKeeper connections) only recognized Krb5LoginModule, so waitUntilConnected waited for SyncConnected for any other SASL login module. When a ZooKeeper server requires SASL client authentication, the client reaches SaslAuthenticated for DIGEST-MD5 the same way it does for Kerberos, and SyncConnected never follows, so the controller and participant timed out even after a successful login. Extract the login module check into isSaslAuthLoginModule and extend it to also match DigestLoginModule, so DIGEST-MD5 sessions wait for the same terminal state Kerberos sessions already do. Kerberos behavior is unchanged. Added a unit test for the extracted predicate covering Kerberos, Digest and a non-SASL module. It does not require a live SASL handshake, since the earlier connection-based approach was unreliable against a non-SASL test server. Fixes apache#3184
There was a problem hiding this comment.
Should this method name change now? Same with the Javadoc for the return statement
There was a problem hiding this comment.
Good point, the method covers DIGEST as well now, so I renamed it to isSaslAuthEnabled and updated its javadoc (including the @return), the log message, and the tests that reach it by reflection. Pushed as a separate commit.
The method also returns true for DIGEST login modules since the previous commit, so the name and javadoc no longer said what it checks. Renamed it, updated the javadoc, the step comments and the log message, and renamed the tests and the reflection helper that reach it.
|
This logic hard-codes only Krb5LoginModule and DigestLoginModule as SASL authentication. ZooKeeper clients can use a custom or wrapped JAAS LoginModule; in that case, SASL may still complete and emit SaslAuthenticated, but this method returns false and waitUntilConnected() waits for SyncConnected instead. That can reintroduce the connection timeout this change is trying to fix. Can we determine the expected connection state from whether client SASL is enabled/configured, rather than from a substring match on two specific login-module class names? At minimum, please add coverage for a non-built-in/custom login-module configuration. |
|
Thanks, that makes sense. I checked how ZooKeeper decides this: ZooKeeperSaslClient builds its SASL client whenever the JAAS config has an entry for the client context, whatever login module that entry names, and clientTunneledAuthenticationInProgress() uses the same check. So the module class name is not what decides it. I removed the Krb5LoginModule/DigestLoginModule match. isSaslAuthEnabled() now returns true when the SASL client is enabled and the JAAS config has an entry for the client context. I also replaced the predicate test with tests for a DIGEST module and a custom module. They enable SASL after the client is built, because with SASL expected a live connection to the non-SASL test server would wait for a SaslAuthenticated event that never arrives. |
junkaixue
left a comment
There was a problem hiding this comment.
Could we add an integration test that configures a DIGEST-enabled server and client before building ZkClient, then asserts that the client connects successfully? This helper deliberately connects to the non-SASL server first and enables SASL afterward. The current tests therefore verify isSaslAuthEnabled() by reflection, but do not exercise the waitUntilConnected() path this PR is intended to fix. A regression that still times out during a real DIGEST connection would pass these tests.
The test starts a ZooKeeper server that requires DIGEST SASL, builds the client with SASL already configured, and waits for the connection after authentication has completed. It fails when waitUntilConnected() expects SyncConnected.
|
Thanks, I added the integration test in 4b63a5a. It starts a ZooKeeper server that requires DIGEST SASL, builds the client with SASL already configured, and checks that the client connects. It then waits for SaslAuthenticated and checks that waitUntilConnected() returns. With the fix, the five tests in TestZkClientKerberos pass. With waitUntilConnected() expecting SyncConnected again, as before the fix, the new test fails. On a local server the client sees SyncConnected before SaslAuthenticated replaces it, so building the client alone passes either way. The waitUntilConnected() call after SaslAuthenticated is what makes the old behavior fail. |
|
Thanks for the integration test and for grounding the check in how 1. Behavior when SASL is configured but login fails (blocking)
Failing fast may well be the right choice, but it is a behavior change beyond the bug fix. Have 2. PR description does not match the actual mechanism The description says 3. Test nits
|
|
Thanks for the detailed review. I pushed ca9d35b.
The full zookeeper-api module passes (106 tests). |
|
Thanks, ca9d35b covers everything from the last round, and I verified TestZkClientSasl passes when merged onto current master (ZooKeeper 3.9.3). One last change, and it corrects my own earlier suggestion: please drop the || saslClient.isFailed() half of isSaslSkipped() and keep only the null check you added. When the SASL client is in the failed state, ClientCnxn moves the session to AUTH_FAILED, so that session is never usable. Accepting SyncConnected there only lets connect() return a dead client in the window before the AuthFailed event is processed. I reproduced that with a Client JAAS entry whose login succeeds but produces no SASL client: build() returns with zkState=AUTH_FAILED. With only the null check it times out with ZkTimeoutException, and your tests still pass. Please also trim "or it failed" from the Javadoc and the PR description. LGTM once that is in. Also, I triggered the approval workflows. |
Fixes #3184
ZooKeeper always reports
SyncConnectedfirst. When a SASL client is used, it then reportsSaslAuthenticated, which replaces the stored connection state, so the state is no longerSyncConnectedafter authentication.waitUntilConnectedtherefore waits forSaslAuthenticatedwhen SASL is configured. That check was added in #3102 to fix #3101 (it was then namedisKerberosAuthEnabled) and it only recognizedKrb5LoginModule. A DIGEST-MD5 client against a server that requires SASL client authentication (sessionRequireClientSASLAuth=true) behaves the same way, butwaitUntilConnectedkept waiting forSyncConnected. Any call made after the state was replaced, for example a reconnect,retryUntilConnectedafter a connection loss, or a race on the initial connect, then times out although the authentication itself succeeded.The check no longer matches login module class names.
isSaslAuthEnabled(renamed fromisKerberosAuthEnabled) now returns true when the SASL client is enabled and the JAAS config has an entry for the client context. ZooKeeper builds its SASL client whenever that entry exists, whatever login module it names, so this covers Kerberos, DIGEST and custom modules.Because the check now applies to every SASL module,
waitUntilConnectedalso acceptsSyncConnectedwhen ZooKeeper connected without SASL: its SASL client could not be created, for example because the login failed (missing keytab, expired ticket cache, a login module that cannot be loaded), or it failed. ZooKeeper then continues to connect without SASL if the server allows it, so the session is usable but never reachesSaslAuthenticated. Before #3102 that connection worked, and this keeps it working instead of timing out.Testing:
TestZkClientKerberosis renamed toTestZkClientSasl, as it covers more than Kerberos now.testConnectsToDigestSaslServerconnects to an in-process ZooKeeper server that requires DIGEST SASL (sessionRequireClientSASLAuth=true) and checkswaitUntilConnectedafter authentication.testConnectsWhenSaslLoginFailsuses a login module that always fails against the non-SASL test server. It fails without the change withZkTimeoutException: Unable to connect to zookeeper server within timeoutand passes with it.TestZkClientSaslruns 6 tests with no failures, and the fullzookeeper-apimodule run passes (106 tests, 0 failures).