From 49c9f6991851d4c00a91aed3145c1fc712bf6a04 Mon Sep 17 00:00:00 2001 From: "rE-Bo0t.bx1" <54429050+r3bo0tbx1@users.noreply.github.com> Date: Fri, 5 Dec 2025 20:24:55 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20chore:=20update=20scrip?= =?UTF-8?q?ts=20and=20documentation=20for=20v1.1.3=20release?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-entrypoint.sh | 92 ++------------------------------------------ healthcheck.sh | 10 +---- templates/README.md | 5 ++- tools/bridge-line | 6 +-- tools/fingerprint | 3 -- tools/health | 8 ---- tools/status | 10 ----- 7 files changed, 9 insertions(+), 125 deletions(-) diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 08d741e..1dab5d5 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -1,65 +1,40 @@ #!/bin/sh # docker-entrypoint.sh - Tor Guard Relay initialization and process management -# 🆕 v1.1.2 - Ultra-optimized 16.8 MB build with multi-mode support - set -e -# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -# ENV Variable Compatibility Layer -# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -# Supports BOTH naming conventions for maximum compatibility: -# -# 1. Our naming (TOR_* prefix): -# - TOR_NICKNAME, TOR_CONTACT_INFO, TOR_ORPORT, TOR_OBFS4_PORT, etc. -# -# 2. Official Tor Project bridge naming (for compatibility): -# - NICKNAME, EMAIL, OR_PORT, PT_PORT, OBFS4V_* (additional torrc options) -# -# Priority: Official names override TOR_* defaults (for drop-in compatibility) -# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -# Map official bridge ENV vars to our internal TOR_* prefix -# Official naming takes precedence over TOR_* defaults from Dockerfile [ -n "${NICKNAME:-}" ] && TOR_NICKNAME="$NICKNAME" [ -n "${EMAIL:-}" ] && TOR_CONTACT_INFO="$EMAIL" [ -n "${OR_PORT:-}" ] && TOR_ORPORT="$OR_PORT" [ -n "${PT_PORT:-}" ] && TOR_OBFS4_PORT="$PT_PORT" -# Auto-detect bridge mode if PT_PORT is set (official bridge convention) if [ -n "${PT_PORT:-}" ] && [ "${TOR_RELAY_MODE:-guard}" = "guard" ]; then TOR_RELAY_MODE="bridge" fi -# Configuration readonly TOR_CONFIG="${TOR_CONFIG:-/etc/tor/torrc}" readonly TOR_DATA_DIR="${TOR_DATA_DIR:-/var/lib/tor}" readonly TOR_LOG_DIR="${TOR_LOG_DIR:-/var/log/tor}" readonly TOR_RELAY_MODE="${TOR_RELAY_MODE:-guard}" -# Global PID tracking for cleanup TOR_PID="" TAIL_PID="" -# Emoji logging helpers (v1.1.0 style) log() { printf "%s\n" "$1"; } info() { printf " â„šī¸ %s\n" "$1"; } success() { printf "✅ %s\n" "$1"; } warn() { printf "🛑 %s\n" "$1"; } die() { printf "🛑 ERROR: %s\n" "$1"; exit 1; } -# Signal handler for graceful shutdown trap 'cleanup_and_exit' SIGTERM SIGINT cleanup_and_exit() { log "" warn "Shutdown signal received. Stopping Tor relay..." - # Stop log tail process first if [ -n "$TAIL_PID" ] && kill -0 "$TAIL_PID" 2>/dev/null; then kill -TERM "$TAIL_PID" 2>/dev/null || true fi - # Stop Tor process if [ -n "$TOR_PID" ] && kill -0 "$TOR_PID" 2>/dev/null; then log " Sending SIGTERM to Tor (PID: $TOR_PID)..." kill -TERM "$TOR_PID" 2>/dev/null || true @@ -73,15 +48,13 @@ cleanup_and_exit() { exit 0 } -# Startup banner startup_banner() { log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - log "🧅 Tor Guard Relay v1.1.2 - Initialization" + log "🧅 Tor Guard Relay v1.1.3 - Initialization" log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log "" } -# Phase 1: Directory setup phase_1_directories() { log "đŸ—‚ī¸ Phase 1: Directory Structure" mkdir -p "$TOR_DATA_DIR" "$TOR_LOG_DIR" /run/tor /tmp @@ -91,7 +64,6 @@ phase_1_directories() { log " â€ĸ Logs: $TOR_LOG_DIR" log " â€ĸ Run: /run/tor" - # Show disk space if command -v df >/dev/null 2>&1; then available=$(df -h "$TOR_DATA_DIR" 2>/dev/null | tail -n 1 | awk '{print $4}' || echo "unknown") log " đŸ’Ŋ Available disk space: $available" @@ -99,12 +71,9 @@ phase_1_directories() { log "" } -# Phase 2: Permissions phase_2_permissions() { log "🔐 Phase 2: Permission Hardening" - # Note: Ownership is set at build time by Dockerfile (chown requires root) - # Only set directory permissions (chmod doesn't require ownership) chmod 700 "$TOR_DATA_DIR" 2>/dev/null || warn "Failed to set data directory permissions (may be read-only mount)" chmod 755 "$TOR_LOG_DIR" 2>/dev/null || warn "Failed to set log directory permissions (may be read-only mount)" @@ -112,13 +81,11 @@ phase_2_permissions() { log "" } -# Validate relay configuration parameters validate_relay_config() { - # Validate relay mode if [ -n "${TOR_RELAY_MODE:-}" ]; then case "$TOR_RELAY_MODE" in guard|middle|exit|bridge) - : # Valid + : ;; *) die "TOR_RELAY_MODE must be: guard, middle, exit, or bridge (got: $TOR_RELAY_MODE)" @@ -126,17 +93,14 @@ validate_relay_config() { esac fi - # Validate nickname (1-19 alphanumeric characters) if [ -n "${TOR_NICKNAME:-}" ]; then nickname_len=$(printf "%s" "$TOR_NICKNAME" | wc -c) if [ "$nickname_len" -lt 1 ] || [ "$nickname_len" -gt 19 ]; then die "TOR_NICKNAME must be 1-19 characters (got: $nickname_len)" fi - # Check for invalid characters (busybox-compatible) if ! printf "%s" "$TOR_NICKNAME" | grep -qE '^[a-zA-Z0-9]+$'; then die "TOR_NICKNAME must contain only alphanumeric characters" fi - # Check for reserved names case "$(printf "%s" "$TOR_NICKNAME" | tr '[:upper:]' '[:lower:]')" in unnamed|noname|default|tor|relay|bridge|exit) die "TOR_NICKNAME cannot use reserved name: $TOR_NICKNAME" @@ -144,52 +108,39 @@ validate_relay_config() { esac fi - # Validate contact info is not empty if set if [ -n "${TOR_CONTACT_INFO:-}" ]; then - # Trim leading/trailing whitespace (fixes Cosmos ENV variable padding) TOR_CONTACT_INFO="$(printf "%s" "$TOR_CONTACT_INFO" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" contact_len=$(printf "%s" "$TOR_CONTACT_INFO" | wc -c) if [ "$contact_len" -lt 3 ]; then die "TOR_CONTACT_INFO must be at least 3 characters" fi - - # Check for embedded newlines using wc -l (more reliable than grep with escape sequences) line_count=$(printf "%s" "$TOR_CONTACT_INFO" | wc -l) if [ "$line_count" -gt 0 ]; then die "TOR_CONTACT_INFO cannot contain newlines (got $line_count lines)" fi - - # Note: We don't check for null bytes as printf would truncate the string anyway - # Email addresses are safe to use in torrc without additional escaping fi - # Validate port numbers (1024-65535) for port_var in TOR_ORPORT TOR_DIRPORT TOR_OBFS4_PORT; do eval "port_val=\${$port_var:-}" if [ -n "$port_val" ]; then - # Check if it's a valid integer if ! printf "%s" "$port_val" | grep -qE '^[0-9]+$'; then die "$port_var must be a valid port number (got: $port_val)" fi - # Check range (allow 0 for DirPort to disable it) if [ "$port_var" = "TOR_DIRPORT" ] && [ "$port_val" -eq 0 ]; then - : # Allow 0 for DirPort + : elif [ "$port_val" -lt 1 ] || [ "$port_val" -gt 65535 ]; then die "$port_var must be between 1-65535 (got: $port_val)" fi - # Warn about privileged ports if [ "$port_val" -lt 1024 ] && [ "$port_val" -ne 0 ]; then warn "$port_var using privileged port $port_val (may require CAP_NET_BIND_SERVICE)" fi fi done - # Validate bandwidth values if set for bw_var in TOR_BANDWIDTH_RATE TOR_BANDWIDTH_BURST; do eval "bw_val=\${$bw_var:-}" if [ -n "$bw_val" ]; then - # Check for valid Tor bandwidth format (e.g., "10 MB", "1 GB") if ! printf "%s" "$bw_val" | grep -qE '^[0-9]+ ?(Bytes?|KBytes?|MBytes?|GBytes?|TBytes?|KB?|MB?|GB?|TB?)$'; then die "$bw_var has invalid format (got: $bw_val, expected: '10 MB' or '1 GB')" fi @@ -197,15 +148,12 @@ validate_relay_config() { done } -# Phase 3: Configuration phase_3_configuration() { log "🔧 Phase 3: Configuration Setup" - # Priority 1: Mounted config file if [ -f "$TOR_CONFIG" ] && [ -s "$TOR_CONFIG" ]; then success "Using mounted configuration: $TOR_CONFIG" CONFIG_SOURCE="mounted" - # Priority 2: Environment variables elif [ -n "${TOR_NICKNAME:-}" ] && [ -n "${TOR_CONTACT_INFO:-}" ]; then log " Generating configuration from environment variables..." validate_relay_config @@ -219,7 +167,6 @@ phase_3_configuration() { log "" } -# Generate torrc from environment variables generate_config_from_env() { cat > "$TOR_CONFIG" << EOF # Generated Tor configuration for ${TOR_RELAY_MODE} relay @@ -242,7 +189,6 @@ Log notice stdout EOF - # Mode-specific configuration case "$TOR_RELAY_MODE" in guard|middle) cat >> "$TOR_CONFIG" << EOF @@ -289,52 +235,34 @@ EOF [ -n "${TOR_BANDWIDTH_RATE:-}" ] && echo "RelayBandwidthRate ${TOR_BANDWIDTH_RATE}" >> "$TOR_CONFIG" [ -n "${TOR_BANDWIDTH_BURST:-}" ] && echo "RelayBandwidthBurst ${TOR_BANDWIDTH_BURST}" >> "$TOR_CONFIG" - # Process OBFS4V_* additional variables (official Tor Project bridge compatibility) - # SECURITY: Strict validation to prevent injection attacks if [ "${OBFS4_ENABLE_ADDITIONAL_VARIABLES:-0}" = "1" ]; then echo "" >> "$TOR_CONFIG" echo "# Additional torrc options from OBFS4V_* environment variables" >> "$TOR_CONFIG" - # Get OBFS4V_* variables safely env | grep '^OBFS4V_' | sort | while IFS='=' read -r key value; do - # Strip OBFS4V_ prefix to get torrc option name torrc_key="${key#OBFS4V_}" - - # SECURITY: Validate torrc_key (must be alphanumeric with underscores) if ! printf "%s" "$torrc_key" | grep -qE '^[a-zA-Z][a-zA-Z0-9_]*$'; then warn "Skipping invalid OBFS4V variable name: $key (must be alphanumeric)" continue fi - - # SECURITY: Validate value doesn't contain dangerous characters - # Reject actual newlines (not the literal \n), nulls, and control chars - # Use wc -l to detect real newlines (busybox-compatible) line_count=$(printf "%s" "$value" | wc -l) if [ "$line_count" -gt 0 ]; then warn "Skipping $key: value contains newlines ($line_count lines)" continue fi - # Check for null bytes or other control characters (ASCII < 32, except space) if printf "%s" "$value" | tr -d '[ -~]' | grep -q .; then warn "Skipping $key: value contains control characters" continue fi - - # SECURITY: Whitelist known safe torrc options for OBFS4V_* - # Only allow specific torrc directives that are safe for bridges case "$torrc_key" in - # Safe torrc options for bridges AccountingMax|AccountingStart|Address|AddressDisableIPv6|\ BandwidthBurst|BandwidthRate|RelayBandwidthBurst|RelayBandwidthRate|\ ContactInfo|DirPort|MaxMemInQueues|NumCPUs|ORPort|\ OutboundBindAddress|OutboundBindAddressOR|Nickname|\ ServerDNSAllowBrokenConfig|ServerDNSDetectHijacking) - # Safe - write to config - # Use printf to ensure proper escaping printf "%s %s\n" "$torrc_key" "$value" >> "$TOR_CONFIG" ;; *) - # Unknown/potentially dangerous option - reject warn "Skipping $key: torrc option '$torrc_key' not in whitelist" warn " If you need this option, mount a custom torrc file instead" ;; @@ -349,11 +277,9 @@ EOF esac } -# Phase 4: Validation phase_4_validation() { log "🔎 Phase 4: Configuration Validation" - # Check Tor binary if ! command -v tor >/dev/null 2>&1; then die "Tor binary not found in PATH" fi @@ -361,17 +287,14 @@ phase_4_validation() { TOR_VERSION=$(tor --version 2>/dev/null | head -n1 || echo "unknown") log " đŸ“Ļ Tor version: $TOR_VERSION" - # Validate config syntax log " Validating torrc syntax..." - # SECURITY: Set trap BEFORE creating temp file to prevent leaks VERIFY_TMP="" cleanup_verify_tmp() { [ -n "$VERIFY_TMP" ] && rm -f "$VERIFY_TMP" } trap cleanup_verify_tmp EXIT INT TERM - # Create temp file in secure location VERIFY_TMP=$(mktemp -t tor-verify.XXXXXX) if ! tor --verify-config -f "$TOR_CONFIG" >"$VERIFY_TMP" 2>&1; then @@ -389,7 +312,6 @@ phase_4_validation() { log "" } -# Phase 5: Build info phase_5_build_info() { log "📊 Phase 5: Build Information" @@ -406,7 +328,6 @@ phase_5_build_info() { log "" } -# Phase 6: Diagnostic tools phase_6_diagnostics() { log "🧩 Phase 6: Available Diagnostic Tools" log "" @@ -418,14 +339,12 @@ phase_6_diagnostics() { log "" } -# Launch Tor launch_tor() { log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" success "Starting Tor relay..." log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log "" - # Start Tor in background tor -f "$TOR_CONFIG" & TOR_PID=$! @@ -434,14 +353,12 @@ launch_tor() { log "📋 Tor bootstrap logs:" log "" - # Wait for log file to be created (max 5 seconds) log_wait=0 while [ ! -f "$TOR_LOG_DIR/notices.log" ] && [ $log_wait -lt 50 ]; do sleep 0.1 log_wait=$((log_wait + 1)) done - # Tail Tor logs to stdout in background (if log file exists) if [ -f "$TOR_LOG_DIR/notices.log" ]; then tail -F "$TOR_LOG_DIR/notices.log" 2>/dev/null & TAIL_PID=$! @@ -449,11 +366,9 @@ launch_tor() { warn "Log file not created yet, bootstrap messages will not be shown" fi - # Wait for Tor process wait "$TOR_PID" TOR_EXIT_CODE=$? - # Stop tail process if still running if [ -n "$TAIL_PID" ] && kill -0 "$TAIL_PID" 2>/dev/null; then kill -TERM "$TAIL_PID" 2>/dev/null || true fi @@ -463,7 +378,6 @@ launch_tor() { cleanup_and_exit } -# Main execution main() { startup_banner phase_1_directories diff --git a/healthcheck.sh b/healthcheck.sh index b9a13e7..462fdbd 100644 --- a/healthcheck.sh +++ b/healthcheck.sh @@ -1,27 +1,21 @@ #!/bin/sh -# healthcheck.sh - Docker HEALTHCHECK wrapper 🐋💚 -# Validates Tor configuration regardless of source (mounted file or ENV vars) +# Validates Tor configuration regardless of source (mounted file or ENV vars) 🐋💚 set -e TOR_CONFIG="${TOR_CONFIG:-/etc/tor/torrc}" -# Check if config file exists and is readable if [ ! -f "$TOR_CONFIG" ]; then echo "ERROR: Config file not found: $TOR_CONFIG" exit 1 fi - if [ ! -r "$TOR_CONFIG" ]; then echo "ERROR: Config file not readable: $TOR_CONFIG" exit 1 fi - -# Verify Tor configuration if tor --verify-config -f "$TOR_CONFIG" >/dev/null 2>&1; then - # Config is valid exit 0 else echo "ERROR: Invalid Tor configuration" exit 1 -fi +fi \ No newline at end of file diff --git a/templates/README.md b/templates/README.md index ed4abf9..6671d65 100644 --- a/templates/README.md +++ b/templates/README.md @@ -44,6 +44,7 @@ You can configure Tor relays using **TWO methods**: docker run -d \ --name tor-bridge \ --network host \ + --security-opt no-new-privileges:true \ -e TOR_RELAY_MODE=bridge \ -e TOR_NICKNAME=MyBridge \ -e TOR_CONTACT_INFO=admin@example.com \ @@ -293,6 +294,6 @@ If you still see this error after updating to v1.1.1: --- -**Version:** 1.1.2 -**Last Updated:** 2025-11-18 +**Version:** 1.1.3 +**Last Updated:** 2025-12-05 **Maintainer:** rE-Bo0t.bx1 diff --git a/tools/bridge-line b/tools/bridge-line index 3287926..a929240 100644 --- a/tools/bridge-line +++ b/tools/bridge-line @@ -6,9 +6,8 @@ PT_STATE_DIR="${TOR_DATA_DIR}/pt_state" BRIDGE_LINE_FILE="${PT_STATE_DIR}/obfs4_bridgeline.txt" TORRC_FILE="${TOR_CONFIG:-/etc/tor/torrc}" -# Check if we're running as a bridge if [ -f "$TORRC_FILE" ] && grep -q "^BridgeRelay 1" "$TORRC_FILE" 2>/dev/null; then - : # Bridge mode confirmed + : elif [ -f "$TORRC_FILE" ]; then echo "" echo "❌ Not a bridge relay" @@ -26,7 +25,6 @@ else exit 1 fi -# Check for bridge line file if [ -f "$BRIDGE_LINE_FILE" ]; then echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" @@ -53,7 +51,6 @@ if [ -f "$BRIDGE_LINE_FILE" ]; then exit 0 fi -# Bridge line not available yet echo "" echo "âŗ Bridge line not available yet" echo "" @@ -74,7 +71,6 @@ echo " 4. Verify bridge config:" echo " docker exec grep BridgeRelay $TORRC_FILE" echo "" -# Try to find bridge line in logs as fallback if [ -f "/var/log/tor/notices.log" ]; then if grep -q "bridge line" /var/log/tor/notices.log 2>/dev/null; then echo "📝 Found in logs:" diff --git a/tools/fingerprint b/tools/fingerprint index dc25ca4..1124a39 100644 --- a/tools/fingerprint +++ b/tools/fingerprint @@ -10,7 +10,6 @@ if [ -f "$FINGERPRINT_FILE" ]; then echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" - # Read and format fingerprint NICKNAME=$(cat "$FINGERPRINT_FILE" | awk '{print $1}') FINGERPRINT=$(cat "$FINGERPRINT_FILE" | awk '{print $2}') @@ -18,11 +17,9 @@ if [ -f "$FINGERPRINT_FILE" ]; then echo " 🔐 Fingerprint: $FINGERPRINT" echo "" - # Format for easy copying echo " 📋 Copy/paste: $NICKNAME $FINGERPRINT" echo "" - # Show search URL echo " 🔍 Find on Tor Metrics:" echo " https://metrics.torproject.org/rs.html#search/$FINGERPRINT" echo "" diff --git a/tools/health b/tools/health index 443f152..48d9122 100644 --- a/tools/health +++ b/tools/health @@ -4,25 +4,21 @@ TOR_LOG="${TOR_LOG_DIR:-/var/log/tor}/notices.log" TOR_DATA="${TOR_DATA_DIR:-/var/lib/tor}" -# Sanitize numeric values to prevent "bad number" errors sanitize_num() { v=$(printf '%s' "$1" | tr -cd '0-9') [ -z "$v" ] && v=0 printf '%s' "$v" } -# Check if running if ! pgrep -x tor >/dev/null 2>&1; then echo '{"status":"down","bootstrap":0,"reachable":"unknown","errors":0}' exit 1 fi -# Get PID and uptime TOR_PID=$(pgrep -x tor) TOR_PID=$(sanitize_num "$TOR_PID") UPTIME=$(ps -o etime= -p "$TOR_PID" 2>/dev/null | tr -d ' ' || echo "unknown") -# Check bootstrap BOOTSTRAP=0 if [ -f "$TOR_LOG" ]; then if grep -q "Bootstrapped 100%" "$TOR_LOG"; then @@ -33,7 +29,6 @@ if [ -f "$TOR_LOG" ]; then fi BOOTSTRAP=$(sanitize_num "$BOOTSTRAP") -# Check reachability REACHABLE="unknown" if [ -f "$TOR_LOG" ]; then if grep -q "Self-testing indicates your ORPort is reachable" "$TOR_LOG"; then @@ -43,11 +38,9 @@ if [ -f "$TOR_LOG" ]; then fi fi -# Count errors ERRORS=$(grep -c "\[err\]" "$TOR_LOG" 2>/dev/null || echo "0") ERRORS=$(sanitize_num "$ERRORS") -# Get nickname and fingerprint NICKNAME="unknown" FINGERPRINT="unknown" if [ -f "$TOR_DATA/fingerprint" ]; then @@ -55,7 +48,6 @@ if [ -f "$TOR_DATA/fingerprint" ]; then FINGERPRINT=$(cat "$TOR_DATA/fingerprint" | awk '{print $2}') fi -# Output comprehensive JSON cat </dev/null 2>&1; then TOR_PID=$(pgrep -x tor) echo "🚀 Status: RUNNING (PID: $TOR_PID)" @@ -28,14 +25,12 @@ else exit 1 fi -# Check log file if [ ! -f "$TOR_LOG" ]; then echo "âš ī¸ Log: NOT FOUND" echo "" exit 1 fi -# Bootstrap progress if grep -q "Bootstrapped 100%" "$TOR_LOG" 2>/dev/null; then echo "✅ Bootstrap: 100% COMPLETE" else @@ -44,7 +39,6 @@ else echo "âŗ Bootstrap: ${PROGRESS}%" fi -# Reachability if grep -q "Self-testing indicates your ORPort is reachable" "$TOR_LOG"; then echo "🌐 ORPort: REACHABLE" elif grep -q "ORPort is not reachable" "$TOR_LOG"; then @@ -53,19 +47,16 @@ else echo "❓ ORPort: TESTING..." fi -# Get relay info (POSIX-compliant fingerprint truncation) if [ -f "$TOR_DATA/fingerprint" ] && [ -r "$TOR_DATA/fingerprint" ]; then FINGERPRINT=$(cat "$TOR_DATA/fingerprint" | awk '{print $2}') NICKNAME=$(cat "$TOR_DATA/fingerprint" | awk '{print $1}') echo "đŸĒĒ Nickname: $NICKNAME" - # POSIX-compliant substring extraction using cut FP_START=$(printf "%s" "$FINGERPRINT" | cut -c1-8) FP_END=$(printf "%s" "$FINGERPRINT" | cut -c33-40) echo "🔑 Fingerprint: ${FP_START}...${FP_END}" fi -# Count errors and warnings ERRORS=$(grep -c "\[err\]" "$TOR_LOG" 2>/dev/null || echo "0") ERRORS=$(sanitize_num "$ERRORS") WARNINGS=$(grep -c "\[warn\]" "$TOR_LOG" 2>/dev/null || echo "0") @@ -81,7 +72,6 @@ if [ "$WARNINGS" -gt 5 ]; then echo "âš ī¸ Warnings: $WARNINGS" fi -# Uptime UPTIME=$(ps -o etime= -p "$TOR_PID" 2>/dev/null | tr -d ' ' || echo "unknown") [ -n "$UPTIME" ] && echo "âąī¸ Uptime: $UPTIME"