#!/bin/sh # quick-start.sh - Interactive Tor relay deployment script # Helps beginners set up guard, exit, or bridge relays set -e # Color output helpers (if terminal supports it) if [ -t 1 ]; then BOLD='\033[1m' GREEN='\033[0;32m' BLUE='\033[0;34m' YELLOW='\033[1;33m' RED='\033[0;31m' NC='\033[0m' # No Color else BOLD='' GREEN='' BLUE='' YELLOW='' RED='' NC='' fi # Logging helpers log() { printf "${BOLD}%s${NC}\n" "$1"; } info() { printf " ${BLUE}ℹ️ %s${NC}\n" "$1"; } success() { printf "${GREEN}✅ %s${NC}\n" "$1"; } warn() { printf "${YELLOW}⚠️ %s${NC}\n" "$1"; } error() { printf "${RED}🛑 ERROR: %s${NC}\n" "$1"; exit 1; } # Banner show_banner() { cat << "EOF" ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 🧅 Tor Guard Relay - Quick Start Setup ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ This script will help you deploy a Tor relay in minutes! EOF } # Prompt for input with default value prompt() { prompt_text="$1" default_val="$2" var_name="$3" if [ -n "$default_val" ]; then printf "${BOLD}%s${NC} [${GREEN}%s${NC}]: " "$prompt_text" "$default_val" else printf "${BOLD}%s${NC}: " "$prompt_text" fi read -r input if [ -z "$input" ]; then input="$default_val" fi eval "$var_name=\"\$input\"" } # Validate nickname validate_nickname() { nickname="$1" # Length check (1-19 characters) nickname_len=$(printf "%s" "$nickname" | wc -c) if [ "$nickname_len" -lt 1 ] || [ "$nickname_len" -gt 19 ]; then warn "Nickname must be 1-19 characters (you entered: $nickname_len)" return 1 fi # Alphanumeric only if ! printf "%s" "$nickname" | grep -qE '^[a-zA-Z0-9]+$'; then warn "Nickname must contain only letters and numbers" return 1 fi # Reserved names case "$(printf "%s" "$nickname" | tr '[:upper:]' '[:lower:]')" in unnamed|noname|default|tor|relay|bridge|exit) warn "Cannot use reserved name: $nickname" return 1 ;; esac return 0 } # Validate email validate_email() { email="$1" # Basic length check email_len=$(printf "%s" "$email" | wc -c) if [ "$email_len" -lt 5 ]; then warn "Email must be at least 5 characters" return 1 fi # Very basic email format check if ! printf "%s" "$email" | grep -qE '^[^@]+@[^@]+\.[^@]+$'; then warn "Email format appears invalid (should be: name@domain.com)" return 1 fi return 0 } # Validate port validate_port() { port="$1" port_name="$2" # Integer check if ! printf "%s" "$port" | grep -qE '^[0-9]+$'; then warn "$port_name must be a valid number" return 1 fi # Range check if [ "$port" -lt 1 ] || [ "$port" -gt 65535 ]; then warn "$port_name must be between 1-65535" return 1 fi # Warn about privileged ports if [ "$port" -lt 1024 ]; then warn "$port_name is a privileged port (<1024) - may require CAP_NET_BIND_SERVICE capability" fi return 0 } # Main menu show_menu() { log "" log "What type of Tor relay do you want to run?" log "" printf " ${BOLD}1)${NC} ${GREEN}Guard/Middle Relay${NC} - Safest option, routes traffic\n" printf " ${BOLD}2)${NC} ${YELLOW}Exit Relay${NC} - Advanced, requires legal compliance\n" printf " ${BOLD}3)${NC} ${BLUE}Bridge Relay${NC} - Helps users in censored regions (obfs4)\n" printf " ${BOLD}4)${NC} ${RED}Quit${NC}\n" log "" } # Collect common information collect_common_info() { log "" log "📋 Basic Relay Information" log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log "" # Nickname while true; do prompt "Relay Nickname (1-19 alphanumeric)" "MyTorRelay" NICKNAME if validate_nickname "$NICKNAME"; then success "Nickname accepted: $NICKNAME" break fi done log "" # Email while true; do prompt "Contact Email" "admin@example.com" EMAIL if validate_email "$EMAIL"; then success "Email accepted: $EMAIL" break fi done log "" # ORPort while true; do if [ "$RELAY_MODE" = "guard" ]; then prompt "ORPort (recommended: 443 or 9001)" "9001" OR_PORT elif [ "$RELAY_MODE" = "exit" ]; then prompt "ORPort (recommended: 443)" "443" OR_PORT else prompt "ORPort (recommended: 443 or 9001)" "9001" OR_PORT fi if validate_port "$OR_PORT" "ORPort"; then success "ORPort set to: $OR_PORT" break fi done } # Guard relay specific collect_guard_info() { log "" log "⚙️ Guard Relay Configuration" log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log "" info "Guard relays provide directory services and route traffic." info "They are the safest and easiest type of relay to operate." log "" # DirPort while true; do prompt "DirPort (set to 0 to disable)" "9030" DIR_PORT if [ "$DIR_PORT" = "0" ]; then success "DirPort disabled" break fi if validate_port "$DIR_PORT" "DirPort"; then success "DirPort set to: $DIR_PORT" break fi done log "" # Bandwidth prompt "Bandwidth Rate (e.g., 50 MBytes, leave empty for unlimited)" "" BANDWIDTH_RATE if [ -n "$BANDWIDTH_RATE" ]; then prompt "Bandwidth Burst (e.g., 100 MBytes)" "" BANDWIDTH_BURST fi } # Exit relay specific collect_exit_info() { log "" log "⚠️ Exit Relay Configuration" log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log "" warn "EXIT RELAYS ARE ADVANCED AND HAVE LEGAL IMPLICATIONS!" log "" info "Exit relays route traffic to the public internet." info "You WILL receive abuse complaints and may need to respond to legal requests." info "Make sure you understand the risks and legal requirements." log "" # Confirmation prompt "Are you sure you want to run an exit relay? (yes/no)" "no" CONFIRM if [ "$CONFIRM" != "yes" ] && [ "$CONFIRM" != "YES" ]; then error "Exit relay setup cancelled. Consider running a guard or bridge relay instead." fi log "" # DirPort while true; do prompt "DirPort (set to 0 to disable)" "9030" DIR_PORT if [ "$DIR_PORT" = "0" ]; then success "DirPort disabled" break fi if validate_port "$DIR_PORT" "DirPort"; then success "DirPort set to: $DIR_PORT" break fi done log "" # Exit policy log "Exit Policy Options:" printf " ${BOLD}1)${NC} Reduced exit (recommended) - allows common ports\n" printf " ${BOLD}2)${NC} Reject all (safer) - effectively a guard relay\n" printf " ${BOLD}3)${NC} Custom policy - you provide the policy\n" log "" prompt "Exit Policy Choice" "1" POLICY_CHOICE case "$POLICY_CHOICE" in 1) EXIT_POLICY="ExitPolicy accept *:80\\nExitPolicy accept *:443\\nExitPolicy reject *:*" info "Using reduced exit policy (HTTP/HTTPS only)" ;; 2) EXIT_POLICY="ExitPolicy reject *:*" info "Using reject all policy" ;; 3) prompt "Enter custom exit policy (e.g., 'ExitPolicy reject *:*')" "ExitPolicy reject *:*" EXIT_POLICY ;; *) EXIT_POLICY="ExitPolicy reject *:*" warn "Invalid choice, using reject all" ;; esac log "" # Bandwidth prompt "Bandwidth Rate (e.g., 50 MBytes, leave empty for unlimited)" "" BANDWIDTH_RATE if [ -n "$BANDWIDTH_RATE" ]; then prompt "Bandwidth Burst (e.g., 100 MBytes)" "" BANDWIDTH_BURST fi } # Bridge relay specific collect_bridge_info() { log "" log "🌉 Bridge Relay Configuration" log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log "" info "Bridge relays help users in censored regions access Tor." info "They use obfs4 pluggable transport to disguise Tor traffic." log "" # PT_PORT (obfs4 port) while true; do prompt "obfs4 Port (PT_PORT, recommended: 443 or 9002)" "9002" PT_PORT if validate_port "$PT_PORT" "PT_PORT"; then success "obfs4 Port set to: $PT_PORT" break fi done log "" # Bandwidth prompt "Bandwidth Rate (e.g., 50 MBytes, leave empty for unlimited)" "" BANDWIDTH_RATE if [ -n "$BANDWIDTH_RATE" ]; then prompt "Bandwidth Burst (e.g., 100 MBytes)" "" BANDWIDTH_BURST fi } # Generate docker run command generate_docker_run() { log "" log "🐳 Docker Run Command" log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log "" cat > /tmp/tor-relay-run.sh << EOF #!/bin/sh # Tor ${RELAY_MODE} Relay - Generated by quick-start.sh # Run this script to start your Tor relay docker run -d \\ --name tor-${RELAY_MODE} \\ --network host \\ --restart unless-stopped \\ --cap-drop ALL \\ --cap-add NET_BIND_SERVICE \\ --security-opt no-new-privileges \\ -e NICKNAME="${NICKNAME}" \\ -e EMAIL="${EMAIL}" \\ -e OR_PORT=${OR_PORT} \\ EOF # Mode-specific additions if [ "$RELAY_MODE" = "guard" ]; then cat >> /tmp/tor-relay-run.sh << EOF -e TOR_RELAY_MODE=guard \\ -e TOR_DIRPORT=${DIR_PORT} \\ EOF elif [ "$RELAY_MODE" = "exit" ]; then cat >> /tmp/tor-relay-run.sh << EOF -e TOR_RELAY_MODE=exit \\ -e TOR_DIRPORT=${DIR_PORT} \\ -e TOR_EXIT_POLICY="${EXIT_POLICY}" \\ EOF elif [ "$RELAY_MODE" = "bridge" ]; then cat >> /tmp/tor-relay-run.sh << EOF -e PT_PORT=${PT_PORT} \\ EOF fi # Bandwidth (if set) if [ -n "$BANDWIDTH_RATE" ]; then cat >> /tmp/tor-relay-run.sh << EOF -e TOR_BANDWIDTH_RATE="${BANDWIDTH_RATE}" \\ EOF fi if [ -n "$BANDWIDTH_BURST" ]; then cat >> /tmp/tor-relay-run.sh << EOF -e TOR_BANDWIDTH_BURST="${BANDWIDTH_BURST}" \\ EOF fi # Volumes and image cat >> /tmp/tor-relay-run.sh << EOF -v tor-${RELAY_MODE}-data:/var/lib/tor \\ -v tor-${RELAY_MODE}-logs:/var/log/tor \\ ghcr.io/r3bo0tbx1/onion-relay:latest # Wait for container to start sleep 5 # Show container status docker ps | grep tor-${RELAY_MODE} echo "" echo "✅ Tor ${RELAY_MODE} relay started!" echo "" echo "📋 Useful commands:" echo " docker logs tor-${RELAY_MODE} - View logs" echo " docker exec tor-${RELAY_MODE} status - Full health check" echo " docker exec tor-${RELAY_MODE} health - JSON health API" EOF if [ "$RELAY_MODE" = "bridge" ]; then cat >> /tmp/tor-relay-run.sh << EOF echo " docker exec tor-${RELAY_MODE} bridge-line - Get bridge line to share" EOF else cat >> /tmp/tor-relay-run.sh << EOF echo " docker exec tor-${RELAY_MODE} fingerprint - Get relay fingerprint" EOF fi chmod +x /tmp/tor-relay-run.sh success "Docker run script generated: /tmp/tor-relay-run.sh" log "" cat /tmp/tor-relay-run.sh log "" } # Generate docker-compose.yml generate_docker_compose() { log "" log "🐳 Docker Compose Configuration" log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log "" cat > /tmp/docker-compose.yml << EOF # Tor ${RELAY_MODE} Relay - Generated by quick-start.sh # Deploy with: docker-compose up -d version: '3.8' services: tor-${RELAY_MODE}: image: ghcr.io/r3bo0tbx1/onion-relay:latest container_name: tor-${RELAY_MODE} restart: unless-stopped network_mode: host # Security settings cap_drop: - ALL cap_add: - NET_BIND_SERVICE security_opt: - no-new-privileges:true # Environment variables environment: NICKNAME: "${NICKNAME}" EMAIL: "${EMAIL}" OR_PORT: ${OR_PORT} EOF # Mode-specific additions if [ "$RELAY_MODE" = "guard" ]; then cat >> /tmp/docker-compose.yml << EOF TOR_RELAY_MODE: guard TOR_DIRPORT: ${DIR_PORT} EOF elif [ "$RELAY_MODE" = "exit" ]; then cat >> /tmp/docker-compose.yml << EOF TOR_RELAY_MODE: exit TOR_DIRPORT: ${DIR_PORT} TOR_EXIT_POLICY: "${EXIT_POLICY}" EOF elif [ "$RELAY_MODE" = "bridge" ]; then cat >> /tmp/docker-compose.yml << EOF PT_PORT: ${PT_PORT} EOF fi # Bandwidth (if set) if [ -n "$BANDWIDTH_RATE" ]; then cat >> /tmp/docker-compose.yml << EOF TOR_BANDWIDTH_RATE: "${BANDWIDTH_RATE}" EOF fi if [ -n "$BANDWIDTH_BURST" ]; then cat >> /tmp/docker-compose.yml << EOF TOR_BANDWIDTH_BURST: "${BANDWIDTH_BURST}" EOF fi # Volumes cat >> /tmp/docker-compose.yml << EOF # Data persistence volumes: - tor-${RELAY_MODE}-data:/var/lib/tor - tor-${RELAY_MODE}-logs:/var/log/tor volumes: tor-${RELAY_MODE}-data: driver: local tor-${RELAY_MODE}-logs: driver: local EOF success "Docker Compose file generated: /tmp/docker-compose.yml" log "" cat /tmp/docker-compose.yml log "" } # Next steps show_next_steps() { log "" log "🚀 Next Steps" log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" log "" log "1. Review the generated configuration:" info " - Docker run script: /tmp/tor-relay-run.sh" info " - Docker Compose file: /tmp/docker-compose.yml" log "" log "2. Deploy your relay:" info " ${BOLD}Option A:${NC} Run the script" info " sh /tmp/tor-relay-run.sh" log "" info " ${BOLD}Option B:${NC} Use Docker Compose" info " cp /tmp/docker-compose.yml ~/tor-relay/" info " cd ~/tor-relay && docker-compose up -d" log "" log "3. Monitor your relay:" info " docker logs tor-${RELAY_MODE}" info " docker exec tor-${RELAY_MODE} status" log "" if [ "$RELAY_MODE" = "bridge" ]; then log "4. Share your bridge line:" info " Wait 2-5 minutes for Tor to bootstrap (100%)" info " docker exec tor-${RELAY_MODE} bridge-line" info " Share the output with users in censored regions" log "" else log "4. Find your relay on Tor Metrics:" info " Wait 24-48 hours for your relay to appear" info " docker exec tor-${RELAY_MODE} fingerprint" info " Visit the Tor Metrics URL shown" log "" fi if [ "$RELAY_MODE" = "exit" ]; then warn "IMPORTANT FOR EXIT RELAYS:" info " - Monitor abuse complaints: ${EMAIL}" info " - Set up reverse DNS (PTR record) for your IP" info " - Consider getting AS number and WHOIS info" info " - Read: https://community.torproject.org/relay/community-resources/tor-exit-guidelines/" log "" fi log "5. Documentation:" info " - FAQ: https://github.com/r3bo0tbx1/tor-guard-relay/blob/main/docs/FAQ.md" info " - Architecture: https://github.com/r3bo0tbx1/tor-guard-relay/blob/main/docs/ARCHITECTURE.md" info " - Monitoring: https://github.com/r3bo0tbx1/tor-guard-relay/blob/main/docs/MONITORING.md" log "" success "Setup complete! Thank you for supporting the Tor network! 🧅" log "" } # Main execution main() { show_banner # Main menu loop while true; do show_menu prompt "Choose an option" "1" CHOICE case "$CHOICE" in 1) RELAY_MODE="guard" collect_common_info collect_guard_info generate_docker_run generate_docker_compose show_next_steps exit 0 ;; 2) RELAY_MODE="exit" collect_common_info collect_exit_info generate_docker_run generate_docker_compose show_next_steps exit 0 ;; 3) RELAY_MODE="bridge" collect_common_info collect_bridge_info generate_docker_run generate_docker_compose show_next_steps exit 0 ;; 4) log "" log "👋 Goodbye! Thank you for considering running a Tor relay." log "" exit 0 ;; *) warn "Invalid choice. Please select 1-4." ;; esac done } main "$@"