/** * 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"); } }