Files
tor-guard-relay/docs/BACKUP.md
rE-Bo0t.bx1 90b65ee469 🔖 release(v1.1.6): bind mount fix, full repo audit, workflow fixes
🔧 Entrypoint:
- Detect wrong ownership on /var/lib/tor and /var/lib/tor/keys at startup
  with actionable chown commands before Tor fails cryptically in Phase 4
- Accept DEBUG=TRUE, DEBUG=1, DEBUG=yes (case-insensitive)
- Fix signal trap bug: inner cleanup_verify_tmp no longer overwrites
  the global TERM/INT handler (could skip graceful shutdown)

🛡️ Security:
- Deprecate all versions < v1.1.5 (CVE-2025-15467, OpenSSL CVSS 9.8)
- Add deprecation notice to README and SECURITY.md
- Update lifecycle tables in CHANGELOG and SECURITY

🐛 Bug Fixes:
- Fix bootstrap detection in migrate-from-official.sh
  (parsed non-existent "bootstrap_percent" field — now "bootstrap")
- Fix health JSON docs across 4 files: uptime_seconds → uptime,
  add missing pid/errors fields, correct reachable type to string
- Fix validate.yml: bash -n → sh -n (POSIX script, not bash)

📚 Documentation:
- Add "Bind Mount Ownership" troubleshooting section to README
- Fix chown 1000:1000 typo → 100:101 in TROUBLESHOOTING-BRIDGE-MIGRATION.md
- Add [1.1.6] changelog entry
- Update version references across 20+ files to v1.1.6
- Update 47x alpine:3.22.2 → 3.23.3 across migration docs/scripts
- Fix tool count 4 → 5 in DEPLOYMENT, ARCHITECTURE, TROUBLESHOOTING
- Remove 5 broken links (CLAUDE.md, CONTRIBUTORS.md, SECURITY-AUDIT-REPORT.md)
- Fix stale image tags (:1.1.1/:1.1.2 → :latest) in 4 files
- Rewrite PR template as clean reusable form

⚙️ Workflow (release.yml):
- Fix duplicate title in release body (name + body both had ## 🧅 header)
- Fix trailing --- not being stripped from changelog extract
- Fix Full Changelog link comparing current tag to itself
- Extract Alpine version from Dockerfile instead of hardcoding
- Add fetch-depth: 0 for git history in release-notes job
- Fix fallback commit range when no conventional commits found

🐳 Dockerfiles:
- Fix stale base.name label (alpine:3.23.0 → alpine:3.23.3)
- Fix trailing whitespace after backslash in Dockerfile.edge

📋 Templates:
- Update cosmos-compose and docker-compose versions to 1.1.6
2026-02-08 16:04:22 +05:30

15 KiB

🔐 Backup & Recovery Guide - Tor Guard Relay

Complete instructions for backing up and restoring your Tor relay's identity, keys, and configuration data.


Table of Contents


Why Backups Matter

Your Tor relay's identity is permanent. Once established, it becomes part of the Tor network's fabric. Losing these keys means:

  • 🚫 Loss of your relay's fingerprint
  • 📉 Loss of reputation built over time
  • 🔄 New relay starting from zero
  • 8+ days to regain guard flag

Backup your keys immediately after first successful bootstrap.


What to Backup

🔑 Critical Files (Preserve Forever)

Located in /var/lib/tor/:

File Purpose Restore Impact
keys/ed25519_master_id_secret_key Master identity key CRITICAL - Defines relay identity
keys/ed25519_signing_secret_key Signing key CRITICAL - Signs all operations
keys/secret_onion_key Onion key CRITICAL - Onion address generation
fingerprint Your relay fingerprint Reference only (can regenerate)

📋 Important Files (Backup Regularly)

File Purpose Restore Impact
cached-consensus Current Tor consensus Nice to have (rebuilds automatically)
cached-descriptors Relay descriptors Nice to have (rebuilds automatically)
state Relay state file Optional (recreated on startup)

⚙️ Configuration (Backup Before Changes)

File Location Purpose
relay.conf Host machine Your relay configuration
torrc /etc/tor/torrc (in container) Mounted copy of relay.conf

Backup Methods

Quick Backup (Simplest)

Copy the keys directory straight out of a running container:

docker cp <container-name>:/var/lib/tor/keys ./RelayKeyBackup

That's it. The RelayKeyBackup/ folder now contains your relay's identity keys. Store it somewhere safe.


Pros: Complete, easy to restore, version-controlled
Cons: Requires disk space for full volume copy

Step 1: Stop the Relay Gracefully

# Stop relay (allows clean shutdown)
docker stop guard-relay

# Wait for graceful shutdown
sleep 5

# Verify stopped
docker ps | grep guard-relay

Step 2: Backup the Volume

# Create backup directory
mkdir -p ~/tor-relay-backups/$(date +%Y-%m-%d)

# Backup the tor-guard-data volume
docker run --rm \
  -v tor-guard-data:/data \
  -v ~/tor-relay-backups/$(date +%Y-%m-%d):/backup \
  alpine tar czf /backup/tor-data-$(date +%s).tar.gz -C /data .

# Verify backup created
ls -lh ~/tor-relay-backups/$(date +%Y-%m-%d)/

Output:

-rw-r--r-- 1 user user 2.5M Jan 1 12:00 tor-data-1704110400.tar.gz

Step 3: Backup the Logs Volume

# Backup logs for audit trail
docker run --rm \
  -v tor-guard-logs:/data \
  -v ~/tor-relay-backups/$(date +%Y-%m-%d):/backup \
  alpine tar czf /backup/tor-logs-$(date +%s).tar.gz -C /data .

# Verify
ls -lh ~/tor-relay-backups/$(date +%Y-%m-%d)/

Step 4: Restart the Relay

# Restart relay
docker start guard-relay

# Monitor startup
docker logs -f guard-relay

Method 2: Direct Key Extraction

Pros: Minimal, extracts only critical keys
Cons: Manual process, easier to miss files

Extract Keys While Running

# Create secure backup directory
mkdir -p ~/tor-relay-backups/keys-only
chmod 700 ~/tor-relay-backups/keys-only

# Extract keys directly from container
docker exec guard-relay tar czf - -C /var/lib/tor/keys . | \
  tar xzf - -C ~/tor-relay-backups/keys-only/

# Verify contents
ls -la ~/tor-relay-backups/keys-only/

Expected output:

ed25519_master_id_secret_key
ed25519_signing_secret_key
secret_onion_key

Secure the Backup

# Set restrictive permissions
chmod 600 ~/tor-relay-backups/keys-only/*

# Verify ownership
ls -la ~/tor-relay-backups/keys-only/

Method 3: Automated Daily Backup

Pros: Hands-off, versioned history
Cons: Requires cron setup, disk space

Create Backup Script

#!/bin/bash
# Save as: /usr/local/bin/backup-tor-relay.sh

set -euo pipefail

BACKUP_DIR="/backups/tor-relay"
RETENTION_DAYS=30
CONTAINER="guard-relay"
TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S)

# Create backup directory
mkdir -p "$BACKUP_DIR/$TIMESTAMP"

# Stop relay gracefully
echo "🛑 Stopping relay..."
docker stop "$CONTAINER" || true
sleep 5

# Backup data volume
echo "💾 Backing up data volume..."
docker run --rm \
  -v tor-guard-data:/data \
  -v "$BACKUP_DIR/$TIMESTAMP":/backup \
  alpine tar czf /backup/tor-data.tar.gz -C /data .

# Backup logs volume
echo "📝 Backing up logs..."
docker run --rm \
  -v tor-guard-logs:/data \
  -v "$BACKUP_DIR/$TIMESTAMP":/backup \
  alpine tar czf /backup/tor-logs.tar.gz -C /data .

# Extract fingerprint for reference
docker start "$CONTAINER"
sleep 10
docker exec "$CONTAINER" cat /var/lib/tor/fingerprint > "$BACKUP_DIR/$TIMESTAMP/fingerprint.txt" || true

# Create manifest
cat > "$BACKUP_DIR/$TIMESTAMP/MANIFEST.txt" << EOF
Tor Guard Relay Backup
Timestamp: $TIMESTAMP
Container: $CONTAINER
Relay: $(grep Nickname /opt/tor-relay/relay.conf | cut -d' ' -f2)
Files:
  - tor-data.tar.gz (Relay identity and state)
  - tor-logs.tar.gz (Tor logs for audit)
  - fingerprint.txt (Relay fingerprint reference)
EOF

# Cleanup old backups (keep last 30 days)
echo "🧹 Cleaning up old backups..."
find "$BACKUP_DIR" -maxdepth 1 -type d -mtime +$RETENTION_DAYS -exec rm -rf {} \;

# Summary
echo "✅ Backup complete: $BACKUP_DIR/$TIMESTAMP"
du -sh "$BACKUP_DIR/$TIMESTAMP"

Make Executable

chmod +x /usr/local/bin/backup-tor-relay.sh

Add to Cron (Daily at 2 AM)

# Edit crontab
crontab -e

# Add line:
0 2 * * * /usr/local/bin/backup-tor-relay.sh >> /var/log/tor-backup.log 2>&1

Monitor Backup Logs

# View backup logs
tail -f /var/log/tor-backup.log

# Check backup history
ls -lah /backups/tor-relay/

Method 4: Off-Site Backup (Cloud/External)

Pros: Disaster recovery, geographic redundancy
Cons: Security risk if not encrypted, potential costs

Encrypt Backup Before Upload

# Generate encryption key (save this somewhere secure!)
openssl rand -base64 32 > ~/tor-relay-backup.key

# Encrypt backup before upload
gpg --symmetric --cipher-algo AES256 \
  --output tor-data-encrypted.tar.gz.gpg \
  tor-data-$(date +%s).tar.gz

# Upload to cloud (e.g., AWS S3)
aws s3 cp tor-data-encrypted.tar.gz.gpg s3://my-backups/tor-relay/

Decrypt When Needed

# Decrypt backup
gpg --decrypt tor-data-encrypted.tar.gz.gpg > tor-data-restored.tar.gz

# Verify integrity
tar tzf tor-data-restored.tar.gz | head -10

Recovery Procedures

Scenario 1: Container Corruption

Problem: Container is running but data is corrupted
Recovery time: 15 minutes

Steps

# 1. Stop the relay
docker stop guard-relay

# 2. Remove corrupted volume
docker volume rm tor-guard-data

# 3. Create new volume
docker volume create tor-guard-data

# 4. Restore from backup
docker run --rm \
  -v tor-guard-data:/data \
  -v ~/tor-relay-backups/2024-01-01:/backup \
  alpine tar xzf /backup/tor-data-1704110400.tar.gz -C /data

# 5. Verify permissions
docker exec guard-relay chown -R tor:tor /var/lib/tor

# 6. Restart relay
docker start guard-relay

# 7. Monitor startup
docker logs -f guard-relay

Scenario 2: Server Failure - Full Restore

Problem: Entire server lost, migrating to new hardware
Recovery time: 30 minutes

Steps

# 1. Prepare new server with Docker

# 2. Copy backup to new server
scp -r ~/tor-relay-backups/2024-01-01 user@new-server:/tmp/

# 3. On new server, create volume and restore
docker volume create tor-guard-data
docker run --rm \
  -v tor-guard-data:/data \
  -v /tmp/2024-01-01:/backup \
  alpine tar xzf /backup/tor-data-1704110400.tar.gz -C /data

# 4. Copy relay configuration
scp /opt/tor-relay/relay.conf user@new-server:/opt/tor-relay/

# 5. On new server, start relay
docker run -d \
  --name guard-relay \
  --network host \
  -v /opt/tor-relay/relay.conf:/etc/tor/torrc:ro \
  -v tor-guard-data:/var/lib/tor \
  -v tor-guard-logs:/var/log/tor \
  --restart unless-stopped \
  r3bo0tbx1/onion-relay:latest

# 6. Verify relay is using old identity
docker exec guard-relay fingerprint
# Should match original fingerprint!

Scenario 3: Key Loss - Emergency Recovery

Problem: All keys lost, only backup exists
Recovery time: 5 minutes

Restore Keys Only

# Stop relay
docker stop guard-relay

# Clear tor data
docker run --rm \
  -v tor-guard-data:/data \
  alpine rm -rf /data/*

# Restore from backup
docker run --rm \
  -v tor-guard-data:/data \
  -v ~/tor-relay-backups/keys-only:/backup \
  alpine bash -c "cp -r /backup/* /data/"

# Fix permissions
docker exec guard-relay chown -R tor:tor /var/lib/tor
docker exec guard-relay chmod 700 /var/lib/tor/keys

# Restart
docker start guard-relay

# Verify identity recovered
docker exec guard-relay fingerprint

Migration Guide

Move Relay to New Server (Same Identity)

Goal: Keep relay fingerprint, move to new hardware

Pre-Migration Checklist

  • Recent full backup created
  • New server prepared with Docker
  • Network firewall rules ready
  • DNS/IP planning done
  • Maintenance window scheduled

Step-by-Step Migration

# === ON OLD SERVER ===

# 1. Create final backup
docker stop guard-relay
sleep 5
docker run --rm \
  -v tor-guard-data:/data \
  -v ~/tor-relay-backups/migration:/backup \
  alpine tar czf /backup/tor-data-final.tar.gz -C /data .

# 2. Verify backup
ls -lh ~/tor-relay-backups/migration/

# === TRANSFER TO NEW SERVER ===

# 3. Copy backup securely
scp -r ~/tor-relay-backups/migration user@new-server:/tmp/

# 4. Copy relay configuration
scp /opt/tor-relay/relay.conf user@new-server:/opt/tor-relay/

# === ON NEW SERVER ===

# 5. Create volume and restore
docker volume create tor-guard-data
docker run --rm \
  -v tor-guard-data:/data \
  -v /tmp/migration:/backup \
  alpine tar xzf /backup/tor-data-final.tar.gz -C /data

# 6. Start relay on new server
docker run -d \
  --name guard-relay \
  --network host \
  -v /opt/tor-relay/relay.conf:/etc/tor/torrc:ro \
  -v tor-guard-data:/var/lib/tor \
  -v tor-guard-logs:/var/log/tor \
  --restart unless-stopped \
  r3bo0tbx1/onion-relay:latest

# 7. Verify startup and identity
docker logs -f guard-relay
docker exec guard-relay fingerprint

# === FINAL VERIFICATION ===

# 8. Check on Tor Metrics (should recognize old fingerprint within hours)
# https://metrics.torproject.org/rs.html

# 9. After verification, on old server:
docker stop guard-relay
docker rm guard-relay

Verification After Migration

# Check logs for successful bootstrap
docker logs guard-relay 2>&1 | grep "Bootstrapped 100"

# Verify fingerprint matches backup
docker exec guard-relay fingerprint
# Compare with: cat ~/tor-relay-backups/migration/fingerprint.txt

# Monitor for 24 hours for any issues
docker stats guard-relay --no-stream

Zero-Downtime Migration (Advanced)

Goal: Migrate relay without downtime by running dual servers

Setup

# 1. OLD SERVER: Create backup
docker stop guard-relay
docker run --rm \
  -v tor-guard-data:/data \
  -v ~/tor-relay-backups/dual:/backup \
  alpine tar czf /backup/tor-data.tar.gz -C /data .

# 2. NEW SERVER: Restore backup and start
docker volume create tor-guard-data
docker run --rm \
  -v tor-guard-data:/data \
  -v /tmp/dual:/backup \
  alpine tar xzf /backup/tor-data.tar.gz -C /data

docker run -d \
  --name guard-relay \
  --network host \
  -v /opt/tor-relay/relay.conf:/etc/tor/torrc:ro \
  -v tor-guard-data:/var/lib/tor \
  -v tor-guard-logs:/var/log/tor \
  --restart unless-stopped \
  r3bo0tbx1/onion-relay:latest

# 3. Verify NEW server is running
docker logs guard-relay | grep "Bootstrapped"

# 4. Wait 30 minutes for NEW server to stabilize
sleep 1800

# 5. OLD SERVER: Restart old relay
docker start guard-relay

# 6. Both servers now running same relay identity
# Tor network handles this gracefully

# 7. OLD SERVER: After 24 hours, shut down
docker stop guard-relay

Best Practices

DO

  • Backup immediately after bootstrap - Preserve identity
  • Use strong encryption for off-site backups
  • Test restores regularly - Backups are worthless if unverifiable
  • Document fingerprints - Keep reference copy of fingerprint
  • Automate backups - Set and forget with cron
  • Store backups securely - Encrypt sensitive data
  • Keep multiple copies - Local + off-site minimum
  • Version your backups - Date-stamped directories

DON'T

  • Don't backup /etc/tor/torrc - Mount as read-only from host
  • Don't share backup media unencrypted - Keys are sensitive
  • Don't rely on single backup - 3-2-1 rule applies
  • Don't ignore backup failures - Monitor logs
  • Don't delete old backups immediately - Keep 30+ days

📊 3-2-1 Backup Rule

Maintain at minimum:

  • 3 copies of your data
    • Original (running relay)
    • Backup 1 (local storage)
    • Backup 2 (off-site)
  • 2 different media types
    • NVMe/SSD
    • USB external drive
  • 1 off-site copy
    • Cloud storage (encrypted)
    • Or remote server

Troubleshooting

Backup Failed: "Volume is in use"

# Problem: Cannot backup running volume
# Solution: Stop relay first

docker stop guard-relay
sleep 5
# Retry backup command

Restore Failed: "File permissions denied"

# Problem: Restored files have wrong ownership
# Solution: Fix permissions

docker exec guard-relay chown -R tor:tor /var/lib/tor
docker exec guard-relay chmod 700 /var/lib/tor/keys

Fingerprint Changed After Restore

# Problem: Restored relay has different fingerprint
# Cause: Keys weren't backed up, only state
# Solution: Use Method 2 (Direct Key Extraction) for future backups

# For now, accept new identity:
docker exec guard-relay fingerprint
# This is your new permanent fingerprint

Reference

Backup Command Cheat Sheet:

# Quick backup (stop relay)
docker stop guard-relay && \
docker run --rm -v tor-guard-data:/data -v ~/backups:/backup alpine tar czf /backup/tor-$(date +%s).tar.gz -C /data . && \
docker start guard-relay

# Quick restore
docker run --rm -v tor-guard-data:/data -v ~/backups:/backup alpine tar xzf /backup/tor-1704110400.tar.gz -C /data

# Verify backup integrity
tar tzf ~/backups/tor-1704110400.tar.gz | head -20

# Calculate backup size
du -sh ~/backups/tor-1704110400.tar.gz

Support