{"record":{"id":"1e143487b8077a19","repo":"decolua/9router","slug":"sudo-password-required-to-install-root-ca-certific","errorCode":null,"errorMessage":"Sudo password required to install Root CA certificate","messagePattern":"Sudo password required to install Root CA certificate","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/mitm/manager.js","lineNumber":561,"sourceCode":"      const password = sudoPassword || getCachedPassword() || await loadEncryptedPassword();\n      try { await uninstallCert(password, rootCACertPath); } catch { /* best effort */ }\n    }\n    log(\"🔐 Generating Root CA...\");\n    await generateCert();\n  }\n\n  // Step 1.5: Auto-install Root CA if not trusted yet\n  const { checkCertInstalled } = require(\"./cert/install\");\n  const rootCATrusted = await checkCertInstalled(rootCACertPath);\n  const linuxNoSystemTrust = !IS_WIN && !IS_MAC && !isSudoAvailable();\n  if (!rootCATrusted) {\n    log(\"🔐 Cert: not trusted → installing...\");\n    const password = sudoPassword || getCachedPassword() || await loadEncryptedPassword();\n    if (linuxNoSystemTrust) {\n      log(`🔐 Cert: skipping system trust (no sudo). Install ${rootCACertPath} as a trusted CA on machines that use this proxy.`);\n    } else {\n      if (!password && isSudoPasswordRequired()) {\n        throw new Error(\"Sudo password required to install Root CA certificate\");\n      }\n      try {\n        await installCert(password, rootCACertPath);\n        log(\"🔐 Cert: ✅ trusted\");\n      } catch (e) {\n        throw new Error(`Failed to trust certificate: ${e.message}`);\n      }\n    }\n  } else {\n    log(\"🔐 Cert: already trusted ✅\");\n  }\n\n  // Step 2: Spawn server (Root CA already installed in Step 1.5)\n  // Verify server.js exists — recopy if runtime file was deleted (antivirus/cleanup)\n  let effectiveServerPath = SERVER_PATH;\n  if (!effectiveServerPath || !fs.existsSync(effectiveServerPath)) {\n    log(`[MITM] server.js missing at ${effectiveServerPath} → recopying`);\n    effectiveServerPath = ensureRuntimeServer(resolveBundledServerPath());","sourceCodeStart":543,"sourceCodeEnd":579,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/mitm/manager.js#L543-L579","documentation":"During MITM startup the manager auto-installs its generated Root CA into the OS trust store when checkCertInstalled reports it untrusted. Installing a system root CA requires elevated privileges; if no sudo password was supplied (parameter, in-memory cache, or encrypted on-disk store) and isSudoPasswordRequired() says elevation is genuinely needed, startServer throws 'Sudo password required to install Root CA certificate' rather than attempting an install that would fail or hang.","triggerScenarios":"Calling startServer(apiKey) with sudoPassword undefined/empty on a machine where the CA is not yet trusted and passwordless sudo is not configured; the cached password was cleared (process restart, cache invalidated) and loadEncryptedPassword() returns null (never saved, or saved on another machine/user); first-ever MITM run after a fresh install or cert regeneration.","commonSituations":"Headless Linux server where sudo always prompts; running the dashboard as a non-root user while the CA was generated under root; an expired CA was regenerated so trust must be re-established; macOS/Windows keychain/UAC prompts requiring credentials the app doesn't have; user skipped saving an encrypted sudo password at setup.","solutions":["Pass the sudo password to the start call: startServer(apiKey, sudoPassword) or the corresponding dashboard/CLI prompt","Save the encrypted sudo password once so loadEncryptedPassword() can retrieve it on later starts","Configure passwordless sudo (NOPASSWD) for the specific cert-install command in /etc/sudoers so isSudoPasswordRequired() returns false","Run 9Router as a user that can elevate without a prompt, or manually trust the CA: install ~/.9router/mitm/rootCA.crt into the system trust store, then restart — the check will pass without sudo"],"exampleFix":"// before\nawait startServer(apiKey);\n// after\nconst sudoPassword = await promptUserForSudoPassword(); // or loadEncryptedPassword()\nawait startServer(apiKey, sudoPassword);","handlingStrategy":"validation","validationCode":"// Check whether elevation will be needed before starting\nconst { checkCertInstalled } = require('./src/mitm/cert/install');\nconst certPath = require('os').homedir() + '/.9router/mitm/rootCA.crt';\nconst needsInstall = !(await checkCertInstalled(certPath));\nconst sudoPw = loadEncryptedPassword(); // or prompt user\nif (needsInstall && !sudoPw) {\n  throw new Error('Run setup first: the Root CA is untrusted and no sudo password is saved.');\n}\nawait startServer(apiKey, sudoPw);","typeGuard":"function hasSudoCredential(p) {\n  return typeof p === 'string' && p.length > 0;\n}","tryCatchPattern":"try {\n  await startServer(apiKey, sudoPassword);\n} catch (e) {\n  if (/Sudo password required/i.test(e.message)) {\n    const pw = await promptUserForSudoPassword();\n    await startServer(apiKey, pw); // retry once with credentials\n  } else throw e;\n}","preventionTips":["Save the encrypted sudo password during initial setup so later starts are headless","Pre-trust the CA manually (security add-trusted-cert / update-ca-certificates) so the install step is skipped","Configure NOPASSWD sudo for the cert-install commands on servers","Check `checkCertInstalled()` at app startup and prompt for credentials early, not mid-start"],"tags":["certificate","sudo","permissions","tls","mitm"],"backgroundTag":"sudo-password-required","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}