Files
tor-guard-relay/scripts/migration/bridge-migration-fix.sh
rE-Bo0t.bx1 9ed70bdb89 v1.1.1 Security Hardening & Config Enhancement 🛡️
🚀 Major architectural release with a near full rewrite of the entrypoint, validation system, diagnostics, and templates. The image is now ~20MB, fully busybox based, more secure, and more flexible for relay and bridge operators.

🔧 Critical Fixes
- Busybox compatible rewrite of OBFS4V_* parsing for values with spaces.
- Rewritten TOR_CONTACT_INFO validation to prevent crash loops.
- Restored bootstrap logs with Log notice stdout.
- Fixed ENV healthchecks and validation order.
- Resolved busybox regex and quoting issues across the script.

 Features and Enhancements
- Added PT_PORT with complete obfs4 bridge compatibility.
- Support for OR_PORT, PT_PORT, EMAIL, and NICKNAME.
- Rewritten bandwidth logic with correct Rate and Burst translation.
- Unified guard, exit, and bridge via TOR_RELAY_MODE.
- Integrated obfs4 with rewritten diagnostics for status, health, fingerprint, and bridge-line.
- Reliable ENV only mode without torrc files.

📦 Build Improvements
- Image reduced ~45MB to ~20MB with busybox only tools.
- Rewritten healthcheck for ENV and mounted configs.
- Four diagnostic tools rewritten to pure busybox sh.
- Weekly rebuilds with latest Alpine and Tor.

📚 Templates and Documentation
- All templates rewritten and updated with bandwidth options and naming alternatives.
- Updated Cosmos and Docker Compose templates for bridge, guard, exit.
- New templates README with full deployment, migration, and config comparisons.
- Revised Claude file with clearer differences and bandwidth notes.

🔒 Security Hardening
- 32 vulnerabilities fixed across critical, high, medium, low categories.
- Non root runtime with UID 100.
- Strict OBFS4V_* whitelist and rewritten validation.
- No exposed diagnostics ports, docker exec only.
- Smaller attack surface with removed binaries.

🚀 Migration Notes
- From v1.1.0: direct upgrade, no config changes, fingerprint preserved.
- From official obfs4 bridge: one time UID fix required, full ENV compatibility afterward.
- Templates include both TOR_ and official naming for smooth migration.

🧩 Compatibility
- Alpine 3.22.2 base, latest Tor from edge.
- AMD64 and ARM64 supported.
- Works with Docker, Compose, Cosmos Cloud, Portainer.
2025-11-14 02:08:27 +08:00

241 lines
11 KiB
Bash
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# bridge-migration-fix.sh - Emergency fix for bridge container crash loop
# Diagnoses volume issues and preserves bridge identity keys
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log() { echo -e "${BLUE} $1${NC}"; }
success() { echo -e "${GREEN}$1${NC}"; }
error() { echo -e "${RED}$1${NC}"; exit 1; }
warn() { echo -e "${YELLOW}⚠️ $1${NC}"; }
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔧 Bridge Migration Emergency Fix"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
CONTAINER_NAME="obfs4-bridge"
VOLUME_NAME="obfs4-data"
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Step 1: Check if container exists and stop it
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log "Step 1: Checking container status..."
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
log "Stopping container ${CONTAINER_NAME}..."
docker stop "${CONTAINER_NAME}" || warn "Failed to stop container gracefully"
fi
success "Container found"
else
warn "Container ${CONTAINER_NAME} not found (this is OK if you haven't deployed yet)"
fi
echo ""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Step 2: Inspect the volume
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log "Step 2: Inspecting volume ${VOLUME_NAME}..."
if ! docker volume inspect "${VOLUME_NAME}" >/dev/null 2>&1; then
error "Volume ${VOLUME_NAME} does not exist! This means all your bridge keys are lost."
fi
success "Volume exists"
# Get volume mountpoint
VOLUME_PATH=$(docker volume inspect "${VOLUME_NAME}" --format '{{.Mountpoint}}')
log "Volume mountpoint: ${VOLUME_PATH}"
echo ""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Step 3: Check volume contents
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log "Step 3: Checking volume contents..."
echo ""
log "📁 Volume contents:"
docker run --rm -v "${VOLUME_NAME}:/data" alpine ls -la /data | sed 's/^/ /'
echo ""
# Check for critical files
log "🔑 Checking for Tor identity keys..."
HAS_KEYS=false
if docker run --rm -v "${VOLUME_NAME}:/data" alpine test -d /data/keys 2>/dev/null; then
success "keys/ directory exists"
docker run --rm -v "${VOLUME_NAME}:/data" alpine ls -la /data/keys 2>/dev/null | sed 's/^/ /' || true
if docker run --rm -v "${VOLUME_NAME}:/data" alpine test -f /data/keys/secret_id_key 2>/dev/null; then
success "secret_id_key found (RSA identity)"
HAS_KEYS=true
fi
if docker run --rm -v "${VOLUME_NAME}:/data" alpine test -f /data/keys/ed25519_master_id_secret_key 2>/dev/null; then
success "ed25519_master_id_secret_key found (Ed25519 identity)"
HAS_KEYS=true
fi
else
warn "keys/ directory NOT found"
fi
if [ "$HAS_KEYS" = false ]; then
error "No Tor identity keys found! Your bridge identity is lost. You'll need to start fresh."
fi
echo ""
# Check for obfs4 state
log "🔐 Checking for obfs4 bridge state..."
if docker run --rm -v "${VOLUME_NAME}:/data" alpine test -d /data/pt_state 2>/dev/null; then
success "pt_state/ directory exists"
docker run --rm -v "${VOLUME_NAME}:/data" alpine ls -la /data/pt_state 2>/dev/null | sed 's/^/ /' || true
if docker run --rm -v "${VOLUME_NAME}:/data" alpine test -f /data/pt_state/obfs4_state.json 2>/dev/null; then
success "obfs4_state.json found (bridge credentials)"
fi
else
warn "pt_state/ directory NOT found (will be regenerated)"
fi
echo ""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Step 4: Check for problematic torrc file
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log "Step 4: Checking for incompatible torrc files..."
TORRC_FOUND=false
# Check common locations for torrc
for location in "/data/torrc" "/data/etc/tor/torrc" "/data/config/torrc"; do
if docker run --rm -v "${VOLUME_NAME}:/data" alpine test -f "$location" 2>/dev/null; then
warn "Found torrc at: $location"
TORRC_FOUND=true
echo ""
log "Contents of $location:"
docker run --rm -v "${VOLUME_NAME}:/data" alpine cat "$location" 2>/dev/null | head -20 | sed 's/^/ /'
echo ""
fi
done
if [ "$TORRC_FOUND" = false ]; then
success "No torrc files found in volume (this is GOOD)"
fi
echo ""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Step 5: Backup current volume state
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log "Step 5: Creating backup of volume..."
BACKUP_FILE="bridge-backup-$(date +%Y%m%d-%H%M%S).tar.gz"
docker run --rm \
-v "${VOLUME_NAME}:/data" \
-v "$(pwd):/backup" \
alpine tar czf "/backup/${BACKUP_FILE}" /data
success "Backup created: ${BACKUP_FILE}"
log "Keep this backup safe - it contains your bridge identity keys!"
echo ""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Step 6: Remove problematic torrc files
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
if [ "$TORRC_FOUND" = true ]; then
log "Step 6: Removing incompatible torrc files..."
for location in "/data/torrc" "/data/etc/tor/torrc" "/data/config/torrc"; do
if docker run --rm -v "${VOLUME_NAME}:/data" alpine test -f "$location" 2>/dev/null; then
docker run --rm -v "${VOLUME_NAME}:/data" alpine rm -f "$location"
success "Removed: $location"
fi
done
# Also remove /data/etc directory if empty
docker run --rm -v "${VOLUME_NAME}:/data" alpine rm -rf /data/etc 2>/dev/null || true
success "All torrc files removed - ENV variables will be used instead"
echo ""
else
log "Step 6: No torrc cleanup needed"
echo ""
fi
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Step 7: Get current fingerprint (if keys exist)
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
if [ "$HAS_KEYS" = true ]; then
log "Step 7: Extracting current bridge fingerprint..."
# Try to get fingerprint from cached-descriptors
if docker run --rm -v "${VOLUME_NAME}:/data" alpine test -f /data/cached-descriptors 2>/dev/null; then
FINGERPRINT=$(docker run --rm -v "${VOLUME_NAME}:/data" alpine grep "^fingerprint" /data/cached-descriptors 2>/dev/null | head -1 | awk '{print $2}' || echo "")
if [ -n "$FINGERPRINT" ]; then
success "Current fingerprint: ${FINGERPRINT}"
log "Save this! You'll verify it matches after migration."
log "Tor Metrics: https://metrics.torproject.org/rs.html#search/${FINGERPRINT}"
else
warn "Could not extract fingerprint from cached-descriptors"
fi
else
warn "cached-descriptors not found (this is OK for new relays)"
fi
echo ""
fi
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Summary and next steps
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
success "Diagnosis and cleanup complete!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
if [ "$HAS_KEYS" = true ]; then
success "✅ Your bridge identity keys are preserved"
else
error "❌ Bridge identity keys NOT found - identity will be lost"
fi
if [ "$TORRC_FOUND" = true ]; then
success "✅ Incompatible torrc files removed"
else
success "✅ No torrc cleanup was needed"
fi
success "✅ Backup created: ${BACKUP_FILE}"
echo ""
log "Next steps:"
echo " 1. Use the corrected Cosmos JSON (will be provided)"
echo " 2. Deploy container with ENV variables (no torrc mount)"
echo " 3. Verify fingerprint matches: docker exec obfs4-bridge fingerprint"
echo " 4. Check health: docker exec obfs4-bridge health | jq ."
echo ""
log "If container still fails, run with DEBUG=true in environment to see exact error"
echo ""