Files
tor-guard-relay/scripts/utilities/relay-status.sh
rE-Bo0t.bx1 be4f2bc125 feat(v1.1.7): Happy Family support (Tor 0.4.9+ FamilyId)
🔧 New tool: gen-family - generate/view Happy Family keys
  - Supports --force flag to overwrite existing keys without backup prompt

🐳 Dockerfiles: gen-family in both Dockerfile and Dockerfile.edge

🔧 Entrypoint:
- Phase 2: detect *.secret_family_key, log found keys (informational only)
- Guard/exit config gen: append FamilyId + MyFamily from ENV vars
- Bridge intentionally excluded

📊 Status tool: show family key count + Happy Family config state

📚 Docs:
- README: Happy Family section (generate / import), persistence table, flowchart
- ARCHITECTURE: all mermaid diagrams updated (Phase 2, config gen, tools, dirs)
- TOOLS: full gen-family reference with examples and exit codes
- DEPLOYMENT, MIGRATION, MIGRATION-V1.1.X, TROUBLESHOOTING: 5 -> 6 tools
- FAQ, example configs: version bump + FamilyId/MyFamily placeholders
- Directory authority voting: how 9 dirauths vote on relay flags (5/9 consensus)
- CIISS v2 ContactInfo: field reference, generator link, proof:uri-rsa verification
- All TOR_CONTACT_INFO examples updated to CIISS v2 format across templates and docs

📋 Templates:
- Guard/exit/multi-relay compose: TOR_FAMILY_ID + TOR_MY_FAMILY env vars
- All cosmos-compose + docker-compose versions -> 1.1.7

👷 CI: validate.yml gen-family in 8 spots (threshold 6), security tests, quick-test

🛡️ SECURITY.md: 1.1.7 active, 1.1.6 maintenance, gen-family in tools list

🔖 Version bump 1.1.6 -> 1.1.7 across 30+ files, tool count 5 -> 6, CHANGELOG entry

No breaking changes. TOR_FAMILY_ID and TOR_MY_FAMILY are optional.
2026-03-02 16:23:10 +08:00

407 lines
12 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
#
# relay-status.sh - Tor relay/bridge status checker with security validation
# Version: 1.1.7
# Automatically detects Tor containers or uses specified container name
#
set -euo pipefail
# Configuration
CONTAINER="${1:-}" # Accept container name as first argument
readonly FINGERPRINT_PATH="/var/lib/tor/fingerprint"
readonly TORRC_PATH="/etc/tor/torrc"
readonly VERSION="1.1.7"
# Colors for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly CYAN='\033[0;36m'
readonly NC='\033[0m' # No Color
# Helper functions
print_header() {
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}
print_section() {
echo -e "\n${CYAN}$1${NC}"
}
print_success() {
echo -e "${GREEN}${NC} $1"
}
print_warning() {
echo -e "${YELLOW}${NC} $1"
}
print_error() {
echo -e "${RED}${NC} $1"
}
print_info() {
echo -e "${BLUE}${NC} $1"
}
# Detect Tor container automatically
detect_tor_container() {
local containers
containers=$(sudo docker ps --format '{{.Names}}' | grep -iE 'tor|relay|bridge|onion' || true)
if [[ -z "${containers}" ]]; then
print_error "No Tor-related containers found running"
echo
print_info "Running containers:"
sudo docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}' | sed 's/^/ /'
echo
print_info "Usage: $0 [container-name]"
exit 1
fi
local count
count=$(echo "${containers}" | wc -l)
if [[ ${count} -eq 1 ]]; then
CONTAINER="${containers}"
print_success "Auto-detected container: ${CONTAINER}"
else
print_warning "Multiple Tor containers found:"
echo "${containers}" | sed 's/^/ - /'
echo
print_info "Please specify which container to check:"
print_info "Usage: $0 <container-name>"
echo
print_info "Example: $0 ${containers%$'\n'*}"
exit 1
fi
}
# Check if container is running
check_container() {
print_section "Container Status"
if ! sudo docker ps --format '{{.Names}}' | grep -q "^${CONTAINER}$"; then
print_error "Container '${CONTAINER}' is not running"
exit 1
fi
print_success "Container '${CONTAINER}' is running"
# Show uptime
local uptime
uptime=$(sudo docker inspect -f '{{.State.StartedAt}}' "${CONTAINER}" 2>/dev/null)
if [[ -n "${uptime}" ]]; then
echo -e " ${BLUE}Started:${NC} ${uptime}"
fi
# Show image version
local image
image=$(sudo docker inspect -f '{{.Config.Image}}' "${CONTAINER}" 2>/dev/null)
if [[ -n "${image}" ]]; then
echo -e " ${BLUE}Image:${NC} ${image}"
fi
# Check build info if available
local build_info
build_info=$(sudo docker exec "${CONTAINER}" cat /build-info.txt 2>/dev/null || echo "")
if [[ -n "${build_info}" ]]; then
echo -e " ${BLUE}Build:${NC}"
echo "${build_info}" | sed 's/^/ /'
fi
}
# Validate port security configuration
check_port_security() {
print_section "Port Security Validation"
# Check exposed ports
local exposed_ports
exposed_ports=$(sudo docker port "${CONTAINER}" 2>/dev/null || echo "")
if [[ -n "${exposed_ports}" ]]; then
echo -e " ${BLUE}Exposed ports:${NC}"
echo "${exposed_ports}" | sed 's/^/ /'
# Validate only 9001 and 9030 are exposed
if echo "${exposed_ports}" | grep -qE "^9001/tcp"; then
print_success "ORPort 9001 properly exposed"
else
print_warning "ORPort 9001 not exposed (may be using host network)"
fi
if echo "${exposed_ports}" | grep -qE "^9030/tcp"; then
print_success "DirPort 9030 properly exposed"
fi
# Check for improperly exposed internal ports
if echo "${exposed_ports}" | grep -qE "^903[5-9]/tcp"; then
print_error "SECURITY ISSUE: Internal metrics port exposed externally!"
echo -e " ${RED}Fix: Ensure metrics bind to 127.0.0.1 only${NC}"
fi
else
print_info "Using host network mode - checking port bindings..."
fi
# Check internal service bindings
local internal_bindings
internal_bindings=$(sudo docker exec "${CONTAINER}" netstat -tlnp 2>/dev/null | grep -E "9035|9036|9037" || echo "")
if [[ -n "${internal_bindings}" ]]; then
echo -e "\n ${BLUE}Internal services:${NC}"
# Validate localhost-only bindings
while IFS= read -r line; do
if echo "${line}" | grep -q "127.0.0.1"; then
local port
port=$(echo "${line}" | awk '{print $4}' | cut -d':' -f2)
print_success "Port ${port} properly bound to localhost"
else
local port
port=$(echo "${line}" | awk '{print $4}' | cut -d':' -f2)
print_error "SECURITY ISSUE: Port ${port} not bound to localhost!"
fi
done <<< "${internal_bindings}"
else
print_info "No internal service ports detected"
fi
}
# Display recent logs
show_logs() {
print_section "Recent Logs (last 15 lines)"
sudo docker logs --tail 15 "${CONTAINER}" 2>&1 | sed 's/^/ /'
}
# Show bootstrap progress
show_bootstrap() {
print_section "Bootstrap Progress"
local bootstrap_logs
bootstrap_logs=$(sudo docker logs "${CONTAINER}" 2>&1 | grep "Bootstrapped" | tail -5)
if [[ -n "${bootstrap_logs}" ]]; then
echo "${bootstrap_logs}" | sed 's/^/ /'
# Check if fully bootstrapped
if echo "${bootstrap_logs}" | grep -q "Bootstrapped 100%"; then
echo
print_success "Relay is fully bootstrapped!"
fi
else
print_warning "No bootstrap messages found in logs"
print_info "Relay might still be starting up"
fi
}
# Check if relay is reachable
check_reachability() {
print_section "Reachability Status"
local reachable_logs
reachable_logs=$(sudo docker logs "${CONTAINER}" 2>&1 | grep -iE "reachable|self-testing" | tail -3)
if [[ -n "${reachable_logs}" ]]; then
echo "${reachable_logs}" | sed 's/^/ /'
if echo "${reachable_logs}" | grep -q "reachable from the outside"; then
echo
print_success "ORPort is reachable!"
fi
else
print_warning "No reachability test results yet"
fi
}
# Display fingerprint
show_fingerprint() {
print_section "Relay Fingerprint"
if sudo docker exec "${CONTAINER}" test -f "${FINGERPRINT_PATH}" 2>/dev/null; then
local fingerprint
fingerprint=$(sudo docker exec "${CONTAINER}" cat "${FINGERPRINT_PATH}")
echo -e " ${GREEN}${fingerprint}${NC}"
echo
print_info "Search on Tor Metrics: https://metrics.torproject.org/rs.html"
print_info "Search on Onion Metrics: http://hctxrvjzfpvmzh2jllqhgvvkoepxb4kfzdjm6h7egcwlumggtktiftid.onion/rs.html"
else
print_warning "Fingerprint not found yet"
print_info "Tor might still be bootstrapping or generating keys"
fi
}
# Show ORPort configuration
show_orport() {
print_section "ORPort Configuration"
local orport_config
orport_config=$(sudo docker exec "${CONTAINER}" grep -iE "^(ORPort|DirPort|ObfsPort)" "${TORRC_PATH}" 2>/dev/null || true)
if [[ -n "${orport_config}" ]]; then
echo "${orport_config}" | sed 's/^/ /'
# Validate port numbers
if echo "${orport_config}" | grep -q "ORPort 9001"; then
print_success "ORPort configured correctly (9001)"
fi
if echo "${orport_config}" | grep -q "DirPort 9030"; then
print_success "DirPort configured correctly (9030)"
fi
else
print_warning "No ORPort configuration found"
fi
}
# Show relay type and settings
show_relay_info() {
print_section "Relay Information"
local relay_info
relay_info=$(sudo docker exec "${CONTAINER}" grep -iE "^(Nickname|ContactInfo|ExitRelay|BridgeRelay)" "${TORRC_PATH}" 2>/dev/null || true)
if [[ -n "${relay_info}" ]]; then
echo "${relay_info}" | sed 's/^/ /'
# Validate relay type
if echo "${relay_info}" | grep -q "ExitRelay 0"; then
print_success "Configured as guard/middle relay (not exit)"
elif echo "${relay_info}" | grep -q "ExitRelay 1"; then
print_warning "Configured as EXIT relay (higher legal risk)"
fi
else
print_warning "No relay information found in config"
fi
}
# Check for errors
check_errors() {
print_section "Recent Errors/Warnings"
local errors
errors=$(sudo docker logs "${CONTAINER}" 2>&1 | grep -iE "(error|warn|critical)" | tail -5)
if [[ -n "${errors}" ]]; then
echo "${errors}" | sed 's/^/ /'
else
print_success "No recent errors or warnings"
fi
}
# Show resource usage
show_resources() {
print_section "Resource Usage"
if command -v docker &> /dev/null; then
local stats
stats=$(sudo docker stats "${CONTAINER}" --no-stream --format "table {{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}" 2>/dev/null || true)
if [[ -n "${stats}" ]]; then
echo "${stats}" | sed 's/^/ /'
fi
fi
}
# Show network diagnostics
show_network_diagnostics() {
print_section "Network Diagnostics"
print_info "Basic network connectivity check..."
# IPv4 check
if sudo docker exec "${CONTAINER}" curl -4 -s --max-time 5 https://icanhazip.com &>/dev/null; then
print_success "IPv4 connectivity OK"
else
print_warning "IPv4 connectivity issues"
fi
# IPv6 check
if sudo docker exec "${CONTAINER}" curl -6 -s --max-time 5 https://icanhazip.com &>/dev/null; then
print_success "IPv6 connectivity OK"
else
print_info "IPv6 not available or configured"
fi
}
# Show quick help
show_help() {
cat << EOF
${BLUE}Tor Relay Status Checker v${VERSION}${NC}
Usage: $0 [container-name]
If no container name is provided, the script will attempt to auto-detect
Tor containers by searching for containers with names containing:
tor, relay, bridge, or onion
Examples:
$0 # Auto-detect Tor container
$0 guard-relay # Check specific container
$0 my-tor-bridge # Check bridge container
Options:
-h, --help Show this help message
-v, --version Show version information
Security Checks:
- Port exposure validation (9001/9030 only)
- Internal service binding verification (127.0.0.1)
- Bootstrap and reachability status
- Error detection and reporting
EOF
}
# Show version
show_version() {
echo "relay-status.sh version ${VERSION}"
echo "Part of Tor Guard Relay v1.1.7"
}
# Main execution
main() {
# Check for help flag
if [[ "${1:-}" =~ ^(-h|--help)$ ]]; then
show_help
exit 0
fi
# Check for version flag
if [[ "${1:-}" =~ ^(-v|--version)$ ]]; then
show_version
exit 0
fi
# Detect container if not specified
if [[ -z "${CONTAINER}" ]]; then
detect_tor_container
fi
print_header "🧅 Tor Relay Status Check: ${CONTAINER} (v${VERSION})"
check_container
check_port_security
show_relay_info
show_logs
show_bootstrap
check_reachability
show_fingerprint
show_orport
check_errors
show_resources
show_network_diagnostics
echo
print_header "✅ Status Check Complete"
echo
print_info "For live monitoring, use: sudo docker logs -f ${CONTAINER}"
print_info "For detailed diagnostics, use: sudo docker exec ${CONTAINER} status"
echo
}
# Run main function
main "$@"