decolua/9router · error · Error
Sudo password required to install Root CA certificate
Error message
Sudo password required to install Root CA certificate
What it means
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.
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
Example fix
// before await startServer(apiKey); // after const sudoPassword = await promptUserForSudoPassword(); // or loadEncryptedPassword() await startServer(apiKey, sudoPassword);
Defensive patterns
Strategy: validation
Validate before calling
// Check whether elevation will be needed before starting
const { checkCertInstalled } = require('./src/mitm/cert/install');
const certPath = require('os').homedir() + '/.9router/mitm/rootCA.crt';
const needsInstall = !(await checkCertInstalled(certPath));
const sudoPw = loadEncryptedPassword(); // or prompt user
if (needsInstall && !sudoPw) {
throw new Error('Run setup first: the Root CA is untrusted and no sudo password is saved.');
}
await startServer(apiKey, sudoPw); Type guard
function hasSudoCredential(p) {
return typeof p === 'string' && p.length > 0;
} Try / catch
try {
await startServer(apiKey, sudoPassword);
} catch (e) {
if (/Sudo password required/i.test(e.message)) {
const pw = await promptUserForSudoPassword();
await startServer(apiKey, pw); // retry once with credentials
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Failed to trust certificate
- Sudo password required to trust certificate
- Certificate install failed
- EACCES
- [mitm] http2 client error
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/1e143487b8077a19.
Report an issue: GitHub.
Appendix: source
Thrown at src/mitm/manager.js:561
const password = sudoPassword || getCachedPassword() || await loadEncryptedPassword();
try { await uninstallCert(password, rootCACertPath); } catch { /* best effort */ }
}
log("🔐 Generating Root CA...");
await generateCert();
}
// Step 1.5: Auto-install Root CA if not trusted yet
const { checkCertInstalled } = require("./cert/install");
const rootCATrusted = await checkCertInstalled(rootCACertPath);
const linuxNoSystemTrust = !IS_WIN && !IS_MAC && !isSudoAvailable();
if (!rootCATrusted) {
log("🔐 Cert: not trusted → installing...");
const password = sudoPassword || getCachedPassword() || await loadEncryptedPassword();
if (linuxNoSystemTrust) {
log(`🔐 Cert: skipping system trust (no sudo). Install ${rootCACertPath} as a trusted CA on machines that use this proxy.`);
} else {
if (!password && isSudoPasswordRequired()) {
throw new Error("Sudo password required to install Root CA certificate");
}
try {
await installCert(password, rootCACertPath);
log("🔐 Cert: ✅ trusted");
} catch (e) {
throw new Error(`Failed to trust certificate: ${e.message}`);
}
}
} else {
log("🔐 Cert: already trusted ✅");
}
// Step 2: Spawn server (Root CA already installed in Step 1.5)
// Verify server.js exists — recopy if runtime file was deleted (antivirus/cleanup)
let effectiveServerPath = SERVER_PATH;
if (!effectiveServerPath || !fs.existsSync(effectiveServerPath)) {
log(`[MITM] server.js missing at ${effectiveServerPath} → recopying`);
effectiveServerPath = ensureRuntimeServer(resolveBundledServerPath());View on GitHub (pinned to 90b52e06ff)