summaryrefslogtreecommitdiff
path: root/jstests/sslSpecial/cluster_auth_x509_rolling_configuration.js
blob: e51896587f84941a9a31f28d15fd21c87e0da767 (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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
/**
 * This test does full rollovers of the X509 auth for cluster membership using configurable
 * attributes and extensions. It ensures that it is possible to change the attributes and/or
 * extensions used to determine cluster membership while rotating cluster certificates.
 *
 * @tags: [requires_persistence, requires_replication, requires_fcv_70]
 */

(function() {
'use strict';

load('jstests/ssl/libs/ssl_helpers.js');

if (determineSSLProvider() !== "openssl") {
    print('Skipping test, tlsClusterAuthX509 options are only available with OpenSSL');
    return;
}

/**
 * This is the path of the original certificate and the CA cert used in all configurations.
 */
// Subject: C=US, ST=New York, L=New York, O=MongoDB, OU=Kernel, CN=server
const originalDNAttributes = "O=MongoDB, OU=Kernel";
const originalCert = 'jstests/libs/server.pem';
const originalCACert = 'jstests/libs/ca.pem';
const defaultPolicyClusterAuthX509Override = {
    attributes: originalDNAttributes,
};
/**
 * This is the tlsClusterAuthX509Attributes and path of the certificate which is rolled over to
 * after switching to custom attributes.
 */
// Subject: C=US, ST=New York, L=New York City, CN=server, title=foo
const fooTitleDNAttributes = "C=US, ST=New York, L=New York City, title=foo";
const fooTitleDNCert = 'jstests/libs/server_title_foo_no_o_ou_dc.pem';
const fooTitleClusterAuthX509Override = {
    attributes: fooTitleDNAttributes,
};
/**
 * This is the DN of the certificate which is rolled over to after switching the custom attribute
 * value.
 */
// Subject: C=US, ST=New York, L=New York City, O=MongoDB, OU=Kernel, CN=server, title=bar
const barTitleDNAttributes = "C=US, ST=New York, L=New York City, title=bar";
const barTitleDNCert = 'jstests/libs/server_title_bar.pem';
const barTitleClusterAuthX509Override = {
    attributes: barTitleDNAttributes,
};
/**
 * This is the path of the certificate containing the cluster membership extension set to 'foo'.
 */
const fooExtensionCert = 'jstests/ssl/libs/cluster-member-foo.pem';
const fooExtensionClusterAuthX509Override = {
    extensionValue: 'foo',
};
/**
 * This is the path of the certificate containing the cluster membership extension set to 'bar'.
 */
const barExtensionCert = 'jstests/ssl/libs/cluster-member-bar.pem';
const barExtensionClusterAuthX509Override = {
    extensionValue: 'bar',
};

const rst = new ReplSetTest({
    nodes: 3,
    waitForKeys: false,
    nodeOptions: {
        tlsMode: "preferTLS",
        clusterAuthMode: "x509",
        tlsCertificateKeyFile: originalCert,
        tlsCAFile: originalCACert,
        tlsAllowInvalidHostnames: "",
    }
});
rst.startSet();

rst.initiateWithAnyNodeAsPrimary(
    Object.extend(rst.getReplSetConfig(), {writeConcernMajorityJournalDefault: true}));

// Create a user to login as when auth is enabled later
rst.getPrimary().getDB('admin').createUser({user: 'root', pwd: 'root', roles: ['root']}, {w: 3});
rst.nodes.forEach((node) => {
    assert(node.getDB("admin").auth("root", "root"));
});

// Future connections should authenticate immediately on connecting so that replSet actions succeed.
const originalAwaitConnection = MongoRunner.awaitConnection;
MongoRunner.awaitConnection = function(args) {
    const conn = originalAwaitConnection(args);
    assert(conn.getDB('admin').auth('root', 'root'));
    return conn;
};

// This will rollover the cluster to a new config in a rolling fashion. It will return when
// there is a primary and we are able to write to it.
function rolloverConfig(newConfig) {
    function restart(node) {
        const nodeId = rst.getNodeId(node);
        rst.stop(nodeId);
        const configId = "n" + nodeId;
        rst.nodeOptions[configId] = newConfig;
        rst.start(nodeId, {remember: false}, true, true);
        rst.awaitSecondaryNodes();
    }

    rst.nodes.forEach(function(node) {
        restart(node);
    });

    assert.soon(() => {
        let primary = rst.getPrimary();
        assert.commandWorked(primary.getDB("admin").runCommand({hello: 1}));
        assert.commandWorked(primary.getDB('test').a.insert({a: 1, str: 'TESTTESTTEST'}));

        return true;
    });
}

// Scenario 1: From no tlsClusterAuthX509 to tlsClusterAuthX509Attributes.
jsTestLog("Transitioning from DC/O/OU only to DC/O/OU and custom subject DN attributes");
rolloverConfig({
    tlsMode: "preferTLS",
    clusterAuthMode: "x509",
    tlsCertificateKeyFile: originalCert,
    tlsCAFile: originalCACert,
    tlsAllowInvalidHostnames: "",
    tlsClusterAuthX509Attributes: fooTitleDNAttributes,
    setParameter: {
        tlsClusterAuthX509Override: tojson(defaultPolicyClusterAuthX509Override),
    },
});

jsTestLog("Rotating cluster member certificates to use subject DNs with custom attributes");
rolloverConfig({
    tlsMode: "preferTLS",
    clusterAuthMode: "x509",
    tlsCertificateKeyFile: fooTitleDNCert,
    tlsCAFile: originalCACert,
    tlsAllowInvalidHostnames: "",
    tlsClusterAuthX509Attributes: fooTitleDNAttributes,
    setParameter: {
        tlsClusterAuthX509Override: tojson(defaultPolicyClusterAuthX509Override),
    },
});

jsTestLog("Removing DC/O/OU policy from override");
rolloverConfig({
    tlsMode: "preferTLS",
    clusterAuthMode: "x509",
    tlsCertificateKeyFile: fooTitleDNCert,
    tlsCAFile: originalCACert,
    tlsAllowInvalidHostnames: "",
    tlsClusterAuthX509Attributes: fooTitleDNAttributes,
});

jsTestLog(
    "SUCCESS - certificate rotation from DC/O/OU certs to custom subject DN attribute-based certs");

// Scenario 2: From tlsClusterAuthX509Attributes: 'title=foo' to tlsClusterAuthX509Attributes:
// 'title=bar'
jsTestLog(
    "Transitioning from custom subject DN attributes with 'title=foo' to custom subject DN attributes with 'title=bar'");
rolloverConfig({
    tlsMode: "preferTLS",
    clusterAuthMode: "x509",
    tlsCertificateKeyFile: fooTitleDNCert,
    tlsCAFile: originalCACert,
    tlsAllowInvalidHostnames: "",
    tlsClusterAuthX509Attributes: barTitleDNAttributes,
    setParameter: {
        tlsClusterAuthX509Override: tojson(fooTitleClusterAuthX509Override),
    },
});

jsTestLog("Rotating cluster member certificates to use certificates with 'title=bar'");
rolloverConfig({
    tlsMode: "preferTLS",
    clusterAuthMode: "x509",
    tlsCertificateKeyFile: barTitleDNCert,
    tlsCAFile: originalCACert,
    tlsAllowInvalidHostnames: "",
    tlsClusterAuthX509Attributes: barTitleDNAttributes,
    setParameter: {
        tlsClusterAuthX509Override: tojson(fooTitleClusterAuthX509Override),
    },
});

jsTestLog("Removing 'title=foo' attribute from override");
rolloverConfig({
    tlsMode: "preferTLS",
    clusterAuthMode: "x509",
    tlsCertificateKeyFile: barTitleDNCert,
    tlsCAFile: originalCACert,
    tlsAllowInvalidHostnames: "",
    tlsClusterAuthX509Attributes: barTitleDNAttributes,
});

jsTestLog("SUCCESS - certificate rotation from one set of custom subject DN attributes to another");

// Scenario 3: From tlsClusterAuthX509Attributes to tlsClusterAuthX509ExtensionValue.
jsTestLog("Transitioning from custom attributes to certificate extension");
rolloverConfig({
    tlsMode: "preferTLS",
    clusterAuthMode: "x509",
    tlsCertificateKeyFile: barTitleDNCert,
    tlsCAFile: originalCACert,
    tlsAllowInvalidHostnames: "",
    tlsClusterAuthX509ExtensionValue: 'foo',
    setParameter: {
        tlsClusterAuthX509Override: tojson(barTitleClusterAuthX509Override),
    },
});

jsTestLog("Rotating cluster member certificates to use certificates with extension set to 'foo'");
rolloverConfig({
    tlsMode: "preferTLS",
    clusterAuthMode: "x509",
    tlsCertificateKeyFile: fooExtensionCert,
    tlsCAFile: originalCACert,
    tlsAllowInvalidHostnames: "",
    tlsClusterAuthX509ExtensionValue: 'foo',
    setParameter: {
        tlsClusterAuthX509Override: tojson(barTitleClusterAuthX509Override),
    },
});

jsTestLog("Removing 'title=bar' attribute from override");
rolloverConfig({
    tlsMode: "preferTLS",
    clusterAuthMode: "x509",
    tlsCertificateKeyFile: fooExtensionCert,
    tlsCAFile: originalCACert,
    tlsAllowInvalidHostnames: "",
    tlsClusterAuthX509ExtensionValue: 'foo',
});

jsTestLog(
    "SUCCESS - certificate rotation from custom subject DN attributes to custom extension value");

// Scenario 4: From tlsClusterAuthX509ExtensionValue: 'foo' to tlsClusterAuthX509ExtensionValue:
// 'bar'
jsTestLog("Transitioning from one custom extension value to another");
rolloverConfig({
    tlsMode: "preferTLS",
    clusterAuthMode: "x509",
    tlsCertificateKeyFile: fooExtensionCert,
    tlsCAFile: originalCACert,
    tlsAllowInvalidHostnames: "",
    tlsClusterAuthX509ExtensionValue: 'bar',
    setParameter: {
        tlsClusterAuthX509Override: tojson(fooExtensionClusterAuthX509Override),
    },
});

jsTestLog("Rotating cluster member certificates to use certificates with extension set to 'bar'");
rolloverConfig({
    tlsMode: "preferTLS",
    clusterAuthMode: "x509",
    tlsCertificateKeyFile: barExtensionCert,
    tlsCAFile: originalCACert,
    tlsAllowInvalidHostnames: "",
    tlsClusterAuthX509ExtensionValue: 'bar',
    setParameter: {
        tlsClusterAuthX509Override: tojson(fooExtensionClusterAuthX509Override),
    },
});

jsTestLog("Removing 'foo' extension from override");
rolloverConfig({
    tlsMode: "preferTLS",
    clusterAuthMode: "x509",
    tlsCertificateKeyFile: barExtensionCert,
    tlsCAFile: originalCACert,
    tlsAllowInvalidHostnames: "",
    tlsClusterAuthX509ExtensionValue: 'bar',
});

jsTestLog("SUCCESS - certificate rotation from one custom extension value to another");

// Scenario 5: From tlsClusterAuthExtensionValue back to default.
jsTestLog("Transitioning custom extension value to custom subject DN attributes");
rolloverConfig({
    tlsMode: "preferTLS",
    clusterAuthMode: "x509",
    tlsCertificateKeyFile: fooExtensionCert,
    tlsCAFile: originalCACert,
    tlsAllowInvalidHostnames: "",
    setParameter: {
        tlsClusterAuthX509Override: tojson(barExtensionClusterAuthX509Override),
    },
});

jsTestLog("Rotating cluster member certificates to use certificates with DC/O/OU");
rolloverConfig({
    tlsMode: "preferTLS",
    clusterAuthMode: "x509",
    tlsCertificateKeyFile: originalCert,
    tlsCAFile: originalCACert,
    tlsAllowInvalidHostnames: "",
    setParameter: {
        tlsClusterAuthX509Override: tojson(barExtensionClusterAuthX509Override),
    },
});

// This works because the original cert meets the default DC/O/OU criteria.
jsTestLog("Removing 'bar' extension from override and the custom attributes");
rolloverConfig({
    tlsMode: "preferTLS",
    clusterAuthMode: "x509",
    tlsCertificateKeyFile: originalCert,
    tlsCAFile: originalCACert,
    tlsAllowInvalidHostnames: "",
});

jsTestLog("SUCCESS - certificate rotation from extension value back to default");

rst.stopSet();
})();