mongo/jstests/ocsp/lib/mock_ocsp.js

193 lines
5.7 KiB
JavaScript

/**
* Starts a mock OCSP Server to test
* OCSP certificate revocation.
*/
import {getPython3Binary} from "jstests/libs/python.js";
import {
OCSP_CA_CERT,
OCSP_CA_KEY,
OCSP_CA_PEM,
OCSP_INTERMEDIATE_CA_ONLY_CERT,
OCSP_INTERMEDIATE_CA_ONLY_KEY,
OCSP_INTERMEDIATE_CA_WITH_ROOT_PEM,
OCSP_RESPONDER_CERT,
OCSP_RESPONDER_KEY,
} from "jstests/ocsp/lib/ocsp_helpers.js";
// These are a list of faults to match the list of faults
// in ocsp_mock.py.
export const FAULT_REVOKED = "revoked";
export const FAULT_UNKNOWN = "unknown";
export const OCSP_PROGRAM = "jstests/ocsp/lib/ocsp_mock.py";
export class ResponderCertSet {
/**
* Set of certificates for the OCSP responder.'
* @param {string} cafile
* @param {string} certfile
* @param {string} keyfile
*/
constructor(cafile, certfile, keyfile) {
this.cafile = cafile;
this.certfile = certfile;
this.keyfile = keyfile;
}
}
export const OCSP_DELEGATE_RESPONDER = new ResponderCertSet(OCSP_CA_PEM, OCSP_RESPONDER_CERT, OCSP_RESPONDER_KEY);
export const OCSP_CA_RESPONDER = new ResponderCertSet(OCSP_CA_PEM, OCSP_CA_CERT, OCSP_CA_KEY);
export const OCSP_INTERMEDIATE_RESPONDER = new ResponderCertSet(
OCSP_INTERMEDIATE_CA_WITH_ROOT_PEM,
OCSP_INTERMEDIATE_CA_ONLY_CERT,
OCSP_INTERMEDIATE_CA_ONLY_KEY,
);
export class MockOCSPServer {
/**
* Create a new OCSP Server.
*
* @param {string} fault_type
* @param {number} next_update_secs
* @param {object} responder_certificate_set
*/
constructor(
fault_type,
next_update_secs,
responder_certificate_set = OCSP_DELEGATE_RESPONDER,
response_delay_secs = 0,
include_extraneous_status = false,
issuer_hash_algorithm = "",
) {
this.python = getPython3Binary();
this.fault_type = fault_type;
this.ca_file = responder_certificate_set.cafile;
this.ocsp_cert_file = responder_certificate_set.certfile;
this.ocsp_cert_key = responder_certificate_set.keyfile;
print("Using python interpreter: " + this.python);
// The port must be hard coded to match the port of the
// responder in the certificates.
this.port = 8100;
this.next_update_secs = next_update_secs;
this.response_delay_secs = response_delay_secs;
this.include_extraneous_status = include_extraneous_status;
this.issuer_hash_algorithm = issuer_hash_algorithm;
}
start() {
print("Mock OCSP Server will listen on port: " + this.port);
let args = [
this.python,
"-u",
OCSP_PROGRAM,
"--port=" + this.port,
"--ca_file=" + this.ca_file,
"--ocsp_responder_cert=" + this.ocsp_cert_file,
"--ocsp_responder_key=" + this.ocsp_cert_key,
"--verbose",
];
if (this.fault_type) {
args.push("--fault=" + this.fault_type);
}
if (this.next_update_secs || this.next_update_secs === 0) {
args.push("--next_update_seconds=" + this.next_update_secs);
}
if (this.response_delay_secs) {
args.push("--response_delay_seconds=" + this.response_delay_secs);
}
if (this.include_extraneous_status) {
args.push("--include_extraneous_status");
}
if (this.issuer_hash_algorithm) {
args.push("--issuer_hash_algorithm=" + this.issuer_hash_algorithm);
}
const MAX_ATTEMPTS = 10;
let backoff_ms = 1000;
let portInUse = false;
for (let attempts = 0; attempts < MAX_ATTEMPTS; attempts++) {
clearRawMongoProgramOutput();
const pid = _startMongoProgram({args: args});
assert(checkProgram(pid).alive);
portInUse = false;
assert.soon(function () {
// Change this line if the OCSP endpoint changes
let output = rawMongoProgramOutput(".*");
portInUse = output.search("Address already in use") !== -1;
return portInUse || output.search("Running on http") !== -1;
});
if (!portInUse) {
this.pid = pid;
break;
}
assert.soon(function () {
return !checkProgram(pid).alive;
});
print(`Retrying OCSP mock responder startup after ${backoff_ms / 1000} seconds.`);
sleep(backoff_ms);
backoff_ms *= 2;
}
assert(!portInUse);
sleep(2000);
}
/**
* Get the URL.
*
* @return {string} url of http server
*/
getURL() {
return "http://localhost:" + this.port;
}
/**
* Stop the web server
*/
stop() {
if (!this.pid) {
print("Not stopping Mock OCSP Server, it was never started");
return;
}
print("Stopping Mock OCSP Server");
if (_isWindows()) {
// we use taskkill because we need to kill children
waitProgram(_startMongoProgram("taskkill", "/F", "/T", "/PID", this.pid));
// waitProgram to ignore error code
waitProgram(this.pid);
} else {
const kSIGINT = 2;
stopMongoProgramByPid(this.pid, kSIGINT);
for (let i = 0; i < 50; i++) {
if (!checkProgram(this.pid).alive) {
print("Mock OCSP Server stop complete");
return;
}
sleep(100);
}
// Mock could be caught in a hang, force terminate it
const kSIGKILL = 9;
stopMongoProgramByPid(this.pid, kSIGKILL);
}
print("Mock OCSP Server stop complete");
}
}