fix(mitm): generate root ca on server startup (#2228)

Direct MITM server startup read rootCA.key/.crt immediately and exited
when either was missing, bypassing the manager.js CA setup path.

- generate Root CA from server.js when key/cert is missing
- make generateRootCA()/generateCert() synchronous to avoid a startup
  race before readFileSync
- add unit test covering synchronous Root CA creation

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Sutarto Jordan Chrisfivo
2026-07-01 09:30:52 +07:00
committed by decolua
parent 0b3c794075
commit 182c849979
4 changed files with 44 additions and 4 deletions

View File

@@ -7,8 +7,8 @@ const { generateRootCA, loadRootCA, generateLeafCert } = require("./rootCA");
* Generate Root CA certificate (one-time setup)
* This replaces the old static wildcard cert approach
*/
async function generateCert() {
return await generateRootCA();
function generateCert() {
return generateRootCA();
}
/**

View File

@@ -23,7 +23,7 @@ function isCertExpired(certPath) {
* Generate Root CA certificate (only once, auto-regenerate if expired)
* This Root CA will sign all dynamic leaf certificates
*/
async function generateRootCA() {
function generateRootCA() {
const exists = fs.existsSync(ROOT_CA_KEY_PATH) && fs.existsSync(ROOT_CA_CERT_PATH);
if (exists && !isCertExpired(ROOT_CA_CERT_PATH)) {
console.log("✅ Root CA already exists");

View File

@@ -9,7 +9,7 @@ const { execSync } = require("child_process");
const { log, err, dumpRequest, createResponseDumper, clearDumpDir } = require("./logger");
const { IS_DEV, LSOF_BIN, TARGET_HOSTS, URL_PATTERNS, MODEL_SYNONYMS, MODEL_PATTERNS, MODEL_NO_MAP, getToolForHost } = require("./config");
const { DATA_DIR, MITM_DIR } = require("./paths");
const { getCertForDomain } = require("./cert/generate");
const { generateCert, getCertForDomain } = require("./cert/generate");
const { getMitmAlias } = require("./dbReader");
const { applyAntigravityIdeVersionOverride } = require("./antigravityIdeVersion");
const LOCAL_PORT = 443;
@@ -57,6 +57,11 @@ function sniCallback(servername, cb) {
let sslOptions;
try {
if (!fs.existsSync(path.join(MITM_DIR, "rootCA.key")) || !fs.existsSync(path.join(MITM_DIR, "rootCA.crt"))) {
log("Root CA missing, generating...");
generateCert();
}
const rootKey = fs.readFileSync(path.join(MITM_DIR, "rootCA.key"));
const rootCert = fs.readFileSync(path.join(MITM_DIR, "rootCA.crt"));
rootCAPem = rootCert.toString("utf8");

View File

@@ -0,0 +1,35 @@
import { describe, expect, it } from "vitest";
import { createRequire } from "module";
import fs from "fs";
import os from "os";
import path from "path";
const require = createRequire(import.meta.url);
function loadRootCAWithDataDir(dataDir) {
const rootCAPath = require.resolve("../../src/mitm/cert/rootCA.js");
const pathsPath = require.resolve("../../src/mitm/paths.js");
delete require.cache[rootCAPath];
delete require.cache[pathsPath];
const oldDataDir = process.env.DATA_DIR;
process.env.DATA_DIR = dataDir;
try {
return require("../../src/mitm/cert/rootCA.js");
} finally {
if (oldDataDir === undefined) delete process.env.DATA_DIR;
else process.env.DATA_DIR = oldDataDir;
}
}
describe("MITM Root CA generation", () => {
it("creates Root CA files synchronously for direct server startup", () => {
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "9router-mitm-ca-"));
const { generateRootCA } = loadRootCAWithDataDir(dataDir);
generateRootCA();
expect(fs.existsSync(path.join(dataDir, "mitm", "rootCA.key"))).toBe(true);
expect(fs.existsSync(path.join(dataDir, "mitm", "rootCA.crt"))).toBe(true);
});
});