1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
/**
* Tests that mongod does not gossip cluster time metadata and operation time until at least one key
* is created on the
* config server.
*
* This test restarts shard replica sets, so it requires a persistent storage engine.
* @tags: [requires_persistence]
*/
(function() {
"use strict";
// This test uses authentication and runs commands without authenticating, which is not
// compatible with implicit sessions.
TestData.disableImplicitSessions = true;
load("jstests/multiVersion/libs/multi_rs.js");
// TODO SERVER-32672: remove this flag.
TestData.skipGossipingClusterTime = true;
const keyFile = 'jstests/libs/key1';
const adminUser = {
db: "admin",
username: "foo",
password: "bar"
};
const rUser = {
db: "test",
username: "r",
password: "bar"
};
const timeoutValidLogicalTimeMs = 20 * 1000;
function assertContainsValidLogicalTime(adminConn) {
try {
const res = adminConn.runCommand({isMaster: 1});
assert.hasFields(res, ["$clusterTime"]);
assert.hasFields(res.$clusterTime, ["signature", "clusterTime"]);
// clusterTime must be greater than the uninitialzed value.
assert.hasFields(res.$clusterTime.signature, ["hash", "keyId"]);
// The signature must have been signed by a key with a valid generation.
assert(res.$clusterTime.signature.keyId > NumberLong(0));
assert.hasFields(res, ["operationTime"]);
assert(Object.prototype.toString.call(res.operationTime) === "[object Timestamp]",
"operationTime must be a timestamp");
return true;
} catch (error) {
return false;
}
}
let st = new ShardingTest({shards: {rs0: {nodes: 2}}, other: {keyFile: keyFile}});
jsTestLog("Started ShardingTest");
var adminDB = st.s.getDB("admin");
adminDB.createUser({user: adminUser.username, pwd: adminUser.password, roles: ["__system"]});
adminDB.auth(adminUser.username, adminUser.password);
assert(st.s.getDB("admin").system.keys.count() >= 2);
let priRSConn = st.rs0.getPrimary().getDB("admin");
priRSConn.createUser({user: rUser.username, pwd: rUser.password, roles: ["root"]});
priRSConn.auth(rUser.username, rUser.password);
// use assert.soon since it's possible the shard primary may not have refreshed
// and loaded the keys into its KeyManager cache.
assert.soon(function() {
return assertContainsValidLogicalTime(priRSConn);
}, "Failed to assert valid logical time", timeoutValidLogicalTimeMs);
priRSConn.logout();
// Enable the failpoint, remove all keys, and restart the config servers with the failpoint
// still enabled to guarantee there are no keys.
for (let i = 0; i < st.configRS.nodes.length; i++) {
assert.commandWorked(st.configRS.nodes[i].adminCommand(
{"configureFailPoint": "disableKeyGeneration", "mode": "alwaysOn"}));
}
var priCSConn = st.configRS.getPrimary();
authutil.asCluster(priCSConn, keyFile, function() {
priCSConn.getDB("admin").system.keys.remove({purpose: "HMAC"});
});
assert(adminDB.system.keys.count() == 0, "expected there to be no keys on the config server");
adminDB.logout();
st.configRS.stopSet(null /* signal */, true /* forRestart */);
st.configRS.startSet(
{restart: true, setParameter: {"failpoint.disableKeyGeneration": "{'mode':'alwaysOn'}"}});
// bounce rs0 to clean the key cache
st.rs0.stopSet(null /* signal */, true /* forRestart */);
st.rs0.startSet({restart: true});
priRSConn = st.rs0.getPrimary().getDB("admin");
priRSConn.auth(rUser.username, rUser.password);
const resNoKeys = assert.commandWorked(priRSConn.runCommand({isMaster: 1}));
priRSConn.logout();
assert.eq(resNoKeys.hasOwnProperty("$clusterTime"), false);
assert.eq(resNoKeys.hasOwnProperty("operationTime"), false);
st.stop();
})();
|