From a2a37b7e6c83a52630e6d9b5026753cc58cd412d Mon Sep 17 00:00:00 2001 From: almostm4 Date: Tue, 11 Nov 2025 15:11:14 +0100 Subject: [PATCH 1/2] Tor log implementation --- install.sh | 56 ++++++++++++++++++++++++++++++++++++++--- web/api/tor_log.php | 21 ++++++++++++++++ web/index.php | 61 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 web/api/tor_log.php diff --git a/install.sh b/install.sh index 6ea7377..9f0c1b4 100644 --- a/install.sh +++ b/install.sh @@ -29,6 +29,7 @@ COLLECTOR_SRC="$SCRIPT_DIR/bin/torpanel-collect.py" COLLECTOR_BIN="/usr/local/bin/torpanel-collect.py" SVC="/etc/systemd/system/torpanel-collector.service" TIMER="/etc/systemd/system/torpanel-collector.timer" +LOGDUMP_BIN="/usr/local/bin/torpanel-logdump" export DEBIAN_FRONTEND=noninteractive @@ -137,9 +138,10 @@ info "Granting www-data access to Tor cookie" usermod -aG debian-tor www-data || true ok "Permissions set" -info "Allowing www-data to control tor (limited)" -cat > "$SUDOERS_FILE" <<'SUD' +info "Allowing www-data to control tor (limited) + read logs" +cat > "$SUDOERS_FILE" < "$LOGDUMP_BIN" <<'BASH' +#!/usr/bin/env bash +set -Eeuo pipefail +LINES="${1:-500}" +LEVEL="${2:-info}" # info|notice|warning|err|debug + +case "$LINES" in ''|*[!0-9]* ) LINES=500 ;; esac +case "$LEVEL" in debug|info|notice|warning|err) ;; * ) LEVEL=info ;; esac + +unit_guess() { + if systemctl list-units --type=service | grep -q '^tor@default\.service'; then + echo 'tor@default.service' + elif systemctl list-units --type=service | grep -q '^tor\.service'; then + echo 'tor.service' + else + echo '' + fi +} +UNIT="$(unit_guess)" + +if command -v journalctl >/dev/null 2>&1; then + if [[ -n "$UNIT" ]]; then + if journalctl -u "$UNIT" -t tor -p "$LEVEL" -n "$LINES" -o short-iso --no-pager; then + exit 0 + fi + fi + if journalctl -t tor -p "$LEVEL" -n "$LINES" -o short-iso --no-pager; then + exit 0 + fi +fi + +for f in /var/log/tor/notice.log /var/log/tor/info.log /var/log/tor/log; do + if [[ -r "$f" ]]; then + tail -n "$LINES" "$f" + exit 0 + fi +done + +echo "No Tor logs found via journal (identifier 'tor') or /var/log/tor/*. Enable file logs with: + Log notice file /var/log/tor/notice.log + Log info file /var/log/tor/info.log +and reload tor." >&2 +exit 1 +BASH +chmod 0755 "$LOGDUMP_BIN" +ok "Log dumper installed" + info "Restarting services" systemctl daemon-reload systemctl enable --now tor systemctl enable "$PHP_FPM_SVC" nginx >/dev/null systemctl restart "$PHP_FPM_SVC" systemctl restart nginx -systemctl start torpanel-collector.service +systemctl start torpanel-collector.service || true systemctl enable --now torpanel-collector.timer ok "Services running" diff --git a/web/api/tor_log.php b/web/api/tor_log.php new file mode 100644 index 0000000..78284d9 --- /dev/null +++ b/web/api/tor_log.php @@ -0,0 +1,21 @@ + 5000) $n = 5000; + +$level = isset($_GET['level']) ? strtolower($_GET['level']) : 'info'; +$allowed = ['debug','info','notice','warning','err']; +if (!in_array($level, $allowed, true)) $level = 'info'; + +$cmd = sprintf('sudo /usr/local/bin/torpanel-logdump %d %s', $n, escapeshellarg($level)); +exec($cmd . ' 2>&1', $out, $rc); + +if ($rc === 0 && !empty($out)) { + echo json_encode(['ok' => true, 'lines' => $out]); +} else { + echo json_encode(['ok' => false, 'error' => 'no_log', 'rc' => $rc]); +} \ No newline at end of file diff --git a/web/index.php b/web/index.php index a12638f..35dd2d4 100644 --- a/web/index.php +++ b/web/index.php @@ -22,6 +22,8 @@ auth_require(); Tor Relay Panel
+ + Logout
@@ -198,6 +200,32 @@ auth_require(); + + \ No newline at end of file -- 2.49.1 From 570d5273090f78e29f8864157d50a531874c4d38 Mon Sep 17 00:00:00 2001 From: almostm4 Date: Tue, 11 Nov 2025 15:25:34 +0100 Subject: [PATCH 2/2] Status check improvement --- web/index.php | 75 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 22 deletions(-) diff --git a/web/index.php b/web/index.php index 35dd2d4..6cb5216 100644 --- a/web/index.php +++ b/web/index.php @@ -22,7 +22,6 @@ auth_require(); Tor Relay Panel
- Logout @@ -391,29 +390,41 @@ async function refreshChart(){ setChartData(labels, rx, tx); } -async function loadReach(){ - const r = await fetch('api/reach.php'); - const j = await r.json(); - if (!j.ok) return; - if (el.reachNick) el.reachNick.textContent = j.nickname || '—'; - if (el.reachFP) el.reachFP.textContent = j.fingerprint || '—'; - if (el.reachPort) el.reachPort.textContent = j.orport || '—'; - if (el.reachLAN) el.reachLAN.textContent = j.lan_ip || '—'; - if (el.hintPort) el.hintPort.textContent = j.orport || '—'; - if (el.hintLAN) el.hintLAN.textContent = j.lan_ip || '—'; - if (el.reachAddr) el.reachAddr.textContent = j.tor_address || '—'; - let flagStr = '—', seenStr = '—'; - if (j.onionoo && j.onionoo.found) { - flagStr = (j.onionoo.flags || []).join(', ') || '—'; - if (j.onionoo.last_seen) seenStr = 'Last seen: ' + new Date(j.onionoo.last_seen).toLocaleString(); +async function loadReach(){ + try{ + const r = await fetch('api/reach.php', {cache:'no-store'}); + const j = await r.json(); + if (!j.ok) return false; + + if (el.reachNick) el.reachNick.textContent = j.nickname || '—'; + if (el.reachFP) el.reachFP.textContent = j.fingerprint || '—'; + if (el.reachPort) el.reachPort.textContent = j.orport || '—'; + if (el.reachLAN) el.reachLAN.textContent = j.lan_ip || '—'; + if (el.hintPort) el.hintPort.textContent = j.orport || '—'; + if (el.hintLAN) el.hintLAN.textContent = j.lan_ip || '—'; + if (el.reachAddr) el.reachAddr.textContent = j.tor_address || '—'; + + let flagStr = '—', seenStr = '—'; + if (j.onionoo && j.onionoo.found) { + flagStr = (j.onionoo.flags || []).join(', ') || '—'; + if (j.onionoo.last_seen) seenStr = 'Last seen: ' + new Date(j.onionoo.last_seen).toLocaleString(); + } + const running = !!(j.onionoo && j.onionoo.running); + + if (el.reachBadge){ + el.reachBadge.textContent = running ? 'Running (publicly reachable)' : (j.onionoo && j.onionoo.found ? 'Not Running yet' : 'Not in consensus yet'); + el.reachBadge.className = 'badge ' + (running ? 'bg-success' : (j.onionoo && j.onionoo.found ? 'bg-warning' : 'bg-danger')); + el.reachBadge.title = 'Last checked: ' + new Date().toLocaleString(); + } + if (el.reachFlags) el.reachFlags.textContent = flagStr; + if (el.reachSeen) el.reachSeen.textContent = seenStr; + if (el.reachHelp) el.reachHelp.style.display = running ? 'none' : 'block'; + + return running; + }catch(e){ + return false; } - const running = !!(j.onionoo && j.onionoo.running); - if (el.reachBadge){ - el.reachBadge.textContent = running ? 'Running (publicly reachable)' : (j.onionoo && j.onionoo.found ? 'Not Running yet' : 'Not in consensus yet'); - el.reachBadge.className = 'badge ' + (running ? 'bg-success' : (j.onionoo && j.onionoo.found ? 'bg-warning' : 'bg-danger')); - } - if (el.reachHelp) el.reachHelp.style.display = running ? 'none' : 'block'; } if (el.btnEditCfg){ @@ -454,6 +465,26 @@ if (el.cfgForm){ }); } +if (el.reachRefresh){ + el.reachRefresh.addEventListener('click', async ()=>{ + const btn = el.reachRefresh; + const originalClasses = btn.className; + btn.disabled = true; + btn.innerHTML = 'Checking…'; + + const running = await loadReach(); + + btn.className = running ? 'btn btn-sm btn-success' : 'btn btn-sm btn-danger'; + btn.innerHTML = running ? 'Reachable ✓' : 'Not reachable ✗'; + + setTimeout(()=>{ + btn.className = originalClasses; + btn.innerHTML = 'Check status'; + btn.disabled = false; + }, 1500); + }); +} + (function initTheme(){ const t = preferredTheme(); document.documentElement.setAttribute('data-theme', t); -- 2.49.1