Fix : MITM

This commit is contained in:
decolua
2026-03-14 23:59:22 +07:00
parent f264bb9a23
commit 1dd5d60724
10 changed files with 151 additions and 65 deletions
+48 -19
View File
@@ -11,6 +11,7 @@ const IS_WIN = process.platform === "win32";
const { generateCert } = require("./cert/generate");
const { installCert } = require("./cert/install");
const { MITM_DIR } = require("./paths");
const { log, err } = require("./logger");
const MITM_PORT = 443;
const MITM_WIN_NODE_PORT = 8443;
@@ -140,7 +141,7 @@ async function saveMitmSettings(enabled, password) {
if (password) updates.mitmSudoEncrypted = encryptPassword(password);
await _updateSettings(updates);
} catch (e) {
console.log("[MITM] Failed to save settings:", e.message);
err(`Failed to save settings: ${e.message}`);
}
}
@@ -277,8 +278,10 @@ async function getMitmStatus() {
const dnsStatus = checkAllDNSStatus();
const rootCACertPath = path.join(MITM_DIR, "rootCA.crt");
const certExists = fs.existsSync(rootCACertPath);
const { checkCertInstalled } = require("./cert/install");
const certTrusted = certExists ? await checkCertInstalled(rootCACertPath) : false;
return { running, pid, certExists, dnsStatus };
return { running, pid, certExists, certTrusted, dnsStatus };
}
async function scheduleMitmRestart(apiKey) {
@@ -288,7 +291,7 @@ async function scheduleMitmRestart(apiKey) {
if (aliveMs >= MITM_RESTART_RESET_MS) mitmRestartCount = 0;
if (mitmRestartCount >= MITM_MAX_RESTARTS) {
console.error("[MITM] Max restart attempts reached. Giving up.");
err("Max restart attempts reached. Giving up.");
return;
}
@@ -297,28 +300,28 @@ async function scheduleMitmRestart(apiKey) {
mitmRestartCount++;
mitmIsRestarting = true;
console.log(`[MITM] Restarting in ${delay / 1000}s... (${mitmRestartCount}/${MITM_MAX_RESTARTS})`);
log(`Restarting in ${delay / 1000}s... (${mitmRestartCount}/${MITM_MAX_RESTARTS})`);
await new Promise((r) => setTimeout(r, delay));
try {
const settings = _getSettings ? await _getSettings() : null;
if (settings && !settings.mitmEnabled) {
console.log("[MITM] MITM disabled, skipping restart");
log("MITM disabled, skipping restart");
mitmIsRestarting = false;
return;
}
const password = getCachedPassword() || await loadEncryptedPassword();
if (!password && !IS_WIN) {
console.error("[MITM] No cached password, cannot auto-restart");
err("No cached password, cannot auto-restart");
mitmIsRestarting = false;
return;
}
await startServer(apiKey, password);
console.log("[MITM] Restarted successfully");
log("🔄 Restarted successfully");
mitmRestartCount = 0;
mitmIsRestarting = false;
} catch (err) {
console.error(`[MITM] Restart attempt ${mitmRestartCount}/${MITM_MAX_RESTARTS} failed:`, err.message);
} catch (e) {
err(`Restart attempt ${mitmRestartCount}/${MITM_MAX_RESTARTS} failed: ${e.message}`);
mitmIsRestarting = false;
// Schedule next retry
scheduleMitmRestart(apiKey);
@@ -335,7 +338,7 @@ async function startServer(apiKey, sudoPassword) {
const savedPid = parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10);
if (savedPid && isProcessAlive(savedPid)) {
serverPid = savedPid;
console.log(`[MITM] Reusing existing process PID ${savedPid}`);
log(`♻️ Reusing existing process (PID: ${savedPid})`);
await saveMitmSettings(true, sudoPassword);
if (sudoPassword) setCachedPassword(sudoPassword);
return { running: true, pid: savedPid };
@@ -357,7 +360,7 @@ async function startServer(apiKey, sudoPassword) {
if (portStatus === "in-use" || portStatus === "no-permission") {
const owner = await getPort443Owner(sudoPassword);
if (owner && owner.name === "node") {
console.log(`[MITM] Killing orphan node process on port 443 (PID ${owner.pid})...`);
log(`Killing orphan node process on port 443 (PID ${owner.pid})...`);
try {
const { execWithPassword } = require("./dns/dnsConfig");
await execWithPassword(`kill -9 ${owner.pid}`, sudoPassword);
@@ -377,7 +380,7 @@ async function startServer(apiKey, sudoPassword) {
const rootCAKeyPath = path.join(MITM_DIR, "rootCA.key");
if (!fs.existsSync(rootCACertPath) || !fs.existsSync(rootCAKeyPath)) {
console.log("[MITM] Generating Root CA certificate (first time or migration)...");
log("🔐 Generating Root CA (first time)...");
await generateCert();
}
@@ -385,17 +388,20 @@ async function startServer(apiKey, sudoPassword) {
const { checkCertInstalled } = require("./cert/install");
const rootCATrusted = await checkCertInstalled(rootCACertPath);
if (!rootCATrusted) {
console.log("[MITM] Installing Root CA to system trust store...");
log("🔐 Cert: not trusted → installing...");
// Use provided password or cached/stored password
const password = sudoPassword || getCachedPassword() || await loadEncryptedPassword();
if (!password && !IS_WIN) {
throw new Error("Sudo password required to install Root CA certificate");
}
await installCert(password, rootCACertPath);
console.log("✅ Root CA installed successfully");
log("🔐 Cert: ✅ trusted");
} else {
log("🔐 Cert: already trusted ✅");
}
// Step 2: Spawn server (Root CA already installed in Step 1.5)
log("🚀 Starting server...");
if (IS_WIN) {
const psSQ = (s) => s.replace(/'/g, "''");
const nodePs = psSQ(process.execPath);
@@ -436,17 +442,18 @@ async function startServer(apiKey, sudoPassword) {
let startError = null;
if (!IS_WIN) {
serverProcess.stdout.on("data", (data) => {
console.log(`[MITM Server] ${data.toString().trim()}`);
// server.js already formats its own logs — print as-is
process.stdout.write(data);
});
serverProcess.stderr.on("data", (data) => {
const msg = data.toString().trim();
if (msg && !msg.includes("Password:") && !msg.includes("password for")) {
console.error(`[MITM Server Error] ${msg}`);
err(msg);
startError = msg;
}
});
serverProcess.on("exit", (code) => {
console.log(`MITM server exited with code ${code}`);
log(`Server exited (code: ${code})`);
serverProcess = null;
serverPid = null;
try { fs.unlinkSync(PID_FILE); } catch { /* ignore */ }
@@ -470,6 +477,14 @@ async function startServer(apiKey, sudoPassword) {
fs.writeFileSync(PID_FILE, String(serverPid));
}
log(`✅ Server healthy (PID: ${serverPid || health.pid})`);
// Log DNS status per tool
const dnsStatus = checkAllDNSStatus();
for (const [tool, active] of Object.entries(dnsStatus)) {
log(`🌐 DNS ${tool}: ${active ? "✅ active" : "❌ inactive"}`);
}
await saveMitmSettings(true, sudoPassword);
if (sudoPassword) setCachedPassword(sudoPassword);
@@ -483,7 +498,7 @@ async function stopServer(sudoPassword) {
// Prevent auto-restart from triggering on intentional stop
mitmIsRestarting = true;
mitmRestartCount = 0;
console.log("[MITM] Stopping server...");
log(" Stopping server...");
// Kill server process
const proc = serverProcess;
@@ -492,7 +507,7 @@ async function stopServer(sudoPassword) {
: (() => { try { return parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10); } catch { return null; } })();
if (pidToKill && isProcessAlive(pidToKill)) {
console.log(`Killing MITM server (PID: ${pidToKill})...`);
log(`Killing server (PID: ${pidToKill})...`);
killProcess(pidToKill, false, sudoPassword);
await new Promise(r => setTimeout(r, 1000));
if (isProcessAlive(pidToKill)) killProcess(pidToKill, true, sudoPassword);
@@ -562,6 +577,19 @@ async function disableToolDNS(tool, sudoPassword) {
return { success: true };
}
/**
* Install Root CA to system trust store (standalone, no server start)
*/
async function trustCert(sudoPassword) {
const rootCACertPath = path.join(MITM_DIR, "rootCA.crt");
if (!fs.existsSync(rootCACertPath)) throw new Error("Root CA not found. Start server first to generate it.");
const { installCert } = require("./cert/install");
const password = sudoPassword || getCachedPassword() || await loadEncryptedPassword();
if (!password && !IS_WIN) throw new Error("Sudo password required to trust certificate");
await installCert(password, rootCACertPath);
if (password) setCachedPassword(password);
}
// Legacy aliases for backward compatibility
const startMitm = startServer;
const stopMitm = stopServer;
@@ -572,6 +600,7 @@ module.exports = {
stopServer,
enableToolDNS,
disableToolDNS,
trustCert,
// Legacy
startMitm,
stopMitm,