mirror of
https://github.com/r3bo0tbx1/tor-guard-relay.git
synced 2026-04-06 00:32:04 +02:00
feat: v1.1 repository restructuring
Reorganize into professional directory structure: - Add 6 comprehensive documentation files - Update Dependabot configuration - Update README for better navigation - Create docs/, templates/, tools/, .github/ structure See RESTRUCTURING-SUMMARY.md for complete details. BREAKING CHANGES: None - fully backward compatible
This commit is contained in:
534
tools/dashboard
Normal file
534
tools/dashboard
Normal file
@@ -0,0 +1,534 @@
|
||||
#!/bin/sh
|
||||
# dashboard - Web-based relay monitoring dashboard
|
||||
# Usage: dashboard [--port PORT] [--help]
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
VERSION="1.1.0"
|
||||
DASHBOARD_PORT="${DASHBOARD_PORT:-8080}"
|
||||
DASHBOARD_BIND="${DASHBOARD_BIND:-0.0.0.0}"
|
||||
ENABLE_DASHBOARD="${ENABLE_DASHBOARD:-true}"
|
||||
REFRESH_INTERVAL="${REFRESH_INTERVAL:-10}"
|
||||
MULTI_RELAY="${MULTI_RELAY:-false}"
|
||||
|
||||
# Parse arguments
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--help|-h)
|
||||
cat << EOF
|
||||
🎨 Tor-Guard-Relay Web Dashboard v${VERSION}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
USAGE:
|
||||
dashboard [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--port PORT Dashboard port (default: 8080)
|
||||
--bind ADDR Bind address (default: 0.0.0.0)
|
||||
--refresh SEC Auto-refresh interval (default: 10)
|
||||
--multi Enable multi-relay support
|
||||
--help, -h Show this help message
|
||||
|
||||
ENVIRONMENT VARIABLES:
|
||||
DASHBOARD_PORT Port to listen on
|
||||
DASHBOARD_BIND Address to bind
|
||||
ENABLE_DASHBOARD Enable dashboard (true/false)
|
||||
REFRESH_INTERVAL Auto-refresh in seconds
|
||||
MULTI_RELAY Multi-relay mode (true/false)
|
||||
|
||||
FEATURES:
|
||||
• Real-time relay status monitoring
|
||||
• Bootstrap progress visualization
|
||||
• Network diagnostics display
|
||||
• Performance metrics graphs
|
||||
• Error/warning alerts
|
||||
• Multi-relay management (optional)
|
||||
• Mobile-responsive design
|
||||
|
||||
ENDPOINTS:
|
||||
http://localhost:8080/ Main dashboard
|
||||
http://localhost:8080/api/status JSON API
|
||||
http://localhost:8080/api/metrics Metrics API
|
||||
http://localhost:8080/api/logs Recent logs
|
||||
|
||||
DOCKER INTEGRATION:
|
||||
ports:
|
||||
- "8080:8080"
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
--port)
|
||||
shift
|
||||
DASHBOARD_PORT="$1"
|
||||
shift
|
||||
;;
|
||||
--bind)
|
||||
shift
|
||||
DASHBOARD_BIND="$1"
|
||||
shift
|
||||
;;
|
||||
--refresh)
|
||||
shift
|
||||
REFRESH_INTERVAL="$1"
|
||||
shift
|
||||
;;
|
||||
--multi)
|
||||
MULTI_RELAY="true"
|
||||
;;
|
||||
-*)
|
||||
echo "❌ Unknown option: $arg"
|
||||
echo "💡 Use --help for usage information"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Check if dashboard is enabled
|
||||
if [ "$ENABLE_DASHBOARD" != "true" ]; then
|
||||
echo "🎨 Dashboard is disabled"
|
||||
echo "💡 Set ENABLE_DASHBOARD=true to enable"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check for netcat
|
||||
if ! command -v nc > /dev/null 2>&1; then
|
||||
echo "❌ Error: netcat (nc) is required"
|
||||
echo "💡 Install with: apk add netcat-openbsd"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Function to generate dashboard HTML
|
||||
generate_dashboard() {
|
||||
# Get current status
|
||||
STATUS_JSON=$(/usr/local/bin/status --json 2>/dev/null || echo '{}')
|
||||
HEALTH_JSON=$(/usr/local/bin/health --json 2>/dev/null || echo '{}')
|
||||
|
||||
cat << 'EOF'
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Tor Guard Relay Dashboard</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 15px;
|
||||
padding: 25px;
|
||||
margin-bottom: 25px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
color: #764ba2;
|
||||
font-size: 28px;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-block;
|
||||
padding: 5px 15px;
|
||||
border-radius: 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.status-healthy { background: #10b981; color: white; }
|
||||
.status-running { background: #f59e0b; color: white; }
|
||||
.status-starting { background: #3b82f6; color: white; }
|
||||
.status-down { background: #ef4444; color: white; }
|
||||
.status-unknown { background: #6b7280; color: white; }
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
color: #4b5563;
|
||||
font-size: 16px;
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.metric {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.metric:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
color: #6b7280;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
background: #e5e7eb;
|
||||
border-radius: 15px;
|
||||
overflow: hidden;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #667eea, #764ba2);
|
||||
transition: width 0.5s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.fingerprint {
|
||||
font-family: 'Courier New', monospace;
|
||||
background: #f3f4f6;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
word-break: break-all;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.fingerprint:hover {
|
||||
background: #e5e7eb;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: #d1fae5;
|
||||
color: #065f46;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: center;
|
||||
color: white;
|
||||
margin-top: 40px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.refresh-timer {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
padding: 10px 20px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||||
font-size: 14px;
|
||||
color: #4b5563;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>
|
||||
🧅 Tor Guard Relay Dashboard
|
||||
<span class="status-badge status-healthy" id="overall-status">Healthy</span>
|
||||
</h1>
|
||||
<p style="color: #6b7280; margin-top: 5px;">Real-time monitoring and management</p>
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<h2>🚀 Bootstrap Progress</h2>
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" id="bootstrap-progress" style="width: 0%;">
|
||||
0%
|
||||
</div>
|
||||
</div>
|
||||
<p id="bootstrap-message" style="color: #6b7280; font-size: 14px; margin-top: 10px;">
|
||||
Initializing...
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>🌍 Network Status</h2>
|
||||
<div class="metric">
|
||||
<span class="metric-label">Reachability</span>
|
||||
<span class="metric-value" id="reachability">Checking...</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="metric-label">Public IP</span>
|
||||
<span class="metric-value" id="public-ip">Loading...</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="metric-label">ORPort</span>
|
||||
<span class="metric-value" id="orport">-</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>📊 Performance</h2>
|
||||
<div class="metric">
|
||||
<span class="metric-label">Uptime</span>
|
||||
<span class="metric-value" id="uptime">0h 0m</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="metric-label">Bandwidth</span>
|
||||
<span class="metric-value" id="bandwidth">- KB/s</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="metric-label">Connections</span>
|
||||
<span class="metric-value" id="connections">0</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>🔑 Relay Identity</h2>
|
||||
<div class="metric">
|
||||
<span class="metric-label">Nickname</span>
|
||||
<span class="metric-value" id="nickname">-</span>
|
||||
</div>
|
||||
<div class="metric" style="flex-direction: column; align-items: flex-start;">
|
||||
<span class="metric-label" style="margin-bottom: 10px;">Fingerprint</span>
|
||||
<div class="fingerprint" id="fingerprint" onclick="copyFingerprint()">
|
||||
Click to copy
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>⚠️ Health Monitor</h2>
|
||||
<div id="health-alerts">
|
||||
<div class="alert alert-success">
|
||||
✅ All systems operational
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="metric-label">Errors</span>
|
||||
<span class="metric-value" id="error-count">0</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="metric-label">Warnings</span>
|
||||
<span class="metric-value" id="warning-count">0</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>🔗 Quick Actions</h2>
|
||||
<div style="display: flex; flex-direction: column; gap: 10px;">
|
||||
<button onclick="window.open('/api/status', '_blank')" style="padding: 10px; border: none; background: #667eea; color: white; border-radius: 8px; cursor: pointer;">
|
||||
📄 View JSON Status
|
||||
</button>
|
||||
<button onclick="window.open('/api/metrics', '_blank')" style="padding: 10px; border: none; background: #764ba2; color: white; border-radius: 8px; cursor: pointer;">
|
||||
📊 View Metrics
|
||||
</button>
|
||||
<button onclick="refreshData()" style="padding: 10px; border: none; background: #10b981; color: white; border-radius: 8px; cursor: pointer;">
|
||||
🔄 Refresh Now
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>
|
||||
Tor-Guard-Relay v${VERSION} |
|
||||
<a href="https://metrics.torproject.org" target="_blank">Tor Metrics</a> |
|
||||
<a href="https://github.com/torproject/tor" target="_blank">GitHub</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="refresh-timer">
|
||||
🔄 Auto-refresh: <span id="countdown">${REFRESH_INTERVAL}</span>s
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let refreshInterval = ${REFRESH_INTERVAL};
|
||||
let countdown = refreshInterval;
|
||||
|
||||
function updateStatus(data) {
|
||||
// Update overall status
|
||||
const statusEl = document.getElementById('overall-status');
|
||||
statusEl.className = 'status-badge status-' + (data.status || 'unknown');
|
||||
statusEl.textContent = (data.status || 'Unknown').toUpperCase();
|
||||
|
||||
// Update bootstrap progress
|
||||
const bootstrap = data.bootstrap || {};
|
||||
const progressEl = document.getElementById('bootstrap-progress');
|
||||
const percent = bootstrap.percent || 0;
|
||||
progressEl.style.width = percent + '%';
|
||||
progressEl.textContent = percent + '%';
|
||||
document.getElementById('bootstrap-message').textContent =
|
||||
bootstrap.message || 'Waiting for bootstrap...';
|
||||
|
||||
// Update network status
|
||||
const reachable = data.reachability?.reachable;
|
||||
document.getElementById('reachability').textContent =
|
||||
reachable ? '✅ Reachable' : '⏳ Testing...';
|
||||
document.getElementById('public-ip').textContent =
|
||||
data.network?.public_ip || 'Unknown';
|
||||
document.getElementById('orport').textContent =
|
||||
data.configuration?.orport || '-';
|
||||
|
||||
// Update performance
|
||||
document.getElementById('uptime').textContent =
|
||||
data.process?.uptime || '0h 0m';
|
||||
document.getElementById('bandwidth').textContent =
|
||||
data.configuration?.bandwidth || '- KB/s';
|
||||
|
||||
// Update identity
|
||||
document.getElementById('nickname').textContent =
|
||||
data.identity?.nickname || '-';
|
||||
document.getElementById('fingerprint').textContent =
|
||||
data.identity?.fingerprint || 'Not available';
|
||||
|
||||
// Update health
|
||||
const errors = data.issues?.errors || 0;
|
||||
const warnings = data.issues?.warnings || 0;
|
||||
document.getElementById('error-count').textContent = errors;
|
||||
document.getElementById('warning-count').textContent = warnings;
|
||||
|
||||
// Update health alerts
|
||||
const alertsEl = document.getElementById('health-alerts');
|
||||
if (errors > 0) {
|
||||
alertsEl.innerHTML = '<div class="alert alert-error">❌ ' + errors + ' errors detected</div>';
|
||||
} else if (warnings > 0) {
|
||||
alertsEl.innerHTML = '<div class="alert alert-warning">⚠️ ' + warnings + ' warnings detected</div>';
|
||||
} else {
|
||||
alertsEl.innerHTML = '<div class="alert alert-success">✅ All systems operational</div>';
|
||||
}
|
||||
}
|
||||
|
||||
function refreshData() {
|
||||
fetch('/api/status')
|
||||
.then(response => response.json())
|
||||
.then(data => updateStatus(data))
|
||||
.catch(error => console.error('Error fetching status:', error));
|
||||
}
|
||||
|
||||
function copyFingerprint() {
|
||||
const fp = document.getElementById('fingerprint').textContent;
|
||||
if (fp && fp !== 'Not available' && fp !== 'Click to copy') {
|
||||
navigator.clipboard.writeText(fp).then(() => {
|
||||
const el = document.getElementById('fingerprint');
|
||||
const original = el.textContent;
|
||||
el.textContent = '✅ Copied!';
|
||||
setTimeout(() => el.textContent = original, 2000);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Countdown timer
|
||||
setInterval(() => {
|
||||
countdown--;
|
||||
if (countdown <= 0) {
|
||||
countdown = refreshInterval;
|
||||
refreshData();
|
||||
}
|
||||
document.getElementById('countdown').textContent = countdown;
|
||||
}, 1000);
|
||||
|
||||
// Initial load
|
||||
refreshData();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
}
|
||||
|
||||
# Function to handle API requests
|
||||
handle_api() {
|
||||
REQUEST_PATH="$1"
|
||||
|
||||
case "$REQUEST_PATH" in
|
||||
"/api/status")
|
||||
CONTENT=$(/usr/local/bin/status --json 2>/dev/null || echo '{"error":"Failed to get status"}')
|
||||
echo "HTTP/1.1 200 OK"
|
||||
echo "Content-Type: application/json"
|
||||
echo "Cache-Control: no-cache"
|
||||
echo "Connection: close"
|
||||
echo ""
|
||||
echo "$CONTENT"
|
||||
;;
|
||||
|
||||
"/api/metrics")
|
||||
CONTENT=$(/usr/local/bin/metrics 2>/dev/null || echo "# Error generating metrics
|
||||
@@ -1,21 +1,188 @@
|
||||
#!/bin/sh
|
||||
# fingerprint - Display relay fingerprint
|
||||
# Usage: docker exec guard-relay fingerprint
|
||||
# fingerprint - Display and manage Tor relay fingerprint
|
||||
# Usage: docker exec guard-relay fingerprint [--json|--help]
|
||||
|
||||
echo "🔑 Tor Relay Fingerprint"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
set -e
|
||||
|
||||
if [ -f /var/lib/tor/fingerprint ]; then
|
||||
cat /var/lib/tor/fingerprint
|
||||
echo ""
|
||||
echo "🌐 Search on Tor Metrics:"
|
||||
echo " https://metrics.torproject.org/rs.html"
|
||||
echo ""
|
||||
echo "🧅 Search on Onion Metrics (Tor Browser):"
|
||||
echo " http://hctxrvjzfpvmzh2jllqhgvvkoepxb4kfzdjm6h7egcwlumggtktiftid.onion/rs.html"
|
||||
else
|
||||
echo "⚠️ Fingerprint not yet generated."
|
||||
echo "📍 Tor is still bootstrapping or generating keys."
|
||||
echo "💡 Check back in a few minutes."
|
||||
# Configuration
|
||||
VERSION="1.1.0"
|
||||
OUTPUT_FORMAT="${OUTPUT_FORMAT:-text}"
|
||||
FINGERPRINT_FILE="${FINGERPRINT_FILE:-/var/lib/tor/fingerprint}"
|
||||
SHOW_LINKS="${SHOW_LINKS:-true}"
|
||||
|
||||
# Parse arguments
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--help|-h)
|
||||
cat << EOF
|
||||
🔑 Tor-Guard-Relay Fingerprint Tool v${VERSION}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
USAGE:
|
||||
fingerprint [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--json Output in JSON format
|
||||
--plain Plain text output
|
||||
--copy Output for easy copying
|
||||
--links Show monitoring links (default)
|
||||
--no-links Hide monitoring links
|
||||
--help, -h Show this help message
|
||||
|
||||
ENVIRONMENT VARIABLES:
|
||||
OUTPUT_FORMAT Output format (text/json/plain)
|
||||
FINGERPRINT_FILE Path to fingerprint file
|
||||
SHOW_LINKS Show monitoring links (true/false)
|
||||
|
||||
OUTPUT FORMATS:
|
||||
text Human-readable with emojis and links
|
||||
json Machine-readable JSON
|
||||
plain Simple text for scripts
|
||||
copy Formatted for clipboard copying
|
||||
|
||||
MONITORING LINKS:
|
||||
• Tor Metrics (clearnet)
|
||||
• Onion Metrics (Tor Browser only)
|
||||
|
||||
EXAMPLES:
|
||||
fingerprint # Display with links
|
||||
fingerprint --json # JSON output
|
||||
fingerprint --copy # Copy-friendly format
|
||||
fingerprint --plain # Script-friendly output
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
--json) OUTPUT_FORMAT="json" ;;
|
||||
--plain) OUTPUT_FORMAT="plain" ;;
|
||||
--copy) OUTPUT_FORMAT="copy" ;;
|
||||
--links) SHOW_LINKS="true" ;;
|
||||
--no-links) SHOW_LINKS="false" ;;
|
||||
-*)
|
||||
echo "❌ Unknown option: $arg"
|
||||
echo "💡 Use --help for usage information"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Check if fingerprint exists
|
||||
if [ ! -f "$FINGERPRINT_FILE" ]; then
|
||||
case "$OUTPUT_FORMAT" in
|
||||
json)
|
||||
cat << EOF
|
||||
{
|
||||
"status": "not_ready",
|
||||
"message": "Fingerprint not yet generated",
|
||||
"fingerprint": null,
|
||||
"nickname": null
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
plain)
|
||||
echo "NOT_READY"
|
||||
;;
|
||||
*)
|
||||
echo "⚠️ Fingerprint not yet generated."
|
||||
echo "📍 Tor is still bootstrapping or generating keys."
|
||||
echo "💡 Check back in a few minutes."
|
||||
;;
|
||||
esac
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Read fingerprint
|
||||
NICKNAME=$(awk '{print $1}' "$FINGERPRINT_FILE" 2>/dev/null || echo "")
|
||||
FINGERPRINT=$(awk '{print $2}' "$FINGERPRINT_FILE" 2>/dev/null || echo "")
|
||||
|
||||
# Validate fingerprint format (40 hex characters)
|
||||
if ! echo "$FINGERPRINT" | grep -qE "^[A-F0-9]{40}$"; then
|
||||
case "$OUTPUT_FORMAT" in
|
||||
json)
|
||||
echo '{"status":"invalid","message":"Invalid fingerprint format"}'
|
||||
;;
|
||||
plain)
|
||||
echo "INVALID"
|
||||
;;
|
||||
*)
|
||||
echo "❌ Invalid fingerprint format detected"
|
||||
;;
|
||||
esac
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Generate formatted versions
|
||||
FINGERPRINT_SPACED=$(echo "$FINGERPRINT" | sed 's/\(..\)/\1 /g' | sed 's/ $//')
|
||||
FINGERPRINT_COLON=$(echo "$FINGERPRINT" | sed 's/\(..\)/\1:/g' | sed 's/:$//')
|
||||
|
||||
# Get additional info if available
|
||||
CREATION_TIME=""
|
||||
if [ -f "$FINGERPRINT_FILE" ]; then
|
||||
CREATION_TIME=$(stat -c %y "$FINGERPRINT_FILE" 2>/dev/null | cut -d' ' -f1,2 | cut -d'.' -f1 || echo "")
|
||||
fi
|
||||
|
||||
# Output based on format
|
||||
case "$OUTPUT_FORMAT" in
|
||||
json)
|
||||
cat << EOF
|
||||
{
|
||||
"status": "ready",
|
||||
"nickname": "$NICKNAME",
|
||||
"fingerprint": "$FINGERPRINT",
|
||||
"fingerprint_spaced": "$FINGERPRINT_SPACED",
|
||||
"fingerprint_colon": "$FINGERPRINT_COLON",
|
||||
"created": "$CREATION_TIME",
|
||||
"links": {
|
||||
"metrics": "https://metrics.torproject.org/rs.html#search/$FINGERPRINT",
|
||||
"onion_metrics": "http://hctxrvjzfpvmzh2jllqhgvvkoepxb4kfzdjm6h7egcwlumggtktiftid.onion/rs.html#search/$FINGERPRINT"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
|
||||
plain)
|
||||
echo "$NICKNAME $FINGERPRINT"
|
||||
;;
|
||||
|
||||
copy)
|
||||
echo "$FINGERPRINT"
|
||||
echo ""
|
||||
echo "# Formatted versions:"
|
||||
echo "# Spaced: $FINGERPRINT_SPACED"
|
||||
echo "# Colon: $FINGERPRINT_COLON"
|
||||
;;
|
||||
|
||||
*)
|
||||
# Default text format with emojis
|
||||
echo "🔑 Tor Relay Fingerprint"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "📝 Nickname: $NICKNAME"
|
||||
echo "🆔 Fingerprint: $FINGERPRINT"
|
||||
echo ""
|
||||
echo "📋 Formatted versions:"
|
||||
echo " Spaced: $FINGERPRINT_SPACED"
|
||||
echo " Colon: $FINGERPRINT_COLON"
|
||||
|
||||
if [ -n "$CREATION_TIME" ]; then
|
||||
echo ""
|
||||
echo "🕒 Created: $CREATION_TIME"
|
||||
fi
|
||||
|
||||
if [ "$SHOW_LINKS" = "true" ]; then
|
||||
echo ""
|
||||
echo "🌐 Monitor your relay:"
|
||||
echo ""
|
||||
echo " 📊 Tor Metrics:"
|
||||
echo " https://metrics.torproject.org/rs.html#search/$FINGERPRINT"
|
||||
echo ""
|
||||
echo " 🧅 Onion Metrics (Tor Browser only):"
|
||||
echo " http://hctxrvjzfpvmzh2jllqhgvvkoepxb4kfzdjm6h7egcwlumggtktiftid.onion/rs.html#search/$FINGERPRINT"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "💡 Tip: Use 'fingerprint --copy' for easy copying"
|
||||
;;
|
||||
esac
|
||||
|
||||
231
tools/health
Normal file
231
tools/health
Normal file
@@ -0,0 +1,231 @@
|
||||
#!/bin/sh
|
||||
# health - Comprehensive Tor relay health check (hybrid stable version)
|
||||
# Combines full structured output with simplified inline logic (Alpine-safe)
|
||||
|
||||
set -e
|
||||
|
||||
VERSION="1.1.0"
|
||||
OUTPUT_FORMAT="${OUTPUT_FORMAT:-text}"
|
||||
ENABLE_HEALTH_CHECK="${ENABLE_HEALTH_CHECK:-true}"
|
||||
HEALTH_WEBHOOK_URL="${HEALTH_WEBHOOK_URL:-}"
|
||||
CHECK_TIMEOUT="${CHECK_TIMEOUT:-5}"
|
||||
|
||||
safe() { "$@" 2>/dev/null || true; }
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--help|-h)
|
||||
cat << EOF
|
||||
🧅 Tor-Guard-Relay Health Check v${VERSION}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
USAGE:
|
||||
health [--json|--plain|--text|--webhook]
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
EOF
|
||||
exit 0 ;;
|
||||
--json) OUTPUT_FORMAT="json" ;;
|
||||
--plain) OUTPUT_FORMAT="plain" ;;
|
||||
--text) OUTPUT_FORMAT="text" ;;
|
||||
--webhook) SEND_WEBHOOK="true" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Skip if disabled
|
||||
if [ "$ENABLE_HEALTH_CHECK" != "true" ]; then
|
||||
case "$OUTPUT_FORMAT" in
|
||||
json) echo '{"status":"disabled"}' ;;
|
||||
plain) echo "DISABLED" ;;
|
||||
*) echo "⏸️ Health checking disabled" ;;
|
||||
esac
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Initialize variables
|
||||
STATUS="unknown"
|
||||
BOOTSTRAP_PERCENT=0
|
||||
IS_RUNNING=false
|
||||
IS_REACHABLE=false
|
||||
FINGERPRINT=""
|
||||
NICKNAME=""
|
||||
UPTIME="0"
|
||||
ERRORS=0
|
||||
WARNINGS=0
|
||||
VERSION_INFO=""
|
||||
ORPORT=""
|
||||
DIRPORT=""
|
||||
EXIT_RELAY="false"
|
||||
|
||||
# --- Inline Checks (Simplified Logic that Works) ---
|
||||
|
||||
# Tor process
|
||||
if safe pgrep -x tor >/dev/null; then
|
||||
IS_RUNNING=true
|
||||
PID=$(safe pgrep -x tor | head -1)
|
||||
UPTIME=$(safe ps -p "$PID" -o time= | tr -d ' ')
|
||||
[ -z "$UPTIME" ] && UPTIME="0"
|
||||
fi
|
||||
|
||||
# Bootstrap progress
|
||||
if [ -f /var/log/tor/notices.log ]; then
|
||||
BOOTSTRAP_LINE=$(safe grep "Bootstrapped" /var/log/tor/notices.log | tail -1)
|
||||
BOOTSTRAP_PERCENT=$(echo "$BOOTSTRAP_LINE" | grep -oE '[0-9]+%' | tr -d '%' | tail -1)
|
||||
[ -z "$BOOTSTRAP_PERCENT" ] && BOOTSTRAP_PERCENT=0
|
||||
fi
|
||||
|
||||
# Reachability
|
||||
if [ -f /var/log/tor/notices.log ]; then
|
||||
if safe grep -q "reachable from the outside" /var/log/tor/notices.log; then
|
||||
IS_REACHABLE=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Relay info
|
||||
if [ -f /var/lib/tor/fingerprint ]; then
|
||||
NICKNAME=$(safe awk '{print $1}' /var/lib/tor/fingerprint)
|
||||
FINGERPRINT=$(safe awk '{print $2}' /var/lib/tor/fingerprint)
|
||||
fi
|
||||
|
||||
if [ -f /etc/tor/torrc ]; then
|
||||
ORPORT=$(safe grep -E "^ORPort" /etc/tor/torrc | awk '{print $2}' | head -1)
|
||||
DIRPORT=$(safe grep -E "^DirPort" /etc/tor/torrc | awk '{print $2}' | head -1)
|
||||
if safe grep -qE "^ExitRelay\s+1" /etc/tor/torrc; then
|
||||
EXIT_RELAY="true"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f /build-info.txt ]; then
|
||||
VERSION_INFO=$(safe head -1 /build-info.txt | cut -d: -f2- | tr -d ' ')
|
||||
fi
|
||||
|
||||
# Count issues
|
||||
if [ -f /var/log/tor/notices.log ]; then
|
||||
ERRORS=$(safe grep -ciE "\[err\]" /var/log/tor/notices.log)
|
||||
WARNINGS=$(safe grep -ciE "\[warn\]" /var/log/tor/notices.log)
|
||||
fi
|
||||
|
||||
# Determine overall status
|
||||
if [ "$IS_RUNNING" = false ]; then
|
||||
STATUS="down"
|
||||
elif [ "$BOOTSTRAP_PERCENT" -eq 100 ] && [ "$IS_REACHABLE" = true ]; then
|
||||
STATUS="healthy"
|
||||
elif [ "$BOOTSTRAP_PERCENT" -eq 100 ]; then
|
||||
STATUS="running"
|
||||
elif [ "$BOOTSTRAP_PERCENT" -gt 0 ]; then
|
||||
STATUS="starting"
|
||||
else
|
||||
STATUS="unknown"
|
||||
fi
|
||||
|
||||
TIMESTAMP=$(date -u '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || date)
|
||||
|
||||
# --- Output ---
|
||||
case "$OUTPUT_FORMAT" in
|
||||
json)
|
||||
cat << EOF
|
||||
{
|
||||
"status": "$STATUS",
|
||||
"timestamp": "$TIMESTAMP",
|
||||
"process": {
|
||||
"running": $IS_RUNNING,
|
||||
"uptime": "$UPTIME"
|
||||
},
|
||||
"bootstrap": {
|
||||
"percent": $BOOTSTRAP_PERCENT,
|
||||
"complete": $([ "$BOOTSTRAP_PERCENT" -eq 100 ] && echo "true" || echo "false")
|
||||
},
|
||||
"network": {
|
||||
"reachable": $IS_REACHABLE,
|
||||
"orport": "$ORPORT",
|
||||
"dirport": "$DIRPORT"
|
||||
},
|
||||
"relay": {
|
||||
"nickname": "$NICKNAME",
|
||||
"fingerprint": "$FINGERPRINT",
|
||||
"exit_relay": $EXIT_RELAY,
|
||||
"version": "$VERSION_INFO"
|
||||
},
|
||||
"issues": {
|
||||
"errors": $ERRORS,
|
||||
"warnings": $WARNINGS
|
||||
}
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
plain)
|
||||
echo "STATUS=$STATUS"
|
||||
echo "RUNNING=$IS_RUNNING"
|
||||
echo "BOOTSTRAP=$BOOTSTRAP_PERCENT"
|
||||
echo "REACHABLE=$IS_REACHABLE"
|
||||
echo "NICKNAME=$NICKNAME"
|
||||
echo "FINGERPRINT=$FINGERPRINT"
|
||||
echo "ERRORS=$ERRORS"
|
||||
echo "WARNINGS=$WARNINGS"
|
||||
echo "UPTIME=$UPTIME"
|
||||
;;
|
||||
*)
|
||||
echo "🧅 Tor Relay Health Check"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
case "$STATUS" in
|
||||
healthy) echo "📊 Status: ✅ HEALTHY" ;;
|
||||
running) echo "📊 Status: 🟡 RUNNING (awaiting reachability)" ;;
|
||||
starting) echo "📊 Status: 🔄 STARTING ($BOOTSTRAP_PERCENT% bootstrapped)" ;;
|
||||
down) echo "📊 Status: ❌ DOWN" ;;
|
||||
*) echo "📊 Status: ❓ UNKNOWN" ;;
|
||||
esac
|
||||
echo ""
|
||||
echo "⚙️ Process:"
|
||||
if [ "$IS_RUNNING" = true ]; then
|
||||
echo " ✅ Tor is running (uptime: $UPTIME)"
|
||||
else
|
||||
echo " ❌ Tor process not found"
|
||||
fi
|
||||
echo ""
|
||||
echo "🚀 Bootstrap:"
|
||||
if [ "$BOOTSTRAP_PERCENT" -eq 100 ]; then
|
||||
echo " ✅ Fully bootstrapped (100%)"
|
||||
elif [ "$BOOTSTRAP_PERCENT" -gt 0 ]; then
|
||||
echo " 🔄 Bootstrapping... ($BOOTSTRAP_PERCENT%)"
|
||||
else
|
||||
echo " ⏳ Not started"
|
||||
fi
|
||||
echo ""
|
||||
echo "🌍 Network:"
|
||||
if [ "$IS_REACHABLE" = true ]; then
|
||||
echo " ✅ Reachable from the outside"
|
||||
else
|
||||
echo " ⏳ Testing reachability..."
|
||||
fi
|
||||
[ -n "$ORPORT" ] && echo " 📍 ORPort: $ORPORT"
|
||||
[ -n "$DIRPORT" ] && echo " 📍 DirPort: $DIRPORT"
|
||||
echo ""
|
||||
if [ -n "$NICKNAME" ] || [ -n "$FINGERPRINT" ]; then
|
||||
echo "🔑 Relay Identity:"
|
||||
[ -n "$NICKNAME" ] && echo " 📝 Nickname: $NICKNAME"
|
||||
[ -n "$FINGERPRINT" ] && echo " 🆔 Fingerprint: $FINGERPRINT"
|
||||
[ "$EXIT_RELAY" = "true" ] && echo " 🚪 Type: Exit Relay" || echo " 🔒 Type: Guard/Middle Relay"
|
||||
echo ""
|
||||
fi
|
||||
if [ "$ERRORS" -gt 0 ] || [ "$WARNINGS" -gt 0 ]; then
|
||||
echo "⚠️ Issues:"
|
||||
[ "$ERRORS" -gt 0 ] && echo " ❌ Errors: $ERRORS"
|
||||
[ "$WARNINGS" -gt 0 ] && echo " ⚠️ Warnings: $WARNINGS"
|
||||
echo ""
|
||||
fi
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🕒 Checked: $TIMESTAMP"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Optional webhook support
|
||||
if [ "$SEND_WEBHOOK" = "true" ] && [ -n "$HEALTH_WEBHOOK_URL" ]; then
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
/usr/local/bin/health --json | curl -s -X POST "$HEALTH_WEBHOOK_URL" \
|
||||
-H "Content-Type: application/json" -d @- >/dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
|
||||
case "$STATUS" in
|
||||
healthy|running|starting) exit 0 ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
213
tools/metrics
Normal file
213
tools/metrics
Normal file
@@ -0,0 +1,213 @@
|
||||
#!/bin/sh
|
||||
# metrics - Prometheus-compatible metrics exporter for Tor relay
|
||||
# Usage: docker exec guard-relay metrics [--help]
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
VERSION="1.1.0"
|
||||
METRICS_PREFIX="${METRICS_PREFIX:-tor_relay}"
|
||||
INCLUDE_LABELS="${INCLUDE_LABELS:-true}"
|
||||
METRICS_FORMAT="${METRICS_FORMAT:-prometheus}"
|
||||
|
||||
# Parse arguments
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--help|-h)
|
||||
cat << EOF
|
||||
📊 Tor-Guard-Relay Metrics Exporter v${VERSION}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
USAGE:
|
||||
metrics [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--prometheus Output in Prometheus format (default)
|
||||
--json Output metrics as JSON
|
||||
--help, -h Show this help message
|
||||
|
||||
ENVIRONMENT VARIABLES:
|
||||
METRICS_PREFIX Prefix for metric names (default: tor_relay)
|
||||
INCLUDE_LABELS Include labels in output (true/false)
|
||||
METRICS_FORMAT Output format (prometheus/json)
|
||||
|
||||
METRICS EXPORTED:
|
||||
• ${METRICS_PREFIX}_up Relay status (0/1)
|
||||
• ${METRICS_PREFIX}_bootstrap_percent Bootstrap progress
|
||||
• ${METRICS_PREFIX}_reachable Reachability status
|
||||
• ${METRICS_PREFIX}_uptime_seconds Process uptime
|
||||
• ${METRICS_PREFIX}_errors_total Total error count
|
||||
• ${METRICS_PREFIX}_warnings_total Total warning count
|
||||
• ${METRICS_PREFIX}_bandwidth_read_bytes Bytes read
|
||||
• ${METRICS_PREFIX}_bandwidth_write_bytes Bytes written
|
||||
• ${METRICS_PREFIX}_circuits_total Active circuits
|
||||
|
||||
PROMETHEUS INTEGRATION:
|
||||
# prometheus.yml
|
||||
scrape_configs:
|
||||
- job_name: 'tor-relay'
|
||||
static_configs:
|
||||
- targets: ['relay:9052']
|
||||
|
||||
EXAMPLES:
|
||||
metrics # Prometheus format output
|
||||
metrics --json # JSON metrics
|
||||
curl localhost:9052/metrics # HTTP endpoint
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
--prometheus) METRICS_FORMAT="prometheus" ;;
|
||||
--json) METRICS_FORMAT="json" ;;
|
||||
-*)
|
||||
echo "# ERROR: Unknown option: $arg"
|
||||
echo "# Use --help for usage information"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Initialize metrics
|
||||
RELAY_UP=0
|
||||
BOOTSTRAP_PERCENT=0
|
||||
IS_REACHABLE=0
|
||||
UPTIME_SECONDS=0
|
||||
ERROR_COUNT=0
|
||||
WARNING_COUNT=0
|
||||
BANDWIDTH_READ=0
|
||||
BANDWIDTH_WRITE=0
|
||||
CIRCUITS_ACTIVE=0
|
||||
NICKNAME=""
|
||||
FINGERPRINT=""
|
||||
VERSION_INFO=""
|
||||
|
||||
# Get relay identity
|
||||
if [ -f /var/lib/tor/fingerprint ]; then
|
||||
NICKNAME=$(awk '{print $1}' /var/lib/tor/fingerprint 2>/dev/null || echo "unknown")
|
||||
FINGERPRINT=$(awk '{print $2}' /var/lib/tor/fingerprint 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
# Check if Tor is running
|
||||
if pgrep -x tor > /dev/null 2>&1; then
|
||||
RELAY_UP=1
|
||||
|
||||
# Calculate uptime in seconds
|
||||
PID=$(pgrep -x tor | head -1)
|
||||
if [ -n "$PID" ]; then
|
||||
# Get process start time
|
||||
if [ -f "/proc/$PID/stat" ]; then
|
||||
STARTTIME=$(awk '{print $22}' "/proc/$PID/stat" 2>/dev/null || echo 0)
|
||||
UPTIME_TICKS=$(($(cat /proc/uptime | cut -d. -f1) * 100))
|
||||
if [ "$STARTTIME" -gt 0 ]; then
|
||||
UPTIME_SECONDS=$(((UPTIME_TICKS - STARTTIME) / 100))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Parse bootstrap percentage
|
||||
if [ -f /var/log/tor/notices.log ]; then
|
||||
BOOTSTRAP_LINE=$(grep "Bootstrapped" /var/log/tor/notices.log 2>/dev/null | tail -1)
|
||||
if [ -n "$BOOTSTRAP_LINE" ]; then
|
||||
BOOTSTRAP_PERCENT=$(echo "$BOOTSTRAP_LINE" | grep -oE '[0-9]+%' | tr -d '%' | tail -1)
|
||||
[ -z "$BOOTSTRAP_PERCENT" ] && BOOTSTRAP_PERCENT=0
|
||||
fi
|
||||
|
||||
# Check reachability
|
||||
if grep -q "reachable from the outside" /var/log/tor/notices.log 2>/dev/null; then
|
||||
IS_REACHABLE=1
|
||||
fi
|
||||
|
||||
# Count errors and warnings
|
||||
ERROR_COUNT=$(grep -cE "\[err\]|\[error\]" /var/log/tor/notices.log 2>/dev/null || echo 0)
|
||||
WARNING_COUNT=$(grep -cE "\[warn\]|\[warning\]" /var/log/tor/notices.log 2>/dev/null || echo 0)
|
||||
fi
|
||||
|
||||
# Parse bandwidth from state file
|
||||
if [ -f /var/lib/tor/state ]; then
|
||||
BANDWIDTH_READ=$(grep "^AccountingBytesReadInterval" /var/lib/tor/state 2>/dev/null | awk '{print $2}' || echo 0)
|
||||
BANDWIDTH_WRITE=$(grep "^AccountingBytesWrittenInterval" /var/lib/tor/state 2>/dev/null | awk '{print $2}' || echo 0)
|
||||
fi
|
||||
|
||||
# Get version info
|
||||
if [ -f /build-info.txt ]; then
|
||||
VERSION_INFO=$(head -1 /build-info.txt 2>/dev/null | cut -d: -f2- | tr -d ' ' || echo "unknown")
|
||||
fi
|
||||
|
||||
# Generate timestamp
|
||||
TIMESTAMP=$(date +%s)000
|
||||
|
||||
# Output based on format
|
||||
case "$METRICS_FORMAT" in
|
||||
json)
|
||||
cat << EOF
|
||||
{
|
||||
"timestamp": $TIMESTAMP,
|
||||
"metrics": {
|
||||
"up": $RELAY_UP,
|
||||
"bootstrap_percent": $BOOTSTRAP_PERCENT,
|
||||
"reachable": $IS_REACHABLE,
|
||||
"uptime_seconds": $UPTIME_SECONDS,
|
||||
"errors_total": $ERROR_COUNT,
|
||||
"warnings_total": $WARNING_COUNT,
|
||||
"bandwidth_read_bytes": $BANDWIDTH_READ,
|
||||
"bandwidth_write_bytes": $BANDWIDTH_WRITE,
|
||||
"circuits_total": $CIRCUITS_ACTIVE
|
||||
},
|
||||
"labels": {
|
||||
"nickname": "$NICKNAME",
|
||||
"fingerprint": "$FINGERPRINT",
|
||||
"version": "$VERSION_INFO"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
|
||||
*)
|
||||
# Prometheus format (default)
|
||||
echo "# HELP ${METRICS_PREFIX}_up Tor relay status (1 = up, 0 = down)"
|
||||
echo "# TYPE ${METRICS_PREFIX}_up gauge"
|
||||
if [ "$INCLUDE_LABELS" = "true" ] && [ -n "$NICKNAME" ]; then
|
||||
echo "${METRICS_PREFIX}_up{nickname=\"$NICKNAME\",fingerprint=\"$FINGERPRINT\"} $RELAY_UP"
|
||||
else
|
||||
echo "${METRICS_PREFIX}_up $RELAY_UP"
|
||||
fi
|
||||
|
||||
echo "# HELP ${METRICS_PREFIX}_bootstrap_percent Bootstrap completion percentage"
|
||||
echo "# TYPE ${METRICS_PREFIX}_bootstrap_percent gauge"
|
||||
echo "${METRICS_PREFIX}_bootstrap_percent $BOOTSTRAP_PERCENT"
|
||||
|
||||
echo "# HELP ${METRICS_PREFIX}_reachable Relay reachability status"
|
||||
echo "# TYPE ${METRICS_PREFIX}_reachable gauge"
|
||||
echo "${METRICS_PREFIX}_reachable $IS_REACHABLE"
|
||||
|
||||
echo "# HELP ${METRICS_PREFIX}_uptime_seconds Relay process uptime in seconds"
|
||||
echo "# TYPE ${METRICS_PREFIX}_uptime_seconds counter"
|
||||
echo "${METRICS_PREFIX}_uptime_seconds $UPTIME_SECONDS"
|
||||
|
||||
echo "# HELP ${METRICS_PREFIX}_errors_total Total number of errors in log"
|
||||
echo "# TYPE ${METRICS_PREFIX}_errors_total counter"
|
||||
echo "${METRICS_PREFIX}_errors_total $ERROR_COUNT"
|
||||
|
||||
echo "# HELP ${METRICS_PREFIX}_warnings_total Total number of warnings in log"
|
||||
echo "# TYPE ${METRICS_PREFIX}_warnings_total counter"
|
||||
echo "${METRICS_PREFIX}_warnings_total $WARNING_COUNT"
|
||||
|
||||
echo "# HELP ${METRICS_PREFIX}_bandwidth_read_bytes Total bytes read"
|
||||
echo "# TYPE ${METRICS_PREFIX}_bandwidth_read_bytes counter"
|
||||
echo "${METRICS_PREFIX}_bandwidth_read_bytes $BANDWIDTH_READ"
|
||||
|
||||
echo "# HELP ${METRICS_PREFIX}_bandwidth_write_bytes Total bytes written"
|
||||
echo "# TYPE ${METRICS_PREFIX}_bandwidth_write_bytes counter"
|
||||
echo "${METRICS_PREFIX}_bandwidth_write_bytes $BANDWIDTH_WRITE"
|
||||
|
||||
echo "# HELP ${METRICS_PREFIX}_circuits_total Active circuit count"
|
||||
echo "# TYPE ${METRICS_PREFIX}_circuits_total gauge"
|
||||
echo "${METRICS_PREFIX}_circuits_total $CIRCUITS_ACTIVE"
|
||||
|
||||
echo "# HELP ${METRICS_PREFIX}_info Relay information"
|
||||
echo "# TYPE ${METRICS_PREFIX}_info gauge"
|
||||
echo "${METRICS_PREFIX}_info{nickname=\"$NICKNAME\",version=\"$VERSION_INFO\"} 1"
|
||||
;;
|
||||
esac
|
||||
251
tools/metrics-http
Normal file
251
tools/metrics-http
Normal file
@@ -0,0 +1,251 @@
|
||||
#!/bin/sh
|
||||
# metrics-http - HTTP server for Prometheus metrics endpoint
|
||||
# Usage: metrics-http [--port PORT] [--help]
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
VERSION="1.1.0"
|
||||
METRICS_PORT="${METRICS_PORT:-9052}"
|
||||
METRICS_BIND="${METRICS_BIND:-0.0.0.0}"
|
||||
METRICS_PATH="${METRICS_PATH:-/metrics}"
|
||||
ENABLE_METRICS="${ENABLE_METRICS:-true}"
|
||||
RESPONSE_TIMEOUT="${RESPONSE_TIMEOUT:-10}"
|
||||
|
||||
# Parse arguments
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--help|-h)
|
||||
cat << EOF
|
||||
🌐 Tor-Guard-Relay Metrics HTTP Server v${VERSION}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
USAGE:
|
||||
metrics-http [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--port PORT Listen port (default: 9052)
|
||||
--bind ADDR Bind address (default: 0.0.0.0)
|
||||
--path PATH Metrics path (default: /metrics)
|
||||
--daemon Run as daemon
|
||||
--help, -h Show this help message
|
||||
|
||||
ENVIRONMENT VARIABLES:
|
||||
METRICS_PORT Port to listen on (default: 9052)
|
||||
METRICS_BIND Address to bind (default: 0.0.0.0)
|
||||
METRICS_PATH URL path for metrics (default: /metrics)
|
||||
ENABLE_METRICS Enable metrics server (true/false)
|
||||
RESPONSE_TIMEOUT Response timeout in seconds
|
||||
|
||||
ENDPOINTS:
|
||||
http://localhost:9052/metrics Prometheus metrics
|
||||
http://localhost:9052/health Health check endpoint
|
||||
http://localhost:9052/ Status page
|
||||
|
||||
PROMETHEUS CONFIG:
|
||||
scrape_configs:
|
||||
- job_name: 'tor-relay'
|
||||
static_configs:
|
||||
- targets: ['relay:9052']
|
||||
metrics_path: '/metrics'
|
||||
scrape_interval: 30s
|
||||
|
||||
DOCKER INTEGRATION:
|
||||
# Expose in docker-compose.yml
|
||||
ports:
|
||||
- "9052:9052"
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
--port)
|
||||
shift
|
||||
METRICS_PORT="$1"
|
||||
shift
|
||||
;;
|
||||
--bind)
|
||||
shift
|
||||
METRICS_BIND="$1"
|
||||
shift
|
||||
;;
|
||||
--path)
|
||||
shift
|
||||
METRICS_PATH="$1"
|
||||
shift
|
||||
;;
|
||||
--daemon)
|
||||
DAEMON_MODE="true"
|
||||
;;
|
||||
-*)
|
||||
echo "❌ Unknown option: $arg"
|
||||
echo "💡 Use --help for usage information"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Check if metrics are enabled
|
||||
if [ "$ENABLE_METRICS" != "true" ]; then
|
||||
echo "📊 Metrics HTTP server is disabled"
|
||||
echo "💡 Set ENABLE_METRICS=true to enable"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check for netcat
|
||||
if ! command -v nc > /dev/null 2>&1; then
|
||||
echo "❌ Error: netcat (nc) is required but not installed"
|
||||
echo "💡 Install with: apk add netcat-openbsd"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Function to generate HTTP response
|
||||
generate_response() {
|
||||
REQUEST_PATH="$1"
|
||||
|
||||
case "$REQUEST_PATH" in
|
||||
"$METRICS_PATH")
|
||||
# Generate metrics
|
||||
METRICS_OUTPUT=$(/usr/local/bin/metrics 2>/dev/null || echo "# Error generating metrics")
|
||||
CONTENT_LENGTH=$(echo -n "$METRICS_OUTPUT" | wc -c)
|
||||
|
||||
cat << EOF
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/plain; version=0.0.4
|
||||
Content-Length: $CONTENT_LENGTH
|
||||
Cache-Control: no-cache
|
||||
Connection: close
|
||||
|
||||
$METRICS_OUTPUT
|
||||
EOF
|
||||
;;
|
||||
|
||||
"/health")
|
||||
# Health check endpoint
|
||||
HEALTH_JSON=$(/usr/local/bin/health --json 2>/dev/null || echo '{"status":"error"}')
|
||||
CONTENT_LENGTH=$(echo -n "$HEALTH_JSON" | wc -c)
|
||||
|
||||
cat << EOF
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
Content-Length: $CONTENT_LENGTH
|
||||
Cache-Control: no-cache
|
||||
Connection: close
|
||||
|
||||
$HEALTH_JSON
|
||||
EOF
|
||||
;;
|
||||
|
||||
"/")
|
||||
# Status page
|
||||
HTML_CONTENT="<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tor Relay Metrics</title>
|
||||
<style>
|
||||
body { font-family: sans-serif; margin: 40px; background: #f5f5f5; }
|
||||
h1 { color: #7d4698; }
|
||||
.status { padding: 20px; background: white; border-radius: 8px; margin: 20px 0; }
|
||||
.endpoint { background: #f0f0f0; padding: 10px; margin: 10px 0; border-radius: 4px; }
|
||||
a { color: #7d4698; text-decoration: none; }
|
||||
a:hover { text-decoration: underline; }
|
||||
.ok { color: green; }
|
||||
.loading { color: orange; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>🧅 Tor Relay Metrics Server</h1>
|
||||
<div class=\"status\">
|
||||
<h2>Available Endpoints:</h2>
|
||||
<div class=\"endpoint\">
|
||||
📊 <a href=\"$METRICS_PATH\">$METRICS_PATH</a> - Prometheus metrics
|
||||
</div>
|
||||
<div class=\"endpoint\">
|
||||
💚 <a href=\"/health\">/health</a> - Health check (JSON)
|
||||
</div>
|
||||
<div class=\"endpoint\">
|
||||
🏠 <a href=\"/\">/</a> - This status page
|
||||
</div>
|
||||
</div>
|
||||
<div class=\"status\">
|
||||
<h2>Configuration:</h2>
|
||||
<p>Port: $METRICS_PORT</p>
|
||||
<p>Bind: $METRICS_BIND</p>
|
||||
<p>Version: $VERSION</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>"
|
||||
|
||||
CONTENT_LENGTH=$(echo -n "$HTML_CONTENT" | wc -c)
|
||||
|
||||
cat << EOF
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/html
|
||||
Content-Length: $CONTENT_LENGTH
|
||||
Cache-Control: no-cache
|
||||
Connection: close
|
||||
|
||||
$HTML_CONTENT
|
||||
EOF
|
||||
;;
|
||||
|
||||
*)
|
||||
# 404 Not Found
|
||||
ERROR_MSG="404 - Not Found"
|
||||
CONTENT_LENGTH=$(echo -n "$ERROR_MSG" | wc -c)
|
||||
|
||||
cat << EOF
|
||||
HTTP/1.1 404 Not Found
|
||||
Content-Type: text/plain
|
||||
Content-Length: $CONTENT_LENGTH
|
||||
Connection: close
|
||||
|
||||
$ERROR_MSG
|
||||
EOF
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Signal handler for clean shutdown
|
||||
cleanup() {
|
||||
echo ""
|
||||
echo "🛑 Shutting down metrics HTTP server..."
|
||||
exit 0
|
||||
}
|
||||
|
||||
trap cleanup INT TERM
|
||||
|
||||
# Start server
|
||||
echo "📊 Starting Tor Relay Metrics HTTP Server v${VERSION}"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🌐 Listening on: http://$METRICS_BIND:$METRICS_PORT"
|
||||
echo "📍 Metrics path: $METRICS_PATH"
|
||||
echo "💡 Press Ctrl+C to stop"
|
||||
echo ""
|
||||
|
||||
# Main server loop
|
||||
if [ "$DAEMON_MODE" = "true" ]; then
|
||||
# Run in background
|
||||
while true; do
|
||||
echo -e "$(generate_response "$METRICS_PATH")" | nc -l -p "$METRICS_PORT" -w "$RESPONSE_TIMEOUT" > /dev/null 2>&1 &
|
||||
wait
|
||||
done &
|
||||
echo "✅ Server running in daemon mode (PID: $!)"
|
||||
else
|
||||
# Run in foreground
|
||||
while true; do
|
||||
# Wait for connection and parse request
|
||||
REQUEST=$(echo -e "HTTP/1.1 200 OK\r\n\r\n" | nc -l -p "$METRICS_PORT" -w "$RESPONSE_TIMEOUT" 2>/dev/null | head -1)
|
||||
|
||||
if [ -n "$REQUEST" ]; then
|
||||
# Extract path from request
|
||||
REQUEST_PATH=$(echo "$REQUEST" | awk '{print $2}')
|
||||
|
||||
# Log request
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $REQUEST"
|
||||
|
||||
# Generate and send response
|
||||
generate_response "$REQUEST_PATH" | nc -l -p "$METRICS_PORT" -w 1 > /dev/null 2>&1 &
|
||||
fi
|
||||
done
|
||||
fi
|
||||
216
tools/net-check
Normal file
216
tools/net-check
Normal file
@@ -0,0 +1,216 @@
|
||||
#!/bin/sh
|
||||
# net-check - Comprehensive network diagnostics for Tor relay (curl-only edition)
|
||||
# Usage: docker exec guard-relay net-check [--json|--plain|--quick|--full|--help]
|
||||
|
||||
set -e
|
||||
|
||||
VERSION="1.1.0"
|
||||
OUTPUT_FORMAT="text"
|
||||
CHECK_IPV4="true"
|
||||
CHECK_IPV6="true"
|
||||
CHECK_DNS="true"
|
||||
CHECK_CONSENSUS="true"
|
||||
CHECK_PORTS="true"
|
||||
DNS_SERVERS="1.1.1.1 8.8.8.8 9.9.9.9"
|
||||
TEST_TIMEOUT="5"
|
||||
|
||||
# Parse arguments
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--help|-h)
|
||||
cat << EOF
|
||||
🌐 Tor-Guard-Relay Network Diagnostics v${VERSION}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
USAGE:
|
||||
net-check [--json|--plain|--quick|--full|--help]
|
||||
|
||||
OPTIONS:
|
||||
--json Output JSON format
|
||||
--plain Minimal output for scripts
|
||||
--text Formatted output (default)
|
||||
--quick Skip extended tests
|
||||
--full Run all tests (default)
|
||||
--help Show this help message
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
--json) OUTPUT_FORMAT="json" ;;
|
||||
--plain) OUTPUT_FORMAT="plain" ;;
|
||||
--text) OUTPUT_FORMAT="text" ;;
|
||||
--quick)
|
||||
CHECK_CONSENSUS="false"
|
||||
CHECK_PORTS="false"
|
||||
;;
|
||||
--full)
|
||||
CHECK_IPV4="true"
|
||||
CHECK_IPV6="true"
|
||||
CHECK_DNS="true"
|
||||
CHECK_CONSENSUS="true"
|
||||
CHECK_PORTS="true"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Defaults
|
||||
IPV4_STATUS="unknown"
|
||||
IPV6_STATUS="unknown"
|
||||
DNS_STATUS="unknown"
|
||||
CONSENSUS_STATUS="unknown"
|
||||
PORT_STATUS="unknown"
|
||||
PUBLIC_IP=""
|
||||
PUBLIC_IP6=""
|
||||
FAILED_TESTS=0
|
||||
TOTAL_TESTS=0
|
||||
|
||||
command_exists() { command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
# IPv4 check
|
||||
check_ipv4() {
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
if [ "$CHECK_IPV4" = "true" ] && command_exists curl; then
|
||||
PUBLIC_IP=$(curl -4 -fsS --max-time "$TEST_TIMEOUT" https://ipv4.icanhazip.com 2>/dev/null | tr -d '\r')
|
||||
if [ -n "$PUBLIC_IP" ]; then
|
||||
IPV4_STATUS="ok"
|
||||
else
|
||||
IPV4_STATUS="failed"; FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
else
|
||||
IPV4_STATUS="skipped"
|
||||
fi
|
||||
}
|
||||
|
||||
# IPv6 check
|
||||
check_ipv6() {
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
if [ "$CHECK_IPV6" = "true" ] && command_exists curl; then
|
||||
PUBLIC_IP6=$(curl -6 -fsS --max-time "$TEST_TIMEOUT" https://ipv6.icanhazip.com 2>/dev/null | tr -d '\r')
|
||||
if [ -n "$PUBLIC_IP6" ]; then
|
||||
IPV6_STATUS="ok"
|
||||
else
|
||||
IPV6_STATUS="not_available"
|
||||
fi
|
||||
else
|
||||
IPV6_STATUS="skipped"
|
||||
fi
|
||||
}
|
||||
|
||||
# DNS resolution check
|
||||
check_dns() {
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
DNS_WORKING=false
|
||||
if [ "$CHECK_DNS" = "true" ]; then
|
||||
for dns_server in $DNS_SERVERS; do
|
||||
if command_exists nslookup; then
|
||||
if nslookup torproject.org "$dns_server" >/dev/null 2>&1; then DNS_WORKING=true; break; fi
|
||||
elif command_exists dig; then
|
||||
if dig @"$dns_server" torproject.org +short +time="$TEST_TIMEOUT" >/dev/null 2>&1; then DNS_WORKING=true; break; fi
|
||||
elif command_exists host; then
|
||||
if host -t A torproject.org "$dns_server" >/dev/null 2>&1; then DNS_WORKING=true; break; fi
|
||||
fi
|
||||
done
|
||||
[ "$DNS_WORKING" = "true" ] && DNS_STATUS="ok" || { DNS_STATUS="failed"; FAILED_TESTS=$((FAILED_TESTS + 1)); }
|
||||
else
|
||||
DNS_STATUS="skipped"
|
||||
fi
|
||||
}
|
||||
|
||||
# Tor network reachability
|
||||
check_consensus() {
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
if [ "$CHECK_CONSENSUS" = "true" ] && command_exists curl; then
|
||||
if curl -fsS --max-time "$TEST_TIMEOUT" https://collector.torproject.org/index.json | grep -q "metrics"; then
|
||||
CONSENSUS_STATUS="ok"
|
||||
else
|
||||
CONSENSUS_STATUS="failed"; FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
else
|
||||
CONSENSUS_STATUS="skipped"
|
||||
fi
|
||||
}
|
||||
|
||||
# Port accessibility (optional)
|
||||
check_ports() {
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
if [ "$CHECK_PORTS" = "true" ]; then
|
||||
if ! command_exists nc; then PORT_STATUS="skipped"; return; fi
|
||||
if [ -f /etc/tor/torrc ]; then
|
||||
ORPORT=$(grep -E "^ORPort" /etc/tor/torrc 2>/dev/null | awk '{print $2}' | head -1)
|
||||
if [ -n "$ORPORT" ] && [ -n "$PUBLIC_IP" ]; then
|
||||
if nc -z -w "$TEST_TIMEOUT" "$PUBLIC_IP" "$ORPORT" >/dev/null 2>&1; then
|
||||
PORT_STATUS="ok"
|
||||
else
|
||||
PORT_STATUS="closed"; FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
else
|
||||
PORT_STATUS="not_configured"
|
||||
fi
|
||||
else
|
||||
PORT_STATUS="not_configured"
|
||||
fi
|
||||
else
|
||||
PORT_STATUS="skipped"
|
||||
fi
|
||||
}
|
||||
|
||||
# Run all
|
||||
check_ipv4
|
||||
check_ipv6
|
||||
check_dns
|
||||
check_consensus
|
||||
check_ports
|
||||
|
||||
TOTAL_PASSED=$((TOTAL_TESTS - FAILED_TESTS))
|
||||
SUCCESS_RATE=$((TOTAL_PASSED * 100 / TOTAL_TESTS))
|
||||
TIMESTAMP=$(date -u '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# Output
|
||||
case "$OUTPUT_FORMAT" in
|
||||
json)
|
||||
cat <<EOF
|
||||
{
|
||||
"timestamp": "$TIMESTAMP",
|
||||
"success_rate": $SUCCESS_RATE,
|
||||
"tests": { "total": $TOTAL_TESTS, "failed": $FAILED_TESTS },
|
||||
"ipv4": { "status": "$IPV4_STATUS", "address": "$PUBLIC_IP" },
|
||||
"ipv6": { "status": "$IPV6_STATUS", "address": "$PUBLIC_IP6" },
|
||||
"dns": { "status": "$DNS_STATUS", "servers": "$DNS_SERVERS" },
|
||||
"consensus": { "status": "$CONSENSUS_STATUS" },
|
||||
"ports": { "status": "$PORT_STATUS" }
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
plain)
|
||||
echo "TIMESTAMP=$TIMESTAMP"
|
||||
echo "SUCCESS_RATE=$SUCCESS_RATE"
|
||||
echo "IPV4_STATUS=$IPV4_STATUS"
|
||||
echo "IPV4_ADDRESS=$PUBLIC_IP"
|
||||
echo "IPV6_STATUS=$IPV6_STATUS"
|
||||
echo "IPV6_ADDRESS=$PUBLIC_IP6"
|
||||
echo "DNS_STATUS=$DNS_STATUS"
|
||||
echo "CONSENSUS_STATUS=$CONSENSUS_STATUS"
|
||||
echo "PORT_STATUS=$PORT_STATUS"
|
||||
;;
|
||||
*)
|
||||
echo "🌐 Network Diagnostics v$VERSION"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
if [ "$FAILED_TESTS" -eq 0 ]; then
|
||||
echo "📊 Overall: ✅ All checks passed ($SUCCESS_RATE%)"
|
||||
elif [ "$FAILED_TESTS" -lt "$TOTAL_TESTS" ]; then
|
||||
echo "📊 Overall: ⚠️ Some issues detected ($SUCCESS_RATE% passed)"
|
||||
else
|
||||
echo "📊 Overall: ❌ Multiple failures ($SUCCESS_RATE% passed)"
|
||||
fi
|
||||
echo ""
|
||||
echo "🔌 IPv4: $IPV4_STATUS ${PUBLIC_IP:+($PUBLIC_IP)}"
|
||||
echo "🔌 IPv6: $IPV6_STATUS ${PUBLIC_IP6:+($PUBLIC_IP6)}"
|
||||
echo "🔍 DNS: $DNS_STATUS"
|
||||
echo "📋 Consensus: $CONSENSUS_STATUS"
|
||||
echo "🚪 Ports: $PORT_STATUS"
|
||||
echo ""
|
||||
echo "🕒 Tested at: $TIMESTAMP"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
;;
|
||||
esac
|
||||
|
||||
[ "$FAILED_TESTS" -eq 0 ] && exit 0 || exit 1
|
||||
@@ -1,105 +0,0 @@
|
||||
#!/bin/sh
|
||||
# relay-status - Comprehensive relay health check
|
||||
# Usage: docker exec guard-relay relay-status
|
||||
|
||||
echo "🧅 Tor Relay Status Report"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# Build Info
|
||||
echo "📦 Build Information:"
|
||||
if [ -f /build-info.txt ]; then
|
||||
cat /build-info.txt | sed 's/^/ /'
|
||||
else
|
||||
echo " Version: Unknown"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Bootstrap Progress
|
||||
echo "🚀 Bootstrap Progress:"
|
||||
if [ -f /var/log/tor/notices.log ]; then
|
||||
BOOTSTRAP=$(grep "Bootstrapped" /var/log/tor/notices.log | tail -5)
|
||||
if [ -n "$BOOTSTRAP" ]; then
|
||||
echo "$BOOTSTRAP" | sed 's/^/ /'
|
||||
if echo "$BOOTSTRAP" | grep -q "Bootstrapped 100%"; then
|
||||
echo " ✅ Relay is fully bootstrapped!"
|
||||
fi
|
||||
else
|
||||
echo " ⏳ No bootstrap messages yet. Tor is starting..."
|
||||
fi
|
||||
else
|
||||
echo " ⚠️ Log file not found."
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Reachability Status
|
||||
echo "🌍 Reachability Status:"
|
||||
if [ -f /var/log/tor/notices.log ]; then
|
||||
REACHABLE=$(grep -iE "reachable|self-testing" /var/log/tor/notices.log | tail -3)
|
||||
if [ -n "$REACHABLE" ]; then
|
||||
echo "$REACHABLE" | sed 's/^/ /'
|
||||
if echo "$REACHABLE" | grep -q "reachable from the outside"; then
|
||||
echo " ✅ ORPort is reachable!"
|
||||
fi
|
||||
else
|
||||
echo " ⏳ No reachability test results yet."
|
||||
fi
|
||||
else
|
||||
echo " ⚠️ Log file not found."
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Fingerprint
|
||||
echo "🔑 Relay Fingerprint:"
|
||||
if [ -f /var/lib/tor/fingerprint ]; then
|
||||
cat /var/lib/tor/fingerprint | sed 's/^/ /'
|
||||
else
|
||||
echo " ⏳ Fingerprint not generated yet."
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ORPort Configuration
|
||||
echo "🔌 ORPort Configuration:"
|
||||
if [ -f /etc/tor/torrc ]; then
|
||||
ORPORT=$(grep -iE "^(ORPort|DirPort|ObfsPort)" /etc/tor/torrc)
|
||||
if [ -n "$ORPORT" ]; then
|
||||
echo "$ORPORT" | sed 's/^/ /'
|
||||
else
|
||||
echo " ⚠️ No ORPort configuration found."
|
||||
fi
|
||||
else
|
||||
echo " ⚠️ Configuration file not found."
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Relay Information
|
||||
echo "📝 Relay Information:"
|
||||
if [ -f /etc/tor/torrc ]; then
|
||||
RELAY_INFO=$(grep -iE "^(Nickname|ContactInfo|ExitRelay|BridgeRelay)" /etc/tor/torrc)
|
||||
if [ -n "$RELAY_INFO" ]; then
|
||||
echo "$RELAY_INFO" | sed 's/^/ /'
|
||||
else
|
||||
echo " ⚠️ No relay information found."
|
||||
fi
|
||||
else
|
||||
echo " ⚠️ Configuration file not found."
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Recent Errors
|
||||
echo "⚠️ Recent Errors/Warnings:"
|
||||
if [ -f /var/log/tor/notices.log ]; then
|
||||
ERRORS=$(grep -iE "(error|warn|critical)" /var/log/tor/notices.log | tail -5)
|
||||
if [ -n "$ERRORS" ]; then
|
||||
echo "$ERRORS" | sed 's/^/ /'
|
||||
else
|
||||
echo " ✅ No recent errors or warnings."
|
||||
fi
|
||||
else
|
||||
echo " ⚠️ Log file not found."
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "💡 For live monitoring: docker logs -f <container-name>"
|
||||
echo "🔗 Search your relay: https://metrics.torproject.org/rs.html"
|
||||
388
tools/setup
Normal file
388
tools/setup
Normal file
@@ -0,0 +1,388 @@
|
||||
#!/bin/sh
|
||||
# setup - Interactive configuration wizard for Tor relay
|
||||
# Usage: setup [--auto|--help]
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
VERSION="1.1.0"
|
||||
CONFIG_FILE="${CONFIG_FILE:-/etc/tor/torrc}"
|
||||
RELAY_TYPE="${RELAY_TYPE:-guard}"
|
||||
AUTO_MODE="${AUTO_MODE:-false}"
|
||||
DEFAULT_NICKNAME="${DEFAULT_NICKNAME:-MyTorRelay}"
|
||||
DEFAULT_CONTACT="${DEFAULT_CONTACT:-admin@example.com}"
|
||||
DEFAULT_ORPORT="${DEFAULT_ORPORT:-9001}"
|
||||
DEFAULT_DIRPORT="${DEFAULT_DIRPORT:-9030}"
|
||||
DEFAULT_BANDWIDTH="${DEFAULT_BANDWIDTH:-1024}"
|
||||
|
||||
# Colors for terminal output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
PURPLE='\033[0;35m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Parse arguments
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--help|-h)
|
||||
cat << EOF
|
||||
🧙 Tor-Guard-Relay Setup Wizard v${VERSION}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
USAGE:
|
||||
setup [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--auto Use defaults for all prompts
|
||||
--config FILE Config file path (default: /etc/tor/torrc)
|
||||
--type TYPE Relay type: guard|exit|bridge
|
||||
--help, -h Show this help message
|
||||
|
||||
ENVIRONMENT VARIABLES:
|
||||
CONFIG_FILE Path to torrc file
|
||||
RELAY_TYPE Type of relay (guard/exit/bridge)
|
||||
DEFAULT_NICKNAME Default nickname
|
||||
DEFAULT_CONTACT Default contact info
|
||||
DEFAULT_ORPORT Default ORPort
|
||||
DEFAULT_DIRPORT Default DirPort
|
||||
DEFAULT_BANDWIDTH Default bandwidth in KB/s
|
||||
AUTO_MODE Skip prompts and use defaults
|
||||
|
||||
RELAY TYPES:
|
||||
guard Middle/Guard relay (recommended for beginners)
|
||||
exit Exit relay (requires careful consideration)
|
||||
bridge Bridge relay (helps censored users)
|
||||
|
||||
WIZARD STEPS:
|
||||
1. Relay nickname selection
|
||||
2. Contact information
|
||||
3. Port configuration (ORPort/DirPort)
|
||||
4. Bandwidth limits
|
||||
5. Relay type selection
|
||||
6. Configuration validation
|
||||
|
||||
EXAMPLES:
|
||||
setup # Interactive wizard
|
||||
setup --auto # Auto-configure with defaults
|
||||
setup --type bridge # Configure as bridge relay
|
||||
|
||||
OUTPUT:
|
||||
Creates a complete torrc configuration file ready
|
||||
for production use with all required settings.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
--auto) AUTO_MODE="true" ;;
|
||||
--config)
|
||||
shift
|
||||
CONFIG_FILE="$1"
|
||||
shift
|
||||
;;
|
||||
--type)
|
||||
shift
|
||||
RELAY_TYPE="$1"
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
echo "❌ Unknown option: $arg"
|
||||
echo "💡 Use --help for usage information"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Helper functions
|
||||
print_header() {
|
||||
echo ""
|
||||
echo -e "${PURPLE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${BLUE}$1${NC}"
|
||||
echo -e "${PURPLE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
print_step() {
|
||||
echo -e "${GREEN}[$1/6]${NC} $2"
|
||||
}
|
||||
|
||||
validate_nickname() {
|
||||
echo "$1" | grep -qE "^[a-zA-Z0-9]{1,19}$"
|
||||
}
|
||||
|
||||
validate_email() {
|
||||
echo "$1" | grep -qE "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
|
||||
}
|
||||
|
||||
validate_port() {
|
||||
[ "$1" -ge 1 ] && [ "$1" -le 65535 ] 2>/dev/null
|
||||
}
|
||||
|
||||
validate_bandwidth() {
|
||||
[ "$1" -ge 256 ] 2>/dev/null
|
||||
}
|
||||
|
||||
# Main setup wizard
|
||||
clear
|
||||
cat << EOF
|
||||
${PURPLE}╔══════════════════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ 🧅 Tor-Guard-Relay Setup Wizard v${VERSION} ║
|
||||
║ ║
|
||||
║ Configure your Tor relay in 6 steps ║
|
||||
║ ║
|
||||
╚══════════════════════════════════════════════════════════╝${NC}
|
||||
|
||||
This wizard will help you configure a Tor relay with optimal
|
||||
settings for your network and preferences.
|
||||
|
||||
Press Ctrl+C at any time to cancel.
|
||||
EOF
|
||||
|
||||
# Step 1: Nickname
|
||||
print_header "Step 1: Relay Nickname"
|
||||
print_step "1" "Choose a nickname for your relay"
|
||||
echo ""
|
||||
echo "Requirements:"
|
||||
echo " • 1-19 characters"
|
||||
echo " • Only letters and numbers"
|
||||
echo " • No spaces or special characters"
|
||||
echo ""
|
||||
|
||||
if [ "$AUTO_MODE" = "true" ]; then
|
||||
NICKNAME="$DEFAULT_NICKNAME"
|
||||
echo -e "${GREEN}✓${NC} Using default: $NICKNAME"
|
||||
else
|
||||
while true; do
|
||||
printf "Enter nickname [${DEFAULT_NICKNAME}]: "
|
||||
read NICKNAME
|
||||
[ -z "$NICKNAME" ] && NICKNAME="$DEFAULT_NICKNAME"
|
||||
|
||||
if validate_nickname "$NICKNAME"; then
|
||||
echo -e "${GREEN}✓${NC} Nickname accepted: $NICKNAME"
|
||||
break
|
||||
else
|
||||
echo -e "${RED}✗${NC} Invalid nickname. Please try again."
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Step 2: Contact Info
|
||||
print_header "Step 2: Contact Information"
|
||||
print_step "2" "Provide contact information (public)"
|
||||
echo ""
|
||||
echo "This helps the Tor Project contact you if needed."
|
||||
echo "Use an email address you're comfortable making public."
|
||||
echo ""
|
||||
|
||||
if [ "$AUTO_MODE" = "true" ]; then
|
||||
CONTACT="$DEFAULT_CONTACT"
|
||||
echo -e "${GREEN}✓${NC} Using default: $CONTACT"
|
||||
else
|
||||
while true; do
|
||||
printf "Enter email [${DEFAULT_CONTACT}]: "
|
||||
read CONTACT
|
||||
[ -z "$CONTACT" ] && CONTACT="$DEFAULT_CONTACT"
|
||||
|
||||
if validate_email "$CONTACT"; then
|
||||
echo -e "${GREEN}✓${NC} Contact accepted: $CONTACT"
|
||||
break
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} Invalid email format, but continuing..."
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Step 3: Port Configuration
|
||||
print_header "Step 3: Port Configuration"
|
||||
print_step "3" "Configure network ports"
|
||||
echo ""
|
||||
echo "ORPort: Main port for Tor traffic (must be accessible)"
|
||||
echo "DirPort: Directory service port (optional)"
|
||||
echo ""
|
||||
|
||||
if [ "$AUTO_MODE" = "true" ]; then
|
||||
ORPORT="$DEFAULT_ORPORT"
|
||||
DIRPORT="$DEFAULT_DIRPORT"
|
||||
echo -e "${GREEN}✓${NC} Using defaults: ORPort=$ORPORT, DirPort=$DIRPORT"
|
||||
else
|
||||
while true; do
|
||||
printf "Enter ORPort [${DEFAULT_ORPORT}]: "
|
||||
read ORPORT
|
||||
[ -z "$ORPORT" ] && ORPORT="$DEFAULT_ORPORT"
|
||||
|
||||
if validate_port "$ORPORT"; then
|
||||
echo -e "${GREEN}✓${NC} ORPort accepted: $ORPORT"
|
||||
break
|
||||
else
|
||||
echo -e "${RED}✗${NC} Invalid port (1-65535). Please try again."
|
||||
fi
|
||||
done
|
||||
|
||||
while true; do
|
||||
printf "Enter DirPort [${DEFAULT_DIRPORT}]: "
|
||||
read DIRPORT
|
||||
[ -z "$DIRPORT" ] && DIRPORT="$DEFAULT_DIRPORT"
|
||||
|
||||
if validate_port "$DIRPORT"; then
|
||||
echo -e "${GREEN}✓${NC} DirPort accepted: $DIRPORT"
|
||||
break
|
||||
else
|
||||
echo -e "${RED}✗${NC} Invalid port (1-65535). Please try again."
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Step 4: Bandwidth Limits
|
||||
print_header "Step 4: Bandwidth Allocation"
|
||||
print_step "4" "Set bandwidth limits (KB/s)"
|
||||
echo ""
|
||||
echo "Recommended minimums:"
|
||||
echo " • Guard relay: 1024 KB/s (1 MB/s)"
|
||||
echo " • Exit relay: 2048 KB/s (2 MB/s)"
|
||||
echo " • Bridge: 512 KB/s"
|
||||
echo ""
|
||||
|
||||
if [ "$AUTO_MODE" = "true" ]; then
|
||||
BANDWIDTH="$DEFAULT_BANDWIDTH"
|
||||
echo -e "${GREEN}✓${NC} Using default: $BANDWIDTH KB/s"
|
||||
else
|
||||
while true; do
|
||||
printf "Enter bandwidth limit in KB/s [${DEFAULT_BANDWIDTH}]: "
|
||||
read BANDWIDTH
|
||||
[ -z "$BANDWIDTH" ] && BANDWIDTH="$DEFAULT_BANDWIDTH"
|
||||
|
||||
if validate_bandwidth "$BANDWIDTH"; then
|
||||
echo -e "${GREEN}✓${NC} Bandwidth accepted: $BANDWIDTH KB/s"
|
||||
break
|
||||
else
|
||||
echo -e "${RED}✗${NC} Minimum bandwidth is 256 KB/s. Please try again."
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Step 5: Relay Type
|
||||
print_header "Step 5: Relay Type Selection"
|
||||
print_step "5" "Choose relay type"
|
||||
echo ""
|
||||
echo "Available types:"
|
||||
echo " ${GREEN}guard${NC} - Middle/Guard relay (recommended)"
|
||||
echo " ${YELLOW}exit${NC} - Exit relay (requires careful consideration)"
|
||||
echo " ${BLUE}bridge${NC} - Bridge relay (helps censored users)"
|
||||
echo ""
|
||||
|
||||
if [ "$AUTO_MODE" = "true" ]; then
|
||||
echo -e "${GREEN}✓${NC} Using default type: $RELAY_TYPE"
|
||||
else
|
||||
printf "Enter relay type [guard/exit/bridge] [${RELAY_TYPE}]: "
|
||||
read TYPE_INPUT
|
||||
[ -n "$TYPE_INPUT" ] && RELAY_TYPE="$TYPE_INPUT"
|
||||
|
||||
case "$RELAY_TYPE" in
|
||||
guard|exit|bridge)
|
||||
echo -e "${GREEN}✓${NC} Relay type: $RELAY_TYPE"
|
||||
;;
|
||||
*)
|
||||
echo -e "${YELLOW}⚠${NC} Unknown type, defaulting to guard"
|
||||
RELAY_TYPE="guard"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Step 6: Generate Configuration
|
||||
print_header "Step 6: Generating Configuration"
|
||||
print_step "6" "Creating torrc file"
|
||||
echo ""
|
||||
|
||||
# Create configuration
|
||||
cat > "$CONFIG_FILE" << EOF
|
||||
# Tor Relay Configuration
|
||||
# Generated by Tor-Guard-Relay Setup Wizard v${VERSION}
|
||||
# Date: $(date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# Basic Information
|
||||
Nickname $NICKNAME
|
||||
ContactInfo $CONTACT
|
||||
|
||||
# Network Settings
|
||||
ORPort $ORPORT
|
||||
DirPort $DIRPORT
|
||||
|
||||
# Bandwidth Limits
|
||||
RelayBandwidthRate $BANDWIDTH KB
|
||||
RelayBandwidthBurst $((BANDWIDTH * 2)) KB
|
||||
|
||||
# Relay Type Configuration
|
||||
EOF
|
||||
|
||||
case "$RELAY_TYPE" in
|
||||
exit)
|
||||
cat >> "$CONFIG_FILE" << EOF
|
||||
ExitRelay 1
|
||||
ExitPolicy accept *:80 # HTTP
|
||||
ExitPolicy accept *:443 # HTTPS
|
||||
ExitPolicy accept *:6667 # IRC
|
||||
ExitPolicy accept *:22 # SSH
|
||||
ExitPolicy reject *:* # Reject everything else
|
||||
EOF
|
||||
;;
|
||||
bridge)
|
||||
cat >> "$CONFIG_FILE" << EOF
|
||||
BridgeRelay 1
|
||||
PublishServerDescriptor bridge
|
||||
ExitRelay 0
|
||||
EOF
|
||||
;;
|
||||
*)
|
||||
cat >> "$CONFIG_FILE" << EOF
|
||||
ExitRelay 0
|
||||
ExitPolicy reject *:*
|
||||
EOF
|
||||
;;
|
||||
esac
|
||||
|
||||
# Add common settings
|
||||
cat >> "$CONFIG_FILE" << EOF
|
||||
|
||||
# Logging
|
||||
Log notice file /var/log/tor/notices.log
|
||||
Log notice syslog
|
||||
|
||||
# Data Directory
|
||||
DataDirectory /var/lib/tor
|
||||
|
||||
# Performance
|
||||
NumCPUs 0 # Use all available CPUs
|
||||
HardwareAccel 1
|
||||
|
||||
# Security
|
||||
NoExec 1
|
||||
EOF
|
||||
|
||||
# Validate configuration
|
||||
echo -e "${GREEN}✓${NC} Configuration generated successfully!"
|
||||
echo ""
|
||||
echo "📋 Configuration Summary:"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " Nickname: $NICKNAME"
|
||||
echo " Contact: $CONTACT"
|
||||
echo " ORPort: $ORPORT"
|
||||
echo " DirPort: $DIRPORT"
|
||||
echo " Bandwidth: $BANDWIDTH KB/s"
|
||||
echo " Type: $RELAY_TYPE"
|
||||
echo " Config: $CONFIG_FILE"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo -e "${GREEN}✅ Setup complete!${NC}"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Review the configuration: cat $CONFIG_FILE"
|
||||
echo " 2. Start your relay: tor -f $CONFIG_FILE"
|
||||
echo " 3. Monitor status: /usr/local/bin/status"
|
||||
echo " 4. Check metrics: /usr/local/bin/metrics"
|
||||
echo ""
|
||||
echo "🌐 Your relay will appear on Tor Metrics after ~3 hours:"
|
||||
echo " https://metrics.torproject.org/rs.html"
|
||||
333
tools/status
Normal file
333
tools/status
Normal file
@@ -0,0 +1,333 @@
|
||||
#!/bin/sh
|
||||
# status - Comprehensive relay status dashboard
|
||||
# Usage: docker exec guard-relay status [--json|--help]
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
VERSION="1.1.0"
|
||||
OUTPUT_FORMAT="${OUTPUT_FORMAT:-text}"
|
||||
SHOW_ALL="${SHOW_ALL:-true}"
|
||||
CHECK_NETWORK="${CHECK_NETWORK:-true}"
|
||||
|
||||
# Parse arguments
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--help|-h)
|
||||
cat << EOF
|
||||
🧅 Tor-Guard-Relay Status Dashboard v${VERSION}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
USAGE:
|
||||
status [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--json Output in JSON format
|
||||
--plain Plain text output
|
||||
--quick Quick status check (skip network tests)
|
||||
--full Full status report (default)
|
||||
--help, -h Show this help message
|
||||
|
||||
ENVIRONMENT VARIABLES:
|
||||
OUTPUT_FORMAT Output format (text/json/plain)
|
||||
SHOW_ALL Show all sections (true/false)
|
||||
CHECK_NETWORK Include network checks (true/false)
|
||||
|
||||
SECTIONS:
|
||||
• Build Information
|
||||
• Bootstrap Progress
|
||||
• Reachability Status
|
||||
• Relay Identity
|
||||
• Network Configuration
|
||||
• Performance Metrics
|
||||
• Recent Activity
|
||||
• Error Summary
|
||||
|
||||
EXAMPLES:
|
||||
status # Full status report
|
||||
status --json # JSON output for monitoring
|
||||
status --quick # Quick status without network
|
||||
status --plain # Machine-readable format
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
--json) OUTPUT_FORMAT="json" ;;
|
||||
--plain) OUTPUT_FORMAT="plain" ;;
|
||||
--quick) CHECK_NETWORK="false" ;;
|
||||
--full)
|
||||
SHOW_ALL="true"
|
||||
CHECK_NETWORK="true"
|
||||
;;
|
||||
-*)
|
||||
echo "❌ Unknown option: $arg"
|
||||
echo "💡 Use --help for usage information"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Gather all status information
|
||||
gather_status() {
|
||||
# Process status
|
||||
IS_RUNNING="false"
|
||||
if pgrep -x tor > /dev/null 2>&1; then
|
||||
IS_RUNNING="true"
|
||||
PID=$(pgrep -x tor | head -1)
|
||||
UPTIME=$(ps -o etime= -p "$PID" 2>/dev/null | tr -d ' ' || echo "0")
|
||||
fi
|
||||
|
||||
# Bootstrap status
|
||||
BOOTSTRAP_PERCENT=0
|
||||
BOOTSTRAP_MESSAGE=""
|
||||
if [ -f /var/log/tor/notices.log ]; then
|
||||
BOOTSTRAP_LINE=$(grep "Bootstrapped" /var/log/tor/notices.log 2>/dev/null | tail -1)
|
||||
if [ -n "$BOOTSTRAP_LINE" ]; then
|
||||
BOOTSTRAP_PERCENT=$(echo "$BOOTSTRAP_LINE" | grep -oE '[0-9]+%' | tr -d '%' | tail -1)
|
||||
BOOTSTRAP_MESSAGE=$(echo "$BOOTSTRAP_LINE" | sed 's/.*Bootstrapped [0-9]*%[: ]*//')
|
||||
fi
|
||||
fi
|
||||
|
||||
# Reachability
|
||||
IS_REACHABLE="false"
|
||||
REACHABILITY_MESSAGE=""
|
||||
if [ -f /var/log/tor/notices.log ]; then
|
||||
REACHABLE_LINE=$(grep -E "reachable|self-testing" /var/log/tor/notices.log 2>/dev/null | tail -1)
|
||||
if echo "$REACHABLE_LINE" | grep -q "reachable from the outside"; then
|
||||
IS_REACHABLE="true"
|
||||
REACHABILITY_MESSAGE="ORPort is reachable from the outside"
|
||||
elif [ -n "$REACHABLE_LINE" ]; then
|
||||
REACHABILITY_MESSAGE=$(echo "$REACHABLE_LINE" | sed 's/.*] //')
|
||||
fi
|
||||
fi
|
||||
|
||||
# Relay identity
|
||||
NICKNAME=""
|
||||
FINGERPRINT=""
|
||||
if [ -f /var/lib/tor/fingerprint ]; then
|
||||
NICKNAME=$(awk '{print $1}' /var/lib/tor/fingerprint 2>/dev/null)
|
||||
FINGERPRINT=$(awk '{print $2}' /var/lib/tor/fingerprint 2>/dev/null)
|
||||
fi
|
||||
|
||||
# Configuration
|
||||
ORPORT=""
|
||||
DIRPORT=""
|
||||
EXIT_RELAY="false"
|
||||
BRIDGE_RELAY="false"
|
||||
BANDWIDTH_RATE=""
|
||||
if [ -f /etc/tor/torrc ]; then
|
||||
ORPORT=$(grep -E "^ORPort" /etc/tor/torrc 2>/dev/null | awk '{print $2}' | head -1)
|
||||
DIRPORT=$(grep -E "^DirPort" /etc/tor/torrc 2>/dev/null | awk '{print $2}' | head -1)
|
||||
grep -qE "^ExitRelay\s+1" /etc/tor/torrc 2>/dev/null && EXIT_RELAY="true"
|
||||
grep -qE "^BridgeRelay\s+1" /etc/tor/torrc 2>/dev/null && BRIDGE_RELAY="true"
|
||||
BANDWIDTH_RATE=$(grep -E "^RelayBandwidthRate" /etc/tor/torrc 2>/dev/null | awk '{print $2,$3}')
|
||||
fi
|
||||
|
||||
# Errors and warnings
|
||||
ERROR_COUNT=0
|
||||
WARNING_COUNT=0
|
||||
RECENT_ERRORS=""
|
||||
if [ -f /var/log/tor/notices.log ]; then
|
||||
ERROR_COUNT=$(grep -cE "\[err\]|\[error\]" /var/log/tor/notices.log 2>/dev/null || echo 0)
|
||||
WARNING_COUNT=$(grep -cE "\[warn\]|\[warning\]" /var/log/tor/notices.log 2>/dev/null || echo 0)
|
||||
RECENT_ERRORS=$(grep -E "\[err\]|\[error\]" /var/log/tor/notices.log 2>/dev/null | tail -3)
|
||||
fi
|
||||
|
||||
# Version info
|
||||
VERSION_INFO=""
|
||||
BUILD_TIME=""
|
||||
if [ -f /build-info.txt ]; then
|
||||
VERSION_INFO=$(grep "Version:" /build-info.txt 2>/dev/null | cut -d: -f2- | tr -d ' ')
|
||||
BUILD_TIME=$(grep "Built:" /build-info.txt 2>/dev/null | cut -d: -f2- | tr -d ' ')
|
||||
fi
|
||||
|
||||
# Network info (if enabled)
|
||||
PUBLIC_IP=""
|
||||
if [ "$CHECK_NETWORK" = "true" ] && command -v curl > /dev/null 2>&1; then
|
||||
PUBLIC_IP=$(curl -s --max-time 5 https://api.ipify.org 2>/dev/null || echo "")
|
||||
fi
|
||||
}
|
||||
|
||||
# Gather all information
|
||||
gather_status
|
||||
|
||||
# Generate timestamp
|
||||
TIMESTAMP=$(date -u '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# Determine overall status
|
||||
if [ "$IS_RUNNING" = "false" ]; then
|
||||
OVERALL_STATUS="down"
|
||||
elif [ "$BOOTSTRAP_PERCENT" -eq 100 ] && [ "$IS_REACHABLE" = "true" ]; then
|
||||
OVERALL_STATUS="healthy"
|
||||
elif [ "$BOOTSTRAP_PERCENT" -eq 100 ]; then
|
||||
OVERALL_STATUS="running"
|
||||
elif [ "$BOOTSTRAP_PERCENT" -gt 0 ]; then
|
||||
OVERALL_STATUS="starting"
|
||||
else
|
||||
OVERALL_STATUS="unknown"
|
||||
fi
|
||||
|
||||
# Output based on format
|
||||
case "$OUTPUT_FORMAT" in
|
||||
json)
|
||||
cat << EOF
|
||||
{
|
||||
"timestamp": "$TIMESTAMP",
|
||||
"status": "$OVERALL_STATUS",
|
||||
"process": {
|
||||
"running": $IS_RUNNING,
|
||||
"uptime": "$UPTIME"
|
||||
},
|
||||
"bootstrap": {
|
||||
"percent": $BOOTSTRAP_PERCENT,
|
||||
"message": "$BOOTSTRAP_MESSAGE"
|
||||
},
|
||||
"reachability": {
|
||||
"reachable": $IS_REACHABLE,
|
||||
"message": "$REACHABILITY_MESSAGE"
|
||||
},
|
||||
"identity": {
|
||||
"nickname": "$NICKNAME",
|
||||
"fingerprint": "$FINGERPRINT"
|
||||
},
|
||||
"configuration": {
|
||||
"orport": "$ORPORT",
|
||||
"dirport": "$DIRPORT",
|
||||
"exit_relay": $EXIT_RELAY,
|
||||
"bridge_relay": $BRIDGE_RELAY,
|
||||
"bandwidth": "$BANDWIDTH_RATE"
|
||||
},
|
||||
"network": {
|
||||
"public_ip": "$PUBLIC_IP"
|
||||
},
|
||||
"issues": {
|
||||
"errors": $ERROR_COUNT,
|
||||
"warnings": $WARNING_COUNT
|
||||
},
|
||||
"version": {
|
||||
"software": "$VERSION_INFO",
|
||||
"build_time": "$BUILD_TIME"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
;;
|
||||
|
||||
plain)
|
||||
echo "STATUS=$OVERALL_STATUS"
|
||||
echo "RUNNING=$IS_RUNNING"
|
||||
echo "UPTIME=$UPTIME"
|
||||
echo "BOOTSTRAP=$BOOTSTRAP_PERCENT"
|
||||
echo "REACHABLE=$IS_REACHABLE"
|
||||
echo "NICKNAME=$NICKNAME"
|
||||
echo "FINGERPRINT=$FINGERPRINT"
|
||||
echo "ORPORT=$ORPORT"
|
||||
echo "DIRPORT=$DIRPORT"
|
||||
echo "ERRORS=$ERROR_COUNT"
|
||||
echo "WARNINGS=$WARNING_COUNT"
|
||||
echo "PUBLIC_IP=$PUBLIC_IP"
|
||||
;;
|
||||
|
||||
*)
|
||||
# Default text format with emojis
|
||||
echo "🧅 Tor Relay Status Report"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# Overall status
|
||||
case "$OVERALL_STATUS" in
|
||||
healthy)
|
||||
echo "⭐ Overall Status: ✅ HEALTHY - Relay is fully operational"
|
||||
;;
|
||||
running)
|
||||
echo "⭐ Overall Status: 🟡 RUNNING - Awaiting reachability confirmation"
|
||||
;;
|
||||
starting)
|
||||
echo "⭐ Overall Status: 🔄 STARTING - Bootstrap in progress ($BOOTSTRAP_PERCENT%)"
|
||||
;;
|
||||
down)
|
||||
echo "⭐ Overall Status: ❌ DOWN - Tor process not running"
|
||||
;;
|
||||
*)
|
||||
echo "⭐ Overall Status: ❓ UNKNOWN"
|
||||
;;
|
||||
esac
|
||||
echo ""
|
||||
|
||||
# Build info
|
||||
if [ -n "$VERSION_INFO" ] || [ -n "$BUILD_TIME" ]; then
|
||||
echo "📦 Build Information:"
|
||||
[ -n "$VERSION_INFO" ] && echo " Version: $VERSION_INFO"
|
||||
[ -n "$BUILD_TIME" ] && echo " Built: $BUILD_TIME"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Bootstrap progress
|
||||
echo "🚀 Bootstrap Progress:"
|
||||
if [ "$BOOTSTRAP_PERCENT" -eq 100 ]; then
|
||||
echo " ✅ Fully bootstrapped (100%)"
|
||||
[ -n "$BOOTSTRAP_MESSAGE" ] && echo " Status: $BOOTSTRAP_MESSAGE"
|
||||
elif [ "$BOOTSTRAP_PERCENT" -gt 0 ]; then
|
||||
echo " 🔄 Bootstrapping: $BOOTSTRAP_PERCENT%"
|
||||
[ -n "$BOOTSTRAP_MESSAGE" ] && echo " Status: $BOOTSTRAP_MESSAGE"
|
||||
else
|
||||
echo " ⏳ Not started yet"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Reachability status
|
||||
echo "🌍 Reachability Status:"
|
||||
if [ "$IS_REACHABLE" = "true" ]; then
|
||||
echo " ✅ Relay is reachable from the outside"
|
||||
elif [ -n "$REACHABILITY_MESSAGE" ]; then
|
||||
echo " 🔄 $REACHABILITY_MESSAGE"
|
||||
else
|
||||
echo " ⏳ No reachability test results yet"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Relay identity
|
||||
if [ -n "$NICKNAME" ] || [ -n "$FINGERPRINT" ]; then
|
||||
echo "🔑 Relay Identity:"
|
||||
[ -n "$NICKNAME" ] && echo " 📝 Nickname: $NICKNAME"
|
||||
[ -n "$FINGERPRINT" ] && echo " 🆔 Fingerprint: $FINGERPRINT"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Network configuration
|
||||
echo "🔌 Network Configuration:"
|
||||
[ -n "$ORPORT" ] && echo " ORPort: $ORPORT"
|
||||
[ -n "$DIRPORT" ] && echo " DirPort: $DIRPORT"
|
||||
[ -n "$PUBLIC_IP" ] && echo " Public IP: $PUBLIC_IP"
|
||||
[ -n "$BANDWIDTH_RATE" ] && echo " Bandwidth: $BANDWIDTH_RATE"
|
||||
|
||||
if [ "$EXIT_RELAY" = "true" ]; then
|
||||
echo " Type: 🚪 Exit Relay"
|
||||
elif [ "$BRIDGE_RELAY" = "true" ]; then
|
||||
echo " Type: 🌉 Bridge Relay"
|
||||
else
|
||||
echo " Type: 🔒 Guard/Middle Relay"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Issues summary
|
||||
if [ "$ERROR_COUNT" -gt 0 ] || [ "$WARNING_COUNT" -gt 0 ]; then
|
||||
echo "⚠️ Issues Summary:"
|
||||
[ "$ERROR_COUNT" -gt 0 ] && echo " ❌ Errors: $ERROR_COUNT"
|
||||
[ "$WARNING_COUNT" -gt 0 ] && echo " ⚠️ Warnings: $WARNING_COUNT"
|
||||
|
||||
if [ -n "$RECENT_ERRORS" ] && [ "$ERROR_COUNT" -gt 0 ]; then
|
||||
echo ""
|
||||
echo " Recent errors:"
|
||||
echo "$RECENT_ERRORS" | sed 's/^/ /'
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "💡 For live monitoring: docker logs -f <container-name>"
|
||||
echo "🔗 Search your relay: https://metrics.torproject.org/rs.html"
|
||||
[ -n "$FINGERPRINT" ] && echo "📊 Direct link: https://metrics.torproject.org/rs.html#search/$FINGERPRINT"
|
||||
echo "🕒 Last updated: $TIMESTAMP"
|
||||
;;
|
||||
esac
|
||||
204
tools/view-logs
204
tools/view-logs
@@ -1,11 +1,201 @@
|
||||
#!/bin/sh
|
||||
# view-logs - Display recent Tor relay logs
|
||||
# Usage: docker exec guard-relay view-logs
|
||||
# view-logs - Advanced log viewer with filtering and analysis
|
||||
# Usage: docker exec guard-relay view-logs [--follow|--errors|--help]
|
||||
|
||||
if [ -f /var/log/tor/notices.log ]; then
|
||||
tail -n 50 -f /var/log/tor/notices.log
|
||||
else
|
||||
echo "⚠️ Log file not found. Tor might still be starting."
|
||||
echo "📍 Expected location: /var/log/tor/notices.log"
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
VERSION="1.1.0"
|
||||
LOG_FILE="${LOG_FILE:-/var/log/tor/notices.log}"
|
||||
LOG_LINES="${LOG_LINES:-50}"
|
||||
FOLLOW_MODE="${FOLLOW_MODE:-false}"
|
||||
FILTER_MODE="${FILTER_MODE:-all}"
|
||||
COLOR_OUTPUT="${COLOR_OUTPUT:-true}"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Parse arguments
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--help|-h)
|
||||
cat << EOF
|
||||
📜 Tor-Guard-Relay Log Viewer v${VERSION}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
USAGE:
|
||||
view-logs [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--follow, -f Follow log output (tail -f)
|
||||
--all Show all log entries (default)
|
||||
--errors Show only errors
|
||||
--warnings Show only warnings
|
||||
--info Show only info messages
|
||||
--bootstrap Show bootstrap progress
|
||||
--last N Show last N lines (default: 50)
|
||||
--no-color Disable colored output
|
||||
--json Output as JSON
|
||||
--help, -h Show this help message
|
||||
|
||||
ENVIRONMENT VARIABLES:
|
||||
LOG_FILE Path to log file
|
||||
LOG_LINES Default number of lines to show
|
||||
COLOR_OUTPUT Enable colored output (true/false)
|
||||
|
||||
FILTER MODES:
|
||||
all All log entries
|
||||
errors Error messages only
|
||||
warnings Warning messages only
|
||||
info Info/notice messages
|
||||
bootstrap Bootstrap related messages
|
||||
network Network/connectivity messages
|
||||
|
||||
EXAMPLES:
|
||||
view-logs # Last 50 lines
|
||||
view-logs --follow # Follow new entries
|
||||
view-logs --errors # Show only errors
|
||||
view-logs --last 100 # Show last 100 lines
|
||||
view-logs --bootstrap # Bootstrap progress
|
||||
|
||||
LOG LEVELS:
|
||||
[err] Error - Critical issues
|
||||
[warn] Warning - Potential problems
|
||||
[notice] Notice - Normal operations
|
||||
[info] Info - Detailed information
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
--follow|-f) FOLLOW_MODE="true" ;;
|
||||
--all) FILTER_MODE="all" ;;
|
||||
--errors) FILTER_MODE="errors" ;;
|
||||
--warnings) FILTER_MODE="warnings" ;;
|
||||
--info) FILTER_MODE="info" ;;
|
||||
--bootstrap) FILTER_MODE="bootstrap" ;;
|
||||
--network) FILTER_MODE="network" ;;
|
||||
--last)
|
||||
shift
|
||||
LOG_LINES="$1"
|
||||
shift
|
||||
;;
|
||||
--no-color) COLOR_OUTPUT="false" ;;
|
||||
--json) OUTPUT_FORMAT="json" ;;
|
||||
-*)
|
||||
echo "❌ Unknown option: $arg"
|
||||
echo "💡 Use --help for usage information"
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Check if log file exists
|
||||
if [ ! -f "$LOG_FILE" ]; then
|
||||
if [ "$OUTPUT_FORMAT" = "json" ]; then
|
||||
echo '{"error":"Log file not found","path":"'$LOG_FILE'"}'
|
||||
else
|
||||
echo "⚠️ Log file not found: $LOG_FILE"
|
||||
echo "📍 Tor might still be starting."
|
||||
echo "💡 Check back in a moment or verify Tor is running."
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Function to colorize log lines
|
||||
colorize_line() {
|
||||
if [ "$COLOR_OUTPUT" != "true" ]; then
|
||||
cat
|
||||
return
|
||||
fi
|
||||
|
||||
sed -e "s/\[err\]/$(printf "${RED}[err]${NC}")/g" \
|
||||
-e "s/\[error\]/$(printf "${RED}[error]${NC}")/g" \
|
||||
-e "s/\[warn\]/$(printf "${YELLOW}[warn]${NC}")/g" \
|
||||
-e "s/\[warning\]/$(printf "${YELLOW}[warning]${NC}")/g" \
|
||||
-e "s/\[notice\]/$(printf "${GREEN}[notice]${NC}")/g" \
|
||||
-e "s/\[info\]/$(printf "${BLUE}[info]${NC}")/g" \
|
||||
-e "s/Bootstrapped [0-9]*%/$(printf "${GREEN}&${NC}")/g"
|
||||
}
|
||||
|
||||
# Function to apply filters
|
||||
apply_filter() {
|
||||
case "$FILTER_MODE" in
|
||||
errors)
|
||||
grep -iE "\[err\]|\[error\]|failed|failure|critical"
|
||||
;;
|
||||
warnings)
|
||||
grep -iE "\[warn\]|\[warning\]"
|
||||
;;
|
||||
info)
|
||||
grep -iE "\[notice\]|\[info\]"
|
||||
;;
|
||||
bootstrap)
|
||||
grep -iE "bootstrap|starting|loading|opening|establishing"
|
||||
;;
|
||||
network)
|
||||
grep -iE "reachable|connection|network|port|address"
|
||||
;;
|
||||
*)
|
||||
cat
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# JSON output mode
|
||||
if [ "$OUTPUT_FORMAT" = "json" ]; then
|
||||
TOTAL_LINES=$(wc -l < "$LOG_FILE")
|
||||
ERROR_COUNT=$(grep -cE "\[err\]|\[error\]" "$LOG_FILE" 2>/dev/null || echo 0)
|
||||
WARNING_COUNT=$(grep -cE "\[warn\]|\[warning\]" "$LOG_FILE" 2>/dev/null || echo 0)
|
||||
|
||||
echo '{'
|
||||
echo ' "file": "'$LOG_FILE'",'
|
||||
echo ' "total_lines": '$TOTAL_LINES','
|
||||
echo ' "error_count": '$ERROR_COUNT','
|
||||
echo ' "warning_count": '$WARNING_COUNT','
|
||||
echo ' "entries": ['
|
||||
|
||||
tail -n "$LOG_LINES" "$LOG_FILE" | apply_filter | while IFS= read -r line; do
|
||||
# Escape quotes and backslashes for JSON
|
||||
line=$(echo "$line" | sed 's/\\/\\\\/g; s/"/\\"/g')
|
||||
echo ' "'$line'",'
|
||||
done | sed '$ s/,$//'
|
||||
|
||||
echo ' ]'
|
||||
echo '}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Regular output mode
|
||||
echo "📜 Tor Relay Logs"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📁 File: $LOG_FILE"
|
||||
echo "🔍 Filter: $FILTER_MODE"
|
||||
|
||||
# Count log entries
|
||||
if [ "$FILTER_MODE" = "all" ]; then
|
||||
TOTAL_LINES=$(wc -l < "$LOG_FILE")
|
||||
ERROR_COUNT=$(grep -cE "\[err\]|\[error\]" "$LOG_FILE" 2>/dev/null || echo 0)
|
||||
WARNING_COUNT=$(grep -cE "\[warn\]|\[warning\]" "$LOG_FILE" 2>/dev/null || echo 0)
|
||||
|
||||
echo "📊 Stats: $TOTAL_LINES total | $ERROR_COUNT errors | $WARNING_COUNT warnings"
|
||||
fi
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# Display logs
|
||||
if [ "$FOLLOW_MODE" = "true" ]; then
|
||||
echo "🔄 Following log output (Ctrl+C to stop)..."
|
||||
echo ""
|
||||
tail -n "$LOG_LINES" -f "$LOG_FILE" | apply_filter | colorize_line
|
||||
else
|
||||
tail -n "$LOG_LINES" "$LOG_FILE" | apply_filter | colorize_line
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "💡 Use 'view-logs --follow' for live updates"
|
||||
fi
|
||||
Reference in New Issue
Block a user