Browse Source
- Add pkg/tor package for Tor hidden service integration - Add Tor config options: ORLY_TOR_ENABLED, ORLY_TOR_PORT, ORLY_TOR_HS_DIR, ORLY_TOR_ONION_ADDRESS - Extend NIP-11 relay info with addresses field for .onion URLs - Add fallback relays (Damus, nos.lol, nostr.band, purplepag.es) for profile lookups - Refactor profile fetching to try local relay first, then fallback relays - Add Tor setup documentation and deployment scripts Files modified: - app/config/config.go: Add Tor configuration options - app/handle-relayinfo.go: Add ExtendedRelayInfo with addresses field - app/main.go: Initialize and manage Tor service lifecycle - app/server.go: Add torService field to Server struct - app/web/src/constants.js: Add FALLBACK_RELAYS - app/web/src/nostr.js: Add fallback relay profile fetching - pkg/tor/: New package for Tor hidden service management - docs/TOR_SETUP.md: Documentation for Tor configuration - deploy/orly-tor.service: Systemd service for Tor integration - scripts/tor-*.sh: Setup scripts for Tor development and production 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>main
16 changed files with 1291 additions and 56 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,73 @@
@@ -0,0 +1,73 @@
|
||||
# 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 |
||||
@ -0,0 +1,294 @@
@@ -0,0 +1,294 @@
|
||||
# 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/ |
||||
``` |
||||
@ -0,0 +1,116 @@
@@ -0,0 +1,116 @@
|
||||
package tor |
||||
|
||||
import ( |
||||
"os" |
||||
"path/filepath" |
||||
"strings" |
||||
"sync" |
||||
"time" |
||||
|
||||
"lol.mleku.dev/chk" |
||||
"lol.mleku.dev/log" |
||||
) |
||||
|
||||
// HostnameWatcher watches the Tor hidden service hostname file for changes.
|
||||
// When Tor creates or updates a hidden service, it writes the .onion address
|
||||
// to a file called "hostname" in the HiddenServiceDir.
|
||||
type HostnameWatcher struct { |
||||
hsDir string |
||||
address string |
||||
onChange func(string) |
||||
|
||||
stopCh chan struct{} |
||||
mu sync.RWMutex |
||||
} |
||||
|
||||
// NewHostnameWatcher creates a new hostname watcher for the given HiddenServiceDir.
|
||||
func NewHostnameWatcher(hsDir string) *HostnameWatcher { |
||||
return &HostnameWatcher{ |
||||
hsDir: hsDir, |
||||
stopCh: make(chan struct{}), |
||||
} |
||||
} |
||||
|
||||
// OnChange sets a callback function to be called when the hostname changes.
|
||||
func (w *HostnameWatcher) OnChange(fn func(string)) { |
||||
w.mu.Lock() |
||||
w.onChange = fn |
||||
w.mu.Unlock() |
||||
} |
||||
|
||||
// Start begins watching the hostname file.
|
||||
func (w *HostnameWatcher) Start() error { |
||||
// Try to read immediately
|
||||
if err := w.readHostname(); err != nil { |
||||
log.D.F("hostname file not yet available: %v", err) |
||||
} |
||||
|
||||
// Start polling goroutine
|
||||
go w.poll() |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// Stop stops the hostname watcher.
|
||||
func (w *HostnameWatcher) Stop() { |
||||
close(w.stopCh) |
||||
} |
||||
|
||||
// Address returns the current .onion address.
|
||||
func (w *HostnameWatcher) Address() string { |
||||
w.mu.RLock() |
||||
defer w.mu.RUnlock() |
||||
return w.address |
||||
} |
||||
|
||||
// poll periodically checks the hostname file for changes.
|
||||
func (w *HostnameWatcher) poll() { |
||||
ticker := time.NewTicker(5 * time.Second) |
||||
defer ticker.Stop() |
||||
|
||||
for { |
||||
select { |
||||
case <-w.stopCh: |
||||
return |
||||
case <-ticker.C: |
||||
if err := w.readHostname(); err != nil { |
||||
// Only log at trace level to avoid spam
|
||||
log.T.F("hostname read: %v", err) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// readHostname reads the hostname file and updates the address if changed.
|
||||
func (w *HostnameWatcher) readHostname() error { |
||||
path := filepath.Join(w.hsDir, "hostname") |
||||
|
||||
data, err := os.ReadFile(path) |
||||
if chk.T(err) { |
||||
return err |
||||
} |
||||
|
||||
// Parse the address (file contains "xyz.onion\n")
|
||||
addr := strings.TrimSpace(string(data)) |
||||
if addr == "" { |
||||
return nil |
||||
} |
||||
|
||||
w.mu.Lock() |
||||
oldAddr := w.address |
||||
w.address = addr |
||||
onChange := w.onChange |
||||
w.mu.Unlock() |
||||
|
||||
// Call callback if address changed
|
||||
if addr != oldAddr && onChange != nil { |
||||
onChange(addr) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// HostnameFilePath returns the path to the hostname file.
|
||||
func (w *HostnameWatcher) HostnameFilePath() string { |
||||
return filepath.Join(w.hsDir, "hostname") |
||||
} |
||||
@ -0,0 +1,188 @@
@@ -0,0 +1,188 @@
|
||||
// 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.
|
||||
package tor |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"net" |
||||
"net/http" |
||||
"sync" |
||||
"time" |
||||
|
||||
"github.com/gorilla/websocket" |
||||
"lol.mleku.dev/chk" |
||||
"lol.mleku.dev/log" |
||||
) |
||||
|
||||
// Config holds Tor hidden service configuration.
|
||||
type Config struct { |
||||
// Port is the internal port that Tor forwards .onion traffic to
|
||||
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 |
||||
// Handler is the HTTP handler to serve (typically the main relay handler)
|
||||
Handler http.Handler |
||||
} |
||||
|
||||
// Service manages the Tor hidden service listener.
|
||||
type Service struct { |
||||
cfg *Config |
||||
listener net.Listener |
||||
server *http.Server |
||||
|
||||
// onionAddress is the detected or configured .onion address
|
||||
onionAddress string |
||||
|
||||
// hostname watcher
|
||||
hostnameWatcher *HostnameWatcher |
||||
|
||||
ctx context.Context |
||||
cancel context.CancelFunc |
||||
wg sync.WaitGroup |
||||
mu sync.RWMutex |
||||
} |
||||
|
||||
// New creates a new Tor service with the given configuration.
|
||||
func New(cfg *Config) (*Service, error) { |
||||
if cfg.Port == 0 { |
||||
cfg.Port = 3336 |
||||
} |
||||
|
||||
ctx, cancel := context.WithCancel(context.Background()) |
||||
|
||||
s := &Service{ |
||||
cfg: cfg, |
||||
ctx: ctx, |
||||
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 |
||||
} |
||||
|
||||
// Start initializes the Tor listener and hostname watcher.
|
||||
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) { |
||||
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
|
||||
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) { |
||||
return fmt.Errorf("failed to listen on %s: %w", addr, err) |
||||
} |
||||
|
||||
// Create HTTP server with the provided handler
|
||||
s.server = &http.Server{ |
||||
Handler: s.cfg.Handler, |
||||
ReadTimeout: 30 * time.Second, |
||||
WriteTimeout: 30 * time.Second, |
||||
IdleTimeout: 120 * time.Second, |
||||
} |
||||
|
||||
// Start serving
|
||||
s.wg.Add(1) |
||||
go func() { |
||||
defer s.wg.Done() |
||||
log.I.F("Tor 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) |
||||
} |
||||
}() |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// Stop gracefully shuts down the Tor service.
|
||||
func (s *Service) Stop() error { |
||||
s.cancel() |
||||
|
||||
// Stop hostname watcher
|
||||
if s.hostnameWatcher != nil { |
||||
s.hostnameWatcher.Stop() |
||||
} |
||||
|
||||
// Shutdown HTTP server
|
||||
if s.server != nil { |
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) |
||||
defer cancel() |
||||
if err := s.server.Shutdown(ctx); chk.E(err) { |
||||
return err |
||||
} |
||||
} |
||||
|
||||
// Close listener
|
||||
if s.listener != nil { |
||||
s.listener.Close() |
||||
} |
||||
|
||||
s.wg.Wait() |
||||
log.I.F("Tor service stopped") |
||||
return nil |
||||
} |
||||
|
||||
// OnionAddress returns the current .onion address (without .onion suffix).
|
||||
func (s *Service) OnionAddress() string { |
||||
s.mu.RLock() |
||||
defer s.mu.RUnlock() |
||||
return s.onionAddress |
||||
} |
||||
|
||||
// OnionWSAddress returns the full WebSocket URL for the hidden service.
|
||||
// Format: ws://<address>.onion/
|
||||
func (s *Service) OnionWSAddress() string { |
||||
addr := s.OnionAddress() |
||||
if addr == "" { |
||||
return "" |
||||
} |
||||
// Ensure address ends with .onion
|
||||
if len(addr) >= 6 && addr[len(addr)-6:] != ".onion" { |
||||
addr = addr + ".onion" |
||||
} |
||||
return "ws://" + addr + "/" |
||||
} |
||||
|
||||
// IsRunning returns whether the Tor service is currently running.
|
||||
func (s *Service) IsRunning() bool { |
||||
return s.listener != nil |
||||
} |
||||
|
||||
// Upgrader returns a WebSocket upgrader configured for Tor connections.
|
||||
// Tor connections don't send Origin headers, so we skip origin check.
|
||||
func (s *Service) Upgrader() *websocket.Upgrader { |
||||
return &websocket.Upgrader{ |
||||
ReadBufferSize: 1024, |
||||
WriteBufferSize: 1024, |
||||
CheckOrigin: func(r *http.Request) bool { |
||||
return true // Allow all origins for Tor
|
||||
}, |
||||
} |
||||
} |
||||
@ -0,0 +1,217 @@
@@ -0,0 +1,217 @@
|
||||
#!/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 "$@" |
||||
@ -0,0 +1,197 @@
@@ -0,0 +1,197 @@
|
||||
#!/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 |
||||
Loading…
Reference in new issue