|
@@ -89,6 +89,9 @@ public class ZooKeeperServer implements SessionExpirer, ServerStats.Provider {
|
|
|
protected static final Logger LOG;
|
|
|
|
|
|
public static final String GLOBAL_OUTSTANDING_LIMIT = "zookeeper.globalOutstandingLimit";
|
|
|
+ public static final String ALLOW_SASL_FAILED_CLIENTS = "zookeeper.allowSaslFailedClients";
|
|
|
+ public static final String SESSION_REQUIRE_CLIENT_SASL_AUTH = "zookeeper.sessionRequireClientSASLAuth";
|
|
|
+ public static final String SASL_AUTH_SCHEME = "sasl";
|
|
|
|
|
|
static {
|
|
|
LOG = LoggerFactory.getLogger(ZooKeeperServer.class);
|
|
@@ -1379,23 +1382,45 @@ public class ZooKeeperServer implements SessionExpirer, ServerStats.Provider {
|
|
|
}
|
|
|
return;
|
|
|
} else if (h.getType() == OpCode.sasl) {
|
|
|
- Record rsp = processSasl(incomingBuffer,cnxn);
|
|
|
- ReplyHeader rh = new ReplyHeader(h.getXid(), 0, KeeperException.Code.OK.intValue());
|
|
|
- cnxn.sendResponse(rh,rsp, "response"); // not sure about 3rd arg..what is it?
|
|
|
- return;
|
|
|
+ processSasl(incomingBuffer,cnxn, h);
|
|
|
} else {
|
|
|
+ if (shouldRequireClientSaslAuth() && !hasCnxSASLAuthenticated(cnxn)) {
|
|
|
+ ReplyHeader replyHeader = new ReplyHeader(h.getXid(), 0,
|
|
|
+ Code.SESSIONCLOSEDREQUIRESASLAUTH.intValue());
|
|
|
+ cnxn.sendResponse(replyHeader, null, "response");
|
|
|
+ cnxn.sendCloseSession();
|
|
|
+ cnxn.disableRecv();
|
|
|
+ } else {
|
|
|
Request si = new Request(cnxn, cnxn.getSessionId(), h.getXid(),
|
|
|
- h.getType(), incomingBuffer, cnxn.getAuthInfo());
|
|
|
+ h.getType(), incomingBuffer, cnxn.getAuthInfo());
|
|
|
si.setOwner(ServerCnxn.me);
|
|
|
// Always treat packet from the client as a possible
|
|
|
// local request.
|
|
|
setLocalSessionFlag(si);
|
|
|
submitRequest(si);
|
|
|
- return;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private Record processSasl(ByteBuffer incomingBuffer, ServerCnxn cnxn) throws IOException {
|
|
|
+ private static boolean shouldAllowSaslFailedClientsConnect() {
|
|
|
+ return Boolean.getBoolean(ALLOW_SASL_FAILED_CLIENTS);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static boolean shouldRequireClientSaslAuth() {
|
|
|
+ return Boolean.getBoolean(SESSION_REQUIRE_CLIENT_SASL_AUTH);
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean hasCnxSASLAuthenticated(ServerCnxn cnxn) {
|
|
|
+ for (Id id : cnxn.getAuthInfo()) {
|
|
|
+ if (id.getScheme().equals(SASL_AUTH_SCHEME)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void processSasl(ByteBuffer incomingBuffer, ServerCnxn cnxn,
|
|
|
+ RequestHeader requestHeader) throws IOException {
|
|
|
LOG.debug("Responding to client SASL token.");
|
|
|
GetSASLRequest clientTokenRecord = new GetSASLRequest();
|
|
|
ByteBufferInputStream.byteBuffer2Record(incomingBuffer,clientTokenRecord);
|
|
@@ -1418,27 +1443,40 @@ public class ZooKeeperServer implements SessionExpirer, ServerStats.Provider {
|
|
|
cnxn.addAuthInfo(new Id("super", ""));
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- catch (SaslException e) {
|
|
|
- LOG.warn("Client failed to SASL authenticate: " + e, e);
|
|
|
- if ((System.getProperty("zookeeper.allowSaslFailedClients") != null)
|
|
|
- &&
|
|
|
- (System.getProperty("zookeeper.allowSaslFailedClients").equals("true"))) {
|
|
|
- LOG.warn("Maintaining client connection despite SASL authentication failure.");
|
|
|
+ } catch (SaslException e) {
|
|
|
+ LOG.warn("Client {} failed to SASL authenticate: {}",
|
|
|
+ cnxn.getRemoteSocketAddress(), e);
|
|
|
+ if (shouldAllowSaslFailedClientsConnect() && !shouldRequireClientSaslAuth()) {
|
|
|
+ LOG.warn("Maintaining client connection despite SASL authentication failure.");
|
|
|
} else {
|
|
|
+ int error;
|
|
|
+ if (shouldRequireClientSaslAuth()) {
|
|
|
+ LOG.warn("Closing client connection due to server requires client SASL authenticaiton," +
|
|
|
+ "but client SASL authentication has failed, or client is not configured with SASL " +
|
|
|
+ "authentication.");
|
|
|
+ error = Code.SESSIONCLOSEDREQUIRESASLAUTH.intValue();
|
|
|
+ } else {
|
|
|
LOG.warn("Closing client connection due to SASL authentication failure.");
|
|
|
- cnxn.close(ServerCnxn.DisconnectReason.SASL_AUTH_FAILURE);
|
|
|
+ error = Code.AUTHFAILED.intValue();
|
|
|
+ }
|
|
|
+
|
|
|
+ ReplyHeader replyHeader = new ReplyHeader(requestHeader.getXid(), 0, error);
|
|
|
+ cnxn.sendResponse(replyHeader, new SetSASLResponse(null), "response");
|
|
|
+ cnxn.sendCloseSession();
|
|
|
+ cnxn.disableRecv();
|
|
|
+ return;
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- catch (NullPointerException e) {
|
|
|
+ } catch (NullPointerException e) {
|
|
|
LOG.error("cnxn.saslServer is null: cnxn object did not initialize its saslServer properly.");
|
|
|
}
|
|
|
if (responseToken != null) {
|
|
|
LOG.debug("Size of server SASL response: " + responseToken.length);
|
|
|
}
|
|
|
- // wrap SASL response token to client inside a Response object.
|
|
|
- return new SetSASLResponse(responseToken);
|
|
|
+
|
|
|
+ ReplyHeader replyHeader = new ReplyHeader(requestHeader.getXid(), 0, Code.OK.intValue());
|
|
|
+ Record record = new SetSASLResponse(responseToken);
|
|
|
+ cnxn.sendResponse(replyHeader, record, "response");
|
|
|
}
|
|
|
|
|
|
// entry point for quorum/Learner.java
|