diff --git a/app/config/config.go b/app/config/config.go index 4563c45..f33b62d 100644 --- a/app/config/config.go +++ b/app/config/config.go @@ -149,11 +149,12 @@ type C struct { BunkerEnabled bool `env:"ORLY_BUNKER_ENABLED" default:"false" usage:"enable NIP-46 bunker signing service (requires WireGuard)"` BunkerPort int `env:"ORLY_BUNKER_PORT" default:"3335" usage:"internal port for bunker WebSocket (only accessible via WireGuard)"` - // Tor hidden service configuration - TorEnabled bool `env:"ORLY_TOR_ENABLED" default:"false" usage:"enable Tor hidden service integration (requires external Tor daemon)"` - TorPort int `env:"ORLY_TOR_PORT" default:"3336" usage:"internal port that Tor forwards .onion traffic to"` - TorHSDir string `env:"ORLY_TOR_HS_DIR" usage:"Tor HiddenServiceDir path to read .onion hostname (e.g., /var/lib/tor/orly-relay)"` - TorOnionAddress string `env:"ORLY_TOR_ONION_ADDRESS" usage:"manual .onion address override (optional, auto-detected from TorHSDir if empty)"` + // Tor hidden service configuration (subprocess mode - runs tor binary automatically) + TorEnabled bool `env:"ORLY_TOR_ENABLED" default:"true" usage:"enable Tor hidden service (spawns tor subprocess; disable with false if tor not installed)"` + TorPort int `env:"ORLY_TOR_PORT" default:"3336" usage:"internal port for Tor hidden service traffic"` + TorDataDir string `env:"ORLY_TOR_DATA_DIR" usage:"Tor data directory (default: $ORLY_DATA_DIR/tor)"` + TorBinary string `env:"ORLY_TOR_BINARY" default:"tor" usage:"path to tor binary (default: search in PATH)"` + TorSOCKS int `env:"ORLY_TOR_SOCKS" default:"0" usage:"SOCKS port for outbound Tor connections (0=disabled)"` // Cashu access token configuration (NIP-XX) CashuEnabled bool `env:"ORLY_CASHU_ENABLED" default:"false" usage:"enable Cashu blind signature tokens for access control"` @@ -645,11 +646,17 @@ func (cfg *C) GetStorageConfigValues() ( func (cfg *C) GetTorConfigValues() ( enabled bool, port int, - hsDir string, - onionAddress string, + dataDir string, + binary string, + socksPort int, ) { + dataDir = cfg.TorDataDir + if dataDir == "" { + dataDir = filepath.Join(cfg.DataDir, "tor") + } return cfg.TorEnabled, cfg.TorPort, - cfg.TorHSDir, - cfg.TorOnionAddress + dataDir, + cfg.TorBinary, + cfg.TorSOCKS } diff --git a/app/main.go b/app/main.go index a3c0eda..6b08154 100644 --- a/app/main.go +++ b/app/main.go @@ -549,22 +549,24 @@ func Run( log.I.F("archive relay manager initialized with %d relays", len(archiveRelays)) } - // Initialize Tor hidden service if enabled - torEnabled, torPort, torHSDir, torOnionAddr := cfg.GetTorConfigValues() + // Initialize Tor hidden service if enabled (spawns tor subprocess) + torEnabled, torPort, torDataDir, torBinary, torSOCKSPort := cfg.GetTorConfigValues() if torEnabled { torCfg := &tor.Config{ - Port: torPort, - HSDir: torHSDir, - OnionAddress: torOnionAddr, - Handler: l, + Port: torPort, + DataDir: torDataDir, + Binary: torBinary, + SOCKSPort: torSOCKSPort, + Handler: l, } var err error l.torService, err = tor.New(torCfg) if err != nil { - log.W.F("failed to create Tor service: %v", err) + log.W.F("Tor disabled: %v", err) } else { if err = l.torService.Start(); err != nil { log.W.F("failed to start Tor service: %v", err) + l.torService = nil } else { if addr := l.torService.OnionWSAddress(); addr != "" { log.I.F("Tor hidden service listening on port %d, address: %s", torPort, addr) diff --git a/deploy/orly-tor.service b/deploy/orly-tor.service deleted file mode 100644 index abdd5d8..0000000 --- a/deploy/orly-tor.service +++ /dev/null @@ -1,73 +0,0 @@ -# ORLY Relay with Tor Hidden Service - Systemd Unit -# -# This is an example systemd unit for running ORLY with Tor support. -# Copy and customize for your deployment. -# -# Installation: -# 1. Copy to /etc/systemd/system/orly-tor.service -# 2. Edit paths and environment variables as needed -# 3. sudo systemctl daemon-reload -# 4. sudo systemctl enable orly-tor -# 5. sudo systemctl start orly-tor -# -# Prerequisites: -# - Tor daemon running (systemctl enable tor && systemctl start tor) -# - Hidden service configured (run scripts/tor-setup.sh) - -[Unit] -Description=ORLY Nostr Relay with Tor Hidden Service -Documentation=https://git.mleku.dev/mleku/orly -After=network.target tor.service -Requires=tor.service -Wants=tor.service - -[Service] -Type=simple -User=orly -Group=orly - -# Working directory -WorkingDirectory=/opt/orly - -# Main relay binary -ExecStart=/opt/orly/orly - -# Environment configuration -# Core settings -Environment=ORLY_PORT=3334 -Environment=ORLY_DATA_DIR=/var/lib/orly -Environment=ORLY_LOG_LEVEL=info - -# Tor hidden service settings -Environment=ORLY_TOR_ENABLED=true -Environment=ORLY_TOR_PORT=3336 -Environment=ORLY_TOR_HS_DIR=/var/lib/tor/orly-relay - -# ACL mode (choose one: none, follows, managed) -Environment=ORLY_ACL_MODE=none - -# TLS (optional - uncomment and configure for production) -# Environment=ORLY_TLS_DOMAINS=relay.example.com - -# Resource limits -LimitNOFILE=65535 -LimitNPROC=4096 - -# Restart policy -Restart=always -RestartSec=5 - -# Security hardening -NoNewPrivileges=yes -ProtectSystem=strict -ProtectHome=yes -ReadWritePaths=/var/lib/orly -PrivateTmp=yes - -# Allow reading Tor hidden service directory -# Note: The Tor user must grant read access to the orly user -# Option 1: Add orly user to debian-tor group -# Option 2: Use ACLs: setfacl -R -m u:orly:rx /var/lib/tor/orly-relay - -[Install] -WantedBy=multi-user.target diff --git a/docs/TOR_SETUP.md b/docs/TOR_SETUP.md deleted file mode 100644 index 8ebbe56..0000000 --- a/docs/TOR_SETUP.md +++ /dev/null @@ -1,294 +0,0 @@ -# Tor Hidden Service Setup for ORLY Relay - -This guide explains how to configure ORLY to automatically mirror your relay as a Tor hidden service, making it accessible via a `.onion` address. - -## Overview - -When Tor support is enabled: -1. ORLY listens on a dedicated internal port for Tor traffic -2. The Tor daemon forwards `.onion` traffic to this port -3. ORLY automatically detects the `.onion` address -4. The `.onion` address is included in NIP-11 relay information - -## Quick Start - -### Development (Local Testing) - -```bash -# One-time setup (requires Tor installed) -./scripts/tor-dev-setup.sh - -# Start relay with Tor -ORLY_TOR_ENABLED=true ORLY_TOR_HS_DIR=~/.tor/orly-dev/hidden_service ./orly -``` - -### Production - -```bash -# One-time setup (requires root) -sudo ./scripts/tor-setup.sh - -# Start relay with Tor -ORLY_TOR_ENABLED=true ORLY_TOR_HS_DIR=/var/lib/tor/orly-relay ./orly -``` - -## Configuration - -### Environment Variables - -| Variable | Default | Description | -|----------|---------|-------------| -| `ORLY_TOR_ENABLED` | `false` | Enable Tor hidden service integration | -| `ORLY_TOR_PORT` | `3336` | Internal port Tor forwards traffic to | -| `ORLY_TOR_HS_DIR` | - | Path to Tor's HiddenServiceDir | -| `ORLY_TOR_ONION_ADDRESS` | - | Manual `.onion` override (optional) | - -### Example Configurations - -**Basic Tor setup:** -```bash -export ORLY_TOR_ENABLED=true -export ORLY_TOR_HS_DIR=/var/lib/tor/orly-relay -./orly -``` - -**Custom port:** -```bash -export ORLY_TOR_ENABLED=true -export ORLY_TOR_PORT=3337 -export ORLY_TOR_HS_DIR=/var/lib/tor/orly-relay -./orly -``` - -**Manual address (if auto-detection doesn't work):** -```bash -export ORLY_TOR_ENABLED=true -export ORLY_TOR_ONION_ADDRESS=abc123xyz.onion -./orly -``` - -## How It Works - -### Architecture - -``` -Internet Users Tor Users - │ │ - ▼ ▼ -┌──────────┐ ┌──────────────┐ -│ Regular │ │ Tor │ -│ Traffic │ │ Network │ -│ (HTTPS) │ │ │ -└────┬─────┘ └──────┬───────┘ - │ │ - │ Port 443 │ .onion:80 - ▼ ▼ -┌─────────────────────────────────────┐ -│ ORLY Relay │ -│ │ -│ ┌─────────────┐ ┌───────────────┐ │ -│ │ Main Server │ │ Tor Service │ │ -│ │ Port 3334 │ │ Port 3336 │ │ -│ └──────┬──────┘ └───────┬───────┘ │ -│ │ │ │ -│ └────────┬────────┘ │ -│ ▼ │ -│ ┌────────────┐ │ -│ │ Database │ │ -│ └────────────┘ │ -└─────────────────────────────────────┘ -``` - -### Address Detection - -1. The Tor daemon creates a hidden service directory containing: - - `hostname` - The `.onion` address - - `hs_ed25519_secret_key` - Private key (persistent) - - `hs_ed25519_public_key` - Public key - -2. ORLY watches the `hostname` file and automatically detects the address - -3. The address is included in NIP-11 relay information under the `addresses` field - -### NIP-11 Integration - -When Tor is enabled and the `.onion` address is detected, the NIP-11 relay info includes: - -```json -{ - "name": "ORLY", - "description": "...", - "pubkey": "...", - "addresses": [ - "wss://relay.example.com", - "ws://abc123xyz.onion/" - ] -} -``` - -## Manual Tor Configuration - -If you prefer to configure Tor manually instead of using the setup scripts: - -### 1. Install Tor - -**Debian/Ubuntu:** -```bash -sudo apt update && sudo apt install tor -``` - -**Arch Linux:** -```bash -sudo pacman -S tor -``` - -**macOS:** -```bash -brew install tor -``` - -### 2. Configure Hidden Service - -Add to `/etc/tor/torrc`: - -``` -HiddenServiceDir /var/lib/tor/orly-relay/ -HiddenServicePort 80 127.0.0.1:3336 -``` - -### 3. Set Permissions - -```bash -# Create directory -sudo mkdir -p /var/lib/tor/orly-relay - -# Set ownership (Debian/Ubuntu) -sudo chown debian-tor:debian-tor /var/lib/tor/orly-relay -sudo chmod 700 /var/lib/tor/orly-relay - -# Or on other systems -sudo chown tor:tor /var/lib/tor/orly-relay -``` - -### 4. Restart Tor - -```bash -sudo systemctl restart tor -``` - -### 5. Verify - -```bash -# Check the .onion address -sudo cat /var/lib/tor/orly-relay/hostname -``` - -## Systemd Service - -For production deployments, use the provided systemd unit: - -```bash -# Copy unit file -sudo cp deploy/orly-tor.service /etc/systemd/system/ - -# Edit configuration -sudo nano /etc/systemd/system/orly-tor.service - -# Enable and start -sudo systemctl daemon-reload -sudo systemctl enable orly-tor -sudo systemctl start orly-tor -``` - -### Permissions for Hidden Service Directory - -The ORLY process needs read access to the Tor hidden service directory: - -**Option 1: Add user to Tor group** -```bash -sudo usermod -aG debian-tor orly -``` - -**Option 2: Use ACLs** -```bash -sudo setfacl -R -m u:orly:rx /var/lib/tor/orly-relay -``` - -## Troubleshooting - -### .onion address not appearing in NIP-11 - -1. Check if Tor is running: - ```bash - systemctl status tor - ``` - -2. Check if hostname file exists: - ```bash - cat /var/lib/tor/orly-relay/hostname - ``` - -3. Check ORLY logs for Tor-related messages - -4. Verify environment variables are set: - ```bash - echo $ORLY_TOR_ENABLED - echo $ORLY_TOR_HS_DIR - ``` - -### Permission denied errors - -Ensure ORLY can read the hidden service directory: -```bash -# Check permissions -ls -la /var/lib/tor/orly-relay/ - -# Fix with ACL -sudo setfacl -m u:$(whoami):rx /var/lib/tor/orly-relay -``` - -### Tor connection timeouts - -1. Check Tor logs: - ```bash - journalctl -u tor -f - ``` - -2. For development, check: - ```bash - tail -f ~/.tor/orly-dev/tor.log - ``` - -3. Ensure Tor can reach the network (check firewall rules) - -### Different .onion address after restart - -This means the hidden service key was lost. The key is stored in: -- Production: `/var/lib/tor/orly-relay/hs_ed25519_secret_key` -- Development: `~/.tor/orly-dev/hidden_service/hs_ed25519_secret_key` - -To preserve your `.onion` address, back up the entire hidden service directory. - -## Security Considerations - -1. **Keep the hidden service key safe** - The `hs_ed25519_secret_key` file is your identity. If compromised, attackers can impersonate your relay. - -2. **Restrict file permissions** - Hidden service directories should be `chmod 700`. - -3. **Separate Tor traffic** - The dedicated Tor port (3336) keeps Tor traffic isolated from regular traffic. - -4. **Regular updates** - Keep Tor updated for security patches. - -## Testing with Tor Browser - -1. Download Tor Browser from https://www.torproject.org/ - -2. Navigate to your `.onion` address: - ``` - ws://your-address.onion/ - ``` - -3. Or test with curl over Tor: - ```bash - curl --socks5-hostname localhost:9050 -H "Accept: application/nostr+json" http://your-address.onion/ - ``` diff --git a/pkg/tor/service.go b/pkg/tor/service.go index faf6fc7..ec07f08 100644 --- a/pkg/tor/service.go +++ b/pkg/tor/service.go @@ -1,13 +1,19 @@ // Package tor provides Tor hidden service integration for the ORLY relay. -// It manages a listener on a dedicated port that receives traffic forwarded -// from the Tor daemon, and exposes the .onion address for NIP-11 integration. +// It spawns a tor subprocess with automatic configuration and manages +// the hidden service lifecycle. package tor import ( + "bufio" "context" "fmt" + "io" "net" "net/http" + "os" + "os/exec" + "path/filepath" + "strings" "sync" "time" @@ -16,25 +22,32 @@ import ( "lol.mleku.dev/log" ) -// Config holds Tor hidden service configuration. +// Config holds Tor subprocess configuration. type Config struct { - // Port is the internal port that Tor forwards .onion traffic to + // Port is the internal port for the hidden service Port int - // HSDir is the Tor HiddenServiceDir path to read .onion hostname from - HSDir string - // OnionAddress is an optional manual override for the .onion address - OnionAddress string + // DataDir is the directory for Tor data (torrc, keys, hostname, etc.) + DataDir string + // Binary is the path to the tor executable + Binary string + // SOCKSPort is the port for outbound SOCKS connections (0 = disabled) + SOCKSPort int // Handler is the HTTP handler to serve (typically the main relay handler) Handler http.Handler } -// Service manages the Tor hidden service listener. +// Service manages the Tor subprocess and hidden service listener. type Service struct { cfg *Config listener net.Listener server *http.Server - // onionAddress is the detected or configured .onion address + // Tor subprocess + cmd *exec.Cmd + stdout io.ReadCloser + stderr io.ReadCloser + + // onionAddress is the detected .onion address onionAddress string // hostname watcher @@ -47,11 +60,29 @@ type Service struct { } // New creates a new Tor service with the given configuration. +// Returns an error if the tor binary is not found. func New(cfg *Config) (*Service, error) { if cfg.Port == 0 { cfg.Port = 3336 } + // Find tor binary + binary := cfg.Binary + if binary == "" { + binary = "tor" + } + + torPath, err := exec.LookPath(binary) + if err != nil { + return nil, fmt.Errorf("tor binary not found: %w (install tor or set ORLY_TOR_ENABLED=false)", err) + } + cfg.Binary = torPath + + // Ensure data directory exists + if err := os.MkdirAll(cfg.DataDir, 0700); err != nil { + return nil, fmt.Errorf("failed to create Tor data directory: %w", err) + } + ctx, cancel := context.WithCancel(context.Background()) s := &Service{ @@ -60,43 +91,117 @@ func New(cfg *Config) (*Service, error) { cancel: cancel, } - // If manual address provided, use it - if cfg.OnionAddress != "" { - s.onionAddress = cfg.OnionAddress - log.I.F("using configured .onion address: %s", s.onionAddress) + return s, nil +} + +// generateTorrc creates the torrc configuration file. +func (s *Service) generateTorrc() (string, error) { + torrcPath := filepath.Join(s.cfg.DataDir, "torrc") + hsDir := filepath.Join(s.cfg.DataDir, "hidden_service") + + // Ensure hidden service directory exists with correct permissions + if err := os.MkdirAll(hsDir, 0700); err != nil { + return "", fmt.Errorf("failed to create hidden service directory: %w", err) } - return s, nil + var sb strings.Builder + sb.WriteString("# ORLY Tor hidden service configuration\n") + sb.WriteString("# Auto-generated - do not edit\n\n") + + // Data directory + sb.WriteString(fmt.Sprintf("DataDirectory %s/data\n", s.cfg.DataDir)) + + // Hidden service configuration + sb.WriteString(fmt.Sprintf("HiddenServiceDir %s\n", hsDir)) + sb.WriteString(fmt.Sprintf("HiddenServicePort 80 127.0.0.1:%d\n", s.cfg.Port)) + + // Optional SOCKS port for outbound connections + if s.cfg.SOCKSPort > 0 { + sb.WriteString(fmt.Sprintf("SocksPort %d\n", s.cfg.SOCKSPort)) + } else { + sb.WriteString("SocksPort 0\n") + } + + // Disable unused features to reduce resource usage + sb.WriteString("ControlPort 0\n") + sb.WriteString("Log notice stdout\n") + + // Write torrc + if err := os.WriteFile(torrcPath, []byte(sb.String()), 0600); err != nil { + return "", fmt.Errorf("failed to write torrc: %w", err) + } + + // Create data subdirectory + if err := os.MkdirAll(filepath.Join(s.cfg.DataDir, "data"), 0700); err != nil { + return "", fmt.Errorf("failed to create Tor data subdirectory: %w", err) + } + + return torrcPath, nil } -// Start initializes the Tor listener and hostname watcher. +// Start spawns the Tor subprocess and initializes the listener. func (s *Service) Start() error { - // Start hostname watcher if HSDir is configured - if s.cfg.HSDir != "" { - s.hostnameWatcher = NewHostnameWatcher(s.cfg.HSDir) - s.hostnameWatcher.OnChange(func(addr string) { + // Generate torrc + torrcPath, err := s.generateTorrc() + if err != nil { + return err + } + + log.I.F("starting Tor subprocess with config: %s", torrcPath) + + // Start tor subprocess + s.cmd = exec.CommandContext(s.ctx, s.cfg.Binary, "-f", torrcPath) + + // Capture stdout/stderr for logging + s.stdout, err = s.cmd.StdoutPipe() + if err != nil { + return fmt.Errorf("failed to get Tor stdout: %w", err) + } + s.stderr, err = s.cmd.StderrPipe() + if err != nil { + return fmt.Errorf("failed to get Tor stderr: %w", err) + } + + if err := s.cmd.Start(); err != nil { + return fmt.Errorf("failed to start Tor: %w", err) + } + + log.I.F("Tor subprocess started (PID %d)", s.cmd.Process.Pid) + + // Log Tor output + s.wg.Add(2) + go s.logOutput("tor", s.stdout) + go s.logOutput("tor", s.stderr) + + // Monitor subprocess + s.wg.Add(1) + go s.monitorProcess() + + // Start hostname watcher + hsDir := filepath.Join(s.cfg.DataDir, "hidden_service") + s.hostnameWatcher = NewHostnameWatcher(hsDir) + s.hostnameWatcher.OnChange(func(addr string) { + s.mu.Lock() + s.onionAddress = addr + s.mu.Unlock() + log.I.F("Tor hidden service address: %s", addr) + }) + if err := s.hostnameWatcher.Start(); err != nil { + log.W.F("failed to start hostname watcher: %v", err) + } else { + // Get initial address + if addr := s.hostnameWatcher.Address(); addr != "" { s.mu.Lock() s.onionAddress = addr s.mu.Unlock() - log.I.F("detected .onion address: %s", addr) - }) - if err := s.hostnameWatcher.Start(); err != nil { - log.W.F("failed to start hostname watcher: %v", err) - } else { - // Get initial address - if addr := s.hostnameWatcher.Address(); addr != "" { - s.mu.Lock() - s.onionAddress = addr - s.mu.Unlock() - } } } - // Create listener + // Create listener for the hidden service port addr := fmt.Sprintf("127.0.0.1:%d", s.cfg.Port) - var err error s.listener, err = net.Listen("tcp", addr) if chk.E(err) { + s.Stop() return fmt.Errorf("failed to listen on %s: %w", addr, err) } @@ -112,7 +217,7 @@ func (s *Service) Start() error { s.wg.Add(1) go func() { defer s.wg.Done() - log.I.F("Tor listener started on %s", addr) + log.I.F("Tor hidden service listener started on %s", addr) if err := s.server.Serve(s.listener); err != nil && err != http.ErrServerClosed { log.E.F("Tor server error: %v", err) } @@ -121,6 +226,40 @@ func (s *Service) Start() error { return nil } +// logOutput reads from a pipe and logs each line. +func (s *Service) logOutput(prefix string, r io.ReadCloser) { + defer s.wg.Done() + scanner := bufio.NewScanner(r) + for scanner.Scan() { + line := scanner.Text() + // Filter out common noise + if strings.Contains(line, "Bootstrapped") { + log.I.F("[%s] %s", prefix, line) + } else if strings.Contains(line, "[warn]") || strings.Contains(line, "[err]") { + log.W.F("[%s] %s", prefix, line) + } else { + log.D.F("[%s] %s", prefix, line) + } + } +} + +// monitorProcess watches the Tor subprocess and logs when it exits. +func (s *Service) monitorProcess() { + defer s.wg.Done() + err := s.cmd.Wait() + if err != nil { + select { + case <-s.ctx.Done(): + // Expected shutdown + log.D.F("Tor subprocess exited (shutdown)") + default: + log.E.F("Tor subprocess exited unexpectedly: %v", err) + } + } else { + log.I.F("Tor subprocess exited cleanly") + } +} + // Stop gracefully shuts down the Tor service. func (s *Service) Stop() error { s.cancel() @@ -135,7 +274,7 @@ func (s *Service) Stop() error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := s.server.Shutdown(ctx); chk.E(err) { - return err + // Continue shutdown anyway } } @@ -144,12 +283,33 @@ func (s *Service) Stop() error { s.listener.Close() } + // Terminate Tor subprocess + if s.cmd != nil && s.cmd.Process != nil { + log.D.F("sending SIGTERM to Tor subprocess (PID %d)", s.cmd.Process.Pid) + s.cmd.Process.Signal(os.Interrupt) + + // Give it a few seconds to exit gracefully + done := make(chan struct{}) + go func() { + s.cmd.Wait() + close(done) + }() + + select { + case <-done: + log.D.F("Tor subprocess exited gracefully") + case <-time.After(5 * time.Second): + log.W.F("Tor subprocess did not exit, killing") + s.cmd.Process.Kill() + } + } + s.wg.Wait() log.I.F("Tor service stopped") return nil } -// OnionAddress returns the current .onion address (without .onion suffix). +// OnionAddress returns the current .onion address. func (s *Service) OnionAddress() string { s.mu.RLock() defer s.mu.RUnlock() @@ -172,7 +332,7 @@ func (s *Service) OnionWSAddress() string { // IsRunning returns whether the Tor service is currently running. func (s *Service) IsRunning() bool { - return s.listener != nil + return s.listener != nil && s.cmd != nil && s.cmd.Process != nil } // Upgrader returns a WebSocket upgrader configured for Tor connections. @@ -186,3 +346,13 @@ func (s *Service) Upgrader() *websocket.Upgrader { }, } } + +// DataDir returns the Tor data directory path. +func (s *Service) DataDir() string { + return s.cfg.DataDir +} + +// HiddenServiceDir returns the hidden service directory path. +func (s *Service) HiddenServiceDir() string { + return filepath.Join(s.cfg.DataDir, "hidden_service") +} diff --git a/pkg/version/version b/pkg/version/version index 222a360..fe2725e 100644 --- a/pkg/version/version +++ b/pkg/version/version @@ -1 +1 @@ -v0.46.0 +v0.46.1 diff --git a/scripts/tor-dev-setup.sh b/scripts/tor-dev-setup.sh deleted file mode 100755 index 5bfb46f..0000000 --- a/scripts/tor-dev-setup.sh +++ /dev/null @@ -1,217 +0,0 @@ -#!/bin/bash -# tor-dev-setup.sh - Development Tor hidden service setup for ORLY relay -# -# This script sets up a user-space Tor hidden service for local development. -# No root privileges required (except for initial Tor installation). -# -# Usage: ./scripts/tor-dev-setup.sh [port] -# port: internal port ORLY listens on for Tor traffic (default: 3336) -# -# After running this script: -# 1. Start ORLY with: ORLY_TOR_ENABLED=true ORLY_TOR_HS_DIR=~/.tor/orly-dev ./orly -# 2. Connect via Tor Browser to the .onion address - -set -e - -# Configuration -TOR_PORT="${1:-3336}" -TOR_DATA_DIR="${HOME}/.tor/orly-dev" -TOR_CONFIG="${TOR_DATA_DIR}/torrc" -TOR_PID_FILE="${TOR_DATA_DIR}/tor.pid" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -info() { echo -e "${GREEN}[INFO]${NC} $1"; } -warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } -error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } -debug() { echo -e "${BLUE}[DEBUG]${NC} $1"; } - -# Check if Tor is installed -check_tor() { - if ! command -v tor &> /dev/null; then - error "Tor is not installed. Please install it first: - Debian/Ubuntu: sudo apt install tor - Arch: sudo pacman -S tor - macOS: brew install tor - Fedora: sudo dnf install tor" - fi - info "Tor is installed: $(tor --version | head -1)" -} - -# Create directory structure -setup_dirs() { - info "Creating directory structure..." - - mkdir -p "${TOR_DATA_DIR}" - mkdir -p "${TOR_DATA_DIR}/hidden_service" - chmod 700 "${TOR_DATA_DIR}" - chmod 700 "${TOR_DATA_DIR}/hidden_service" - - info "Directory created: ${TOR_DATA_DIR}" -} - -# Create Tor configuration -create_config() { - info "Creating Tor configuration..." - - cat > "$TOR_CONFIG" << EOF -# ORLY Development Tor Configuration -# Generated by tor-dev-setup.sh on $(date) - -# Data directory -DataDirectory ${TOR_DATA_DIR}/data - -# Run in background -RunAsDaemon 1 -PidFile ${TOR_PID_FILE} - -# SOCKS proxy for outgoing connections (optional, for testing) -SocksPort 9150 - -# Hidden service for ORLY relay -HiddenServiceDir ${TOR_DATA_DIR}/hidden_service/ -HiddenServicePort 80 127.0.0.1:${TOR_PORT} - -# Logging -Log notice file ${TOR_DATA_DIR}/tor.log -EOF - - chmod 600 "$TOR_CONFIG" - info "Configuration created: ${TOR_CONFIG}" -} - -# Stop existing Tor instance -stop_tor() { - if [ -f "$TOR_PID_FILE" ]; then - PID=$(cat "$TOR_PID_FILE" 2>/dev/null) - if [ -n "$PID" ] && kill -0 "$PID" 2>/dev/null; then - info "Stopping existing Tor instance (PID: $PID)..." - kill "$PID" 2>/dev/null || true - sleep 2 - fi - rm -f "$TOR_PID_FILE" - fi -} - -# Start Tor -start_tor() { - info "Starting Tor..." - - # Ensure data directory exists - mkdir -p "${TOR_DATA_DIR}/data" - - # Start Tor with our config - tor -f "$TOR_CONFIG" 2>&1 | head -20 & - - # Wait for Tor to bootstrap - info "Waiting for Tor to connect to the network..." - - for i in {1..60}; do - if [ -f "${TOR_DATA_DIR}/hidden_service/hostname" ]; then - ONION_ADDR=$(cat "${TOR_DATA_DIR}/hidden_service/hostname") - if [ -n "$ONION_ADDR" ]; then - break - fi - fi - - # Check if Tor is still running - if [ -f "$TOR_PID_FILE" ]; then - PID=$(cat "$TOR_PID_FILE") - if ! kill -0 "$PID" 2>/dev/null; then - error "Tor process died. Check ${TOR_DATA_DIR}/tor.log" - fi - fi - - sleep 1 - echo -n "." - done - echo "" - - if [ -f "${TOR_DATA_DIR}/hidden_service/hostname" ]; then - ONION_ADDR=$(cat "${TOR_DATA_DIR}/hidden_service/hostname") - info "Tor started successfully" - echo "" - echo -e "${GREEN}======================================${NC}" - echo -e "${GREEN}Hidden Service Address:${NC}" - echo -e "${YELLOW}${ONION_ADDR}${NC}" - echo -e "${GREEN}======================================${NC}" - echo "" - else - warn "Tor started but hidden service not ready yet" - warn "Check: tail -f ${TOR_DATA_DIR}/tor.log" - fi -} - -# Print usage instructions -print_instructions() { - echo "" - info "Development Tor setup complete!" - echo "" - echo " To start ORLY with Tor:" - echo -e " ${BLUE}ORLY_TOR_ENABLED=true ORLY_TOR_HS_DIR=${TOR_DATA_DIR}/hidden_service ./orly${NC}" - echo "" - echo " To view the .onion address:" - echo -e " ${BLUE}cat ${TOR_DATA_DIR}/hidden_service/hostname${NC}" - echo "" - echo " To view Tor logs:" - echo -e " ${BLUE}tail -f ${TOR_DATA_DIR}/tor.log${NC}" - echo "" - echo " To stop Tor:" - echo -e " ${BLUE}kill \$(cat ${TOR_PID_FILE})${NC}" - echo "" - echo " To restart Tor:" - echo -e " ${BLUE}./scripts/tor-dev-setup.sh${NC}" - echo "" -} - -# Status command -status() { - if [ -f "$TOR_PID_FILE" ]; then - PID=$(cat "$TOR_PID_FILE") - if kill -0 "$PID" 2>/dev/null; then - info "Tor is running (PID: $PID)" - if [ -f "${TOR_DATA_DIR}/hidden_service/hostname" ]; then - ONION_ADDR=$(cat "${TOR_DATA_DIR}/hidden_service/hostname") - echo -e " Address: ${YELLOW}${ONION_ADDR}${NC}" - fi - return 0 - fi - fi - warn "Tor is not running" - return 1 -} - -# Main -main() { - case "${1:-}" in - status) - status - exit $? - ;; - stop) - stop_tor - info "Tor stopped" - exit 0 - ;; - *) - ;; - esac - - info "ORLY Development Tor Setup" - info "Internal port: ${TOR_PORT}" - echo "" - - check_tor - setup_dirs - stop_tor - create_config - start_tor - print_instructions -} - -main "$@" diff --git a/scripts/tor-setup.sh b/scripts/tor-setup.sh deleted file mode 100755 index 1087d81..0000000 --- a/scripts/tor-setup.sh +++ /dev/null @@ -1,197 +0,0 @@ -#!/bin/bash -# tor-setup.sh - Production Tor hidden service setup for ORLY relay -# -# This script installs Tor and configures a hidden service for the relay. -# The .onion address will be automatically detected by ORLY. -# -# Usage: sudo ./scripts/tor-setup.sh [port] -# port: internal port ORLY listens on for Tor traffic (default: 3336) -# -# Requirements: -# - Root privileges (for installing packages and configuring Tor) -# - Systemd-based Linux distribution -# -# After running this script: -# 1. Start ORLY with: ORLY_TOR_ENABLED=true ORLY_TOR_HS_DIR=/var/lib/tor/orly-relay ./orly -# 2. The .onion address will appear in logs and NIP-11 - -set -e - -# Configuration -TOR_PORT="${1:-3336}" -HS_NAME="orly-relay" -HS_DIR="/var/lib/tor/${HS_NAME}" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -info() { echo -e "${GREEN}[INFO]${NC} $1"; } -warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } -error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } - -# Check if running as root -if [ "$EUID" -ne 0 ]; then - error "Please run as root: sudo $0" -fi - -# Detect package manager and install Tor -install_tor() { - info "Installing Tor..." - - if command -v apt-get &> /dev/null; then - # Debian/Ubuntu - apt-get update - apt-get install -y tor - elif command -v dnf &> /dev/null; then - # Fedora/RHEL - dnf install -y tor - elif command -v pacman &> /dev/null; then - # Arch Linux - pacman -Sy --noconfirm tor - elif command -v apk &> /dev/null; then - # Alpine - apk add tor - elif command -v brew &> /dev/null; then - # macOS (run as user, not root) - brew install tor - else - error "Unsupported package manager. Please install Tor manually." - fi - - info "Tor installed successfully" -} - -# Configure hidden service -configure_tor() { - info "Configuring Tor hidden service..." - - TORRC="/etc/tor/torrc" - - # Check if hidden service already configured - if grep -q "HiddenServiceDir ${HS_DIR}" "$TORRC" 2>/dev/null; then - warn "Hidden service already configured in ${TORRC}" - return 0 - fi - - # Backup original torrc - if [ -f "$TORRC" ]; then - cp "$TORRC" "${TORRC}.backup.$(date +%Y%m%d%H%M%S)" - info "Backed up original torrc" - fi - - # Add hidden service configuration - cat >> "$TORRC" << EOF - -# ORLY Relay Hidden Service -# Added by tor-setup.sh on $(date) -HiddenServiceDir ${HS_DIR}/ -HiddenServicePort 80 127.0.0.1:${TOR_PORT} -EOF - - info "Hidden service configured: ${HS_DIR}" -} - -# Set permissions -set_permissions() { - info "Setting directory permissions..." - - # Create hidden service directory if it doesn't exist - mkdir -p "$HS_DIR" - - # Set correct ownership (debian-tor on Debian/Ubuntu, tor on others) - if id "debian-tor" &>/dev/null; then - chown -R debian-tor:debian-tor "$HS_DIR" - elif id "tor" &>/dev/null; then - chown -R tor:tor "$HS_DIR" - fi - - chmod 700 "$HS_DIR" - - info "Permissions set" -} - -# Restart Tor service -restart_tor() { - info "Restarting Tor service..." - - if command -v systemctl &> /dev/null; then - systemctl enable tor - systemctl restart tor - elif command -v service &> /dev/null; then - service tor restart - else - warn "Could not restart Tor. Please restart manually." - return 1 - fi - - # Wait for Tor to create the hostname file - info "Waiting for hidden service to initialize..." - for i in {1..30}; do - if [ -f "${HS_DIR}/hostname" ]; then - break - fi - sleep 1 - done - - if [ -f "${HS_DIR}/hostname" ]; then - ONION_ADDR=$(cat "${HS_DIR}/hostname") - info "Tor service started successfully" - echo "" - echo -e "${GREEN}======================================${NC}" - echo -e "${GREEN}Hidden Service Address:${NC}" - echo -e "${YELLOW}${ONION_ADDR}${NC}" - echo -e "${GREEN}======================================${NC}" - echo "" - else - warn "Tor started but hostname file not yet created" - warn "Check: ls -la ${HS_DIR}/" - fi -} - -# Print usage instructions -print_instructions() { - echo "" - info "Setup complete! To enable Tor in ORLY:" - echo "" - echo " Option 1 - Environment variables:" - echo " export ORLY_TOR_ENABLED=true" - echo " export ORLY_TOR_HS_DIR=${HS_DIR}" - echo " export ORLY_TOR_PORT=${TOR_PORT}" - echo " ./orly" - echo "" - echo " Option 2 - Command line:" - echo " ORLY_TOR_ENABLED=true ORLY_TOR_HS_DIR=${HS_DIR} ./orly" - echo "" - echo " The .onion address will automatically appear in NIP-11 relay info." - echo "" - echo " To view the .onion address:" - echo " cat ${HS_DIR}/hostname" - echo "" - echo " To check Tor status:" - echo " systemctl status tor" - echo "" -} - -# Main -main() { - info "ORLY Tor Hidden Service Setup" - info "Internal port: ${TOR_PORT}" - echo "" - - # Check if Tor is already installed - if ! command -v tor &> /dev/null; then - install_tor - else - info "Tor is already installed" - fi - - configure_tor - set_permissions - restart_tor - print_instructions -} - -main