-
Type:
Bug
-
Resolution: Unresolved
-
Priority:
Minor - P4
-
None
-
Affects Version/s: None
-
Component/s: Security
-
None
-
ALL
-
None
-
None
-
None
-
None
-
None
-
None
-
None
Summary
When mongos (or any internal client) opens a connection to mongod using speculative SCRAM-SHA-256 authentication, it omits the options field (containing skipEmptyExchange: true) from the saslStart payload embedded in the hello handshake. This forces a third SASL round-trip (an empty saslContinue exchange) that serves no purpose beyond satisfying the SCRAM RFC formality. Drivers correctly include this option and complete authentication in two steps.
Steps to Reproduce
Start a sharded cluster with keyfile authentication, enable command verbosity on the shard mongod, and examine the Successfully authenticated log entries (id 5286306) from mongos connections. Compare with a direct mongosh connection.
mlaunch init --sharded 1 --replicaset --nodes 1 --auth --dir /tmp/test mongosh --port 27018 -u user -p password --authenticationDatabase admin --eval \ 'db.adminCommand({setParameter: 1, logComponentVerbosity: {command: {verbosity: 3}, accessControl: {verbosity: 3}}})' # restart mongos, then grep the mongod log for id 5286306
Observed Behavior
Every mongos-to-mongod connection authenticates in 3 steps (step_total: 3):
- Step 1 (in hello): speculativeAuthenticate carries a saslStart with mechanism: "SCRAM-SHA-256", payload, and db: "local" — note: no options field
- Step 2: saslContinue (client-final message) — server responds with done: false
- Step 3: saslContinue (empty payload) — server responds with done: true — unnecessary extra round-trip
Mongod logs confirm this with authentication metrics in the Successfully authenticated entry:
"metrics": { "conversation_duration": { "summary": { "0": {"step": 1, "step_total": 3, "duration_micros": 842}, "1": {"step": 2, "step_total": 3, "duration_micros": 7}, "2": {"step": 3, "step_total": 3, "duration_micros": 0} } } }
Step 3 takes 0 µs of CPU — it is a pure network round-trip with no computational work.
Expected Behavior
Authentication should complete in 2 steps (step_total: 2), as it does for mongosh and all driver connections that send skipEmptyExchange: true in the saslStart options:
- Step 1 (in hello): speculativeAuthenticate includes a saslStart with options containing skipEmptyExchange: true
- Step 2: saslContinue (client-final) — server responds with done: true
mongosh connecting directly to mongod shows this in the log:
"metrics": { "conversation_duration": { "summary": { "0": {"step": 1, "step_total": 2, "duration_micros": 2209}, "1": {"step": 2, "step_total": 2, "duration_micros": 5} } } }
Root Cause
_speculateSaslStart() in src/mongo/client/authenticate.cpp builds the speculative saslStart body without the options field:
BSONObjBuilder saslStart; saslStart.append("saslStart", 1); saslStart.append("mechanism", mechanism); saslStart.appendBinData("payload", int(payload.size()), BinDataGeneral, payload.c_str()); saslStart.append("db", authDB); // Missing: saslStart.append("options", BSON(saslCommandOptionSkipEmptyExchange << true)); helloRequestBuilder->append(kSpeculativeAuthenticate, saslStart.obj());
The non-speculative explicit saslStart path in src/mongo/client/sasl_client_authenticate_impl.cpp correctly includes it:
BSONObj saslFirstCommandPrefix =
BSON(saslStartCommandName << 1 << saslCommandMechanismFieldName << mechanismName
<< "options" << BSON(saslCommandOptionSkipEmptyExchange << true));
Without options, the server's SaslSCRAMServerMechanism::setOptions() is never called, _skipEmptyExchange stays false, and _totalSteps() returns 3 instead of 2 (src/mongo/db/auth/sasl_scram_server_conversation.h).
Fix
Add one line to _speculateSaslStart() in src/mongo/client/authenticate.cpp:
saslStart.append("options", BSON(saslCommandOptionSkipEmptyExchange << true));
Impact
This affects every mongos-to-mongod connection (and mongod-to-mongod replica set connections) using SCRAM keyfile authentication — which is the default internal auth mechanism. Each new connection in the pool pays one unnecessary network round-trip during the handshake.