136 lines
4.0 KiB
JavaScript
136 lines
4.0 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Lists every Tor hidden-service hostname + nginx loopback URL, and checks
|
|
* that each loopback port returns HTTP (proves nginx → Next are up for that vhost).
|
|
*
|
|
* node scripts/onion-status.cjs
|
|
* npm run onions:status
|
|
*
|
|
* Reading hostname files under /var/lib/tor (one directory per service) usually
|
|
* requires sudo:
|
|
* sudo node scripts/onion-status.cjs
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
const fs = require("fs");
|
|
const http = require("http");
|
|
const path = require("path");
|
|
|
|
const REPO = path.join(__dirname, "..");
|
|
const jsonPath = path.join(__dirname, "onion-nodes.json");
|
|
const data = JSON.parse(fs.readFileSync(jsonPath, "utf8"));
|
|
|
|
/** Populated by export-onion-urls.sh — readable when /var/lib/tor is root-only. */
|
|
function readOnionFromExportFile(torDir) {
|
|
const exportPath = path.join(REPO, "onion-urls.txt");
|
|
let raw;
|
|
try {
|
|
raw = fs.readFileSync(exportPath, "utf8");
|
|
} catch {
|
|
return "";
|
|
}
|
|
const line = raw.split("\n").find((l) => l.trimStart().startsWith(torDir));
|
|
if (!line) return "";
|
|
const m = line.match(/https?:\/\/([a-z2-7]{56}\.onion)\b/i);
|
|
return m ? m[1] : "";
|
|
}
|
|
|
|
function readOnionHost(torDir) {
|
|
const f = path.join("/var/lib/tor", torDir, "hostname");
|
|
try {
|
|
return fs.readFileSync(f, "utf8").trim();
|
|
} catch (e) {
|
|
if (e.code === "EACCES" || e.code === "EPERM") {
|
|
const fromFile = readOnionFromExportFile(torDir);
|
|
if (fromFile) return `${fromFile} (from onion-urls.txt)`;
|
|
return "(hostname not readable — run: sudo bash scripts/export-onion-urls.sh, then retry)";
|
|
}
|
|
if (e.code === "ENOENT") {
|
|
const fromFile = readOnionFromExportFile(torDir);
|
|
if (fromFile) return `${fromFile} (from onion-urls.txt)`;
|
|
return "(no hostname yet — is Tor running?)";
|
|
}
|
|
return `(${e.message})`;
|
|
}
|
|
}
|
|
|
|
function onionHttpUrl(onionField) {
|
|
const m = String(onionField).match(/([a-z2-7]{56}\.onion)/i);
|
|
if (m) return `http://${m[1]}`;
|
|
return onionField;
|
|
}
|
|
|
|
function checkLocalPort(port) {
|
|
return new Promise((resolve) => {
|
|
const req = http.request(
|
|
{
|
|
hostname: "127.0.0.1",
|
|
port,
|
|
path: "/",
|
|
method: "GET",
|
|
timeout: 6000,
|
|
headers: { Connection: "close", Host: "127.0.0.1" },
|
|
},
|
|
(res) => {
|
|
res.resume();
|
|
resolve(res.statusCode || 0);
|
|
},
|
|
);
|
|
req.on("error", () => resolve(0));
|
|
req.on("timeout", () => {
|
|
req.destroy();
|
|
resolve(0);
|
|
});
|
|
req.end();
|
|
});
|
|
}
|
|
|
|
async function main() {
|
|
const rows = [];
|
|
for (const n of data.nodes) {
|
|
const onion = readOnionHost(n.torDir);
|
|
const httpCode = await checkLocalPort(n.port);
|
|
const ok = httpCode >= 200 && httpCode < 500;
|
|
rows.push({
|
|
torDir: n.torDir,
|
|
port: n.port,
|
|
kind: n.kind,
|
|
onion,
|
|
httpCode,
|
|
ok,
|
|
});
|
|
}
|
|
|
|
console.log("");
|
|
console.log("CyberLux — onion URLs (from /var/lib/tor/<service>/hostname) + loopback health");
|
|
console.log("─".repeat(100));
|
|
let bad = 0;
|
|
for (const r of rows) {
|
|
const url =
|
|
r.onion.startsWith("(") || r.onion.includes("not readable")
|
|
? r.onion
|
|
: onionHttpUrl(r.onion);
|
|
const local = `http://127.0.0.1:${r.port}/`;
|
|
const status =
|
|
r.httpCode === 0 ? "DOWN/timeout" : `HTTP ${r.httpCode}`;
|
|
if (!r.ok) bad++;
|
|
console.log(`${r.torDir.padEnd(28)} ${String(r.kind).padEnd(12)} ${String(r.port).padEnd(5)} ${status.padEnd(14)} ${url}`);
|
|
console.log(`${"".padEnd(28)} ${"".padEnd(12)} ${"".padEnd(5)} loopback: ${local}`);
|
|
console.log("");
|
|
}
|
|
console.log("─".repeat(100));
|
|
if (bad > 0) {
|
|
console.log(`✗ ${bad} loopback vhost(s) did not return OK HTTP — ensure: systemctl status nginx tor; systemctl status cyberlux`);
|
|
process.exit(1);
|
|
}
|
|
console.log("✓ All configured loopback nginx vhosts responded (Tor .onion forwarding uses these ports).");
|
|
console.log(" To test via the Tor network use Tor Browser (not curl from clearnet).");
|
|
process.exit(0);
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error(e);
|
|
process.exit(2);
|
|
});
|