summaryrefslogtreecommitdiff
path: root/jstests/sharding/key_rotation.js
blob: 7067efa1cd5a2785778050e5051a2a2af19209cc (plain)
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
/**
 * Tests for causal consistency key rotation. In particular, tests:
 * - that a sharded cluster with no keys inserts new keys after startup.
 * - responses from servers in a sharded cluster contain a cluster time object with a signature.
 * - manual key rotation is possible by deleting existing keys and restarting the cluster.
 *
 * Manual key rotation requires restarting a shard, so a persistent storage engine is necessary.
 * @tags: [requires_persistence]
 */

// This test restarts a shard replica set, potentially changing the primary node, while
// ShardingTest._connections remains stale with the old primary/secondaries information. The UUIDs
// check does a primary only command against the shards using _connections and can fail.
TestData.skipCheckingUUIDsConsistentAcrossCluster = true;

(function() {
    "use strict";

    let st = new ShardingTest({shards: {rs0: {nodes: 2}}});

    // Verify after startup there is a new key in admin.system.keys.
    jsTestLog("Verify the admin.system.keys collection after startup.");

    let startupKeys = st.s.getDB("admin").system.keys.find();
    assert(startupKeys.count() >= 2);  // Should be at least two generations of keys available.
    startupKeys.toArray().forEach(function(key, i) {
        assert.hasFields(
            key,
            ["purpose", "key", "expiresAt"],
            "key document " + i + ": " + tojson(key) + ", did not have all of the expected fields");
    });

    // Verify there is a $clusterTime with a signature in the response.
    jsTestLog("Verify a signature is included in the cluster time in a response.");

    let res = assert.commandWorked(st.s.getDB("test").runCommand({isMaster: 1}));
    assert.hasFields(res, ["$clusterTime"]);
    assert.hasFields(res.$clusterTime, ["signature"]);
    assert.hasFields(res.$clusterTime.signature, ["hash", "keyId"]);

    // Verify manual key rotation.
    jsTestLog("Verify manual key rotation.");

    // Pause key generation on the config server primary.
    for (let i = 0; i < st.configRS.nodes.length; i++) {
        st.configRS.nodes[i].adminCommand(
            {configureFailPoint: "disableKeyGeneration", mode: "alwaysOn"});
    }

    // Delete all existing keys.
    res = st.configRS.getPrimary().getDB("admin").system.keys.remove({purpose: "HMAC"});
    assert(res.nRemoved >= 2);
    assert(st.s.getDB("admin").system.keys.find().count() == 0);

    // Restart the config servers, so they will create new keys once the failpoint is disabled.
    st.configRS.stopSet(null /* signal */, true /* forRestart */);
    st.configRS.startSet(
        {restart: true, setParameter: {"failpoint.disableKeyGeneration": "{'mode':'alwaysOn'}"}});

    // Limit the max time between refreshes on the config server, so new keys are created quickly.
    st.configRS.getPrimary().adminCommand({
        "configureFailPoint": "maxKeyRefreshWaitTimeOverrideMS",
        "mode": "alwaysOn",
        "data": {"overrideMS": 1000}
    });

    // Kill and restart all shards and mongos processes so they have no keys in memory.
    st.rs0.stopSet(null /* signal */, true /* forRestart */);
    st.rs0.startSet({restart: true});

    // The shard primary should return a dummy signed cluster time, because there are no keys.
    res = assert.commandWorked(st.rs0.getPrimary().getDB("test").runCommand({isMaster: 1}));
    assert.hasFields(res, ["$clusterTime", "operationTime"]);
    assert.eq(res.$clusterTime.signature.keyId, NumberLong(0));

    // Resume key generation.
    for (let i = 0; i < st.configRS.nodes.length; i++) {
        st.configRS.getPrimary().adminCommand(
            {configureFailPoint: "disableKeyGeneration", mode: "off"});
    }

    st.restartMongos(0);

    // Wait for config server primary to create new keys.
    assert.soonNoExcept(function() {
        let keys = st.s.getDB("admin").system.keys.find();
        assert(keys.count() >= 2);
        return true;
    }, "expected the config server primary to create new keys");

    st.stop();
})();