Fork of decolua/9router focused on running 9router as a MITM proxy on a VPS (Azure, DigitalOcean, etc.).
All patches are applied directly to the source code — no runtime sed commands needed for the source build.
| # | Commit | Title |
|---|---|---|
| 1 | eea3381 |
fix(mitm): end-to-end fixes for Docker, sudo flow, health probe, and crash hardening |
| 2 | 9fcf8d7 |
fix(mitm): static multi-SAN leaf cert + collision-safe Root CA |
| 3 | 8e2954f |
fix(mitm): allow auto-restart on root-in-Docker without cached password |
Patch 2 (fix(ui): remove broken /dashboard/auth-files menu entry) was dropped — upstream fixed it natively.
Patch details
Patch 1 — MITM end-to-end fixes for Docker, sudo flow, health probe, and crash hardening
- Docker: node:22-slim Dockerfile and docker-compose.yml
- Sudo flow: fix password caching and root detection in manager.js
- Health probe: TCP
/_mitm_healthendpoint in server.js - Crash hardening: IP literals and localhost short-circuit in resolveTargetIP
- Self-loop block: respond 404 on direct IP/empty/localhost Host to stop scanner loops
- proxyFetch: fix direct https.request for non-bun runtimes
- cert/install.js: improve sudo handling
Patch 2 — Static multi-SAN leaf cert + collision-safe Root CA
- Generate one static leaf cert with subjectAltName covering all target hosts
- Collision-safe Root CA generation with unique serial numbers
- Workaround for bun SNICallback bug (retained for node:22-slim compatibility)
Patch 3 — Allow auto-restart on root-in-Docker without cached password
- Disable
if(!password&&!IS_WIN)guard when running as root in Docker - Root in a container never has a cached sudo password; guard blocked all auto-restarts
- Docker + Docker Compose on the VPS
- Ports 443 and 20128 open in the VPS firewall
git clone https://github.com/nguyenvanhuy0612/9router.git
cd 9router
docker compose up --build -dDashboard: http://<vps-ip>:20128
Uses the upstream npm package (9router@0.4.17) with patches applied at build time via sed.
git clone https://github.com/nguyenvanhuy0612/9router.git
cd 9router
docker compose -f docker-compose.release.yml up --build -dBoth options mount ./data to /app/data. The MITM root CA is generated at
data/mitm/rootCA.crt on first start — back this up; losing it means all clients
need to re-trust a new CA.
For clients to route through the VPS MITM without login failures:
Copy data/mitm/rootCA.crt from the VPS to the client machine.
Windows (certutil — machine-wide, requires admin):
certutil -addstore Root C:\path\to\rootCA.crtmacOS:
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /path/to/rootCA.crtNode.js uses its own bundled CA bundle and ignores the OS cert store. Without this, the app's Node.js process rejects the MITM cert and shows a login error at startup even though the OS cert store trusts the CA.
Windows (permanent, applies to all apps for this user):
[System.Environment]::SetEnvironmentVariable(
"NODE_EXTRA_CA_CERTS",
"C:\path\to\rootCA.crt",
"User"
)Log out and back in (or restart the app) for the change to take effect.
macOS (permanent for GUI apps via launchd):
CA_PATH="$HOME/.9router/rootCA.crt" # wherever you copied the cert
cat > ~/Library/LaunchAgents/com.9router.env.plist <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>Label</key><string>com.9router.env</string>
<key>ProgramArguments</key><array>
<string>/bin/launchctl</string><string>setenv</string>
<string>NODE_EXTRA_CA_CERTS</string><string>$CA_PATH</string>
</array>
<key>RunAtLoad</key><true/>
</dict></plist>
EOF
launchctl load ~/Library/LaunchAgents/com.9router.env.plistLog out and back in, then open the app.
# Windows: C:\Windows\System32\drivers\etc\hosts (requires admin)
# macOS/Linux: /etc/hosts
<vps-ip> daily-cloudcode-pa.googleapis.com
Replace <vps-ip> with your VPS IP address (e.g., 70.153.147.45).
Only
daily-cloudcode-pa.googleapis.com— notcloudcode-pa.googleapis.com. The non-daily domain uses certificate pinning; intercepting it breaks login.
Open http://<vps-ip>:20128, start the MITM server, click Trust cert then
Enable Antigravity DNS.
Client (Windows/macOS)
│
│ daily-cloudcode-pa.googleapis.com → <vps-ip> (hosts file)
│ cloudcode-pa.googleapis.com → Google direct (not in hosts)
│
▼
VPS :443 (9router MITM)
│
│ proxyFetch: resolves real IP via Google DNS, bypasses own /etc/hosts
│
▼
Google APIs
| Symptom | Cause | Fix |
|---|---|---|
| App shows login button | NODE_EXTRA_CA_CERTS not set |
Set env var (step 2), restart app |
| Login button after cert set | cloudcode-pa also in hosts |
Remove it; only daily-cloudcode-pa goes in hosts |
| MITM health check fails | Port 443 blocked by VPS firewall | Open port 443 inbound |
certTrusted: false in dashboard |
CA copy missing in data volume | Restart container; entrypoint copies data/mitm/rootCA.crt |
| Container crashes every ~20 min | Internet scanner hitting VPS IP | Patch 1 fixes this (self-loop 404); verify with curl -k https://<vps-ip>/ → 404 |
ECONNREFUSED in dashboard logs |
proxyFetch regression | Patch 1 fixes this (direct https.request) |
| MITM won't auto-restart | Root has no cached sudo password | Patch 3 fixes this (disables password guard for root) |