Files
tor-guard-relay/scripts/migration/validate-bridge-migration.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

297 lines
14 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
# validate-bridge-migration.sh - Verify bridge migration success
# Run this AFTER you've deployed the v1.1.1 container
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}"; }
warn() { echo -e "${YELLOW}⚠️ $1${NC}"; }
CONTAINER_NAME="obfs4-bridge"
EXPECTED_FINGERPRINT="${1:-}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🔍 Bridge Migration Validation"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
if [ -n "$EXPECTED_FINGERPRINT" ]; then
log "Expected fingerprint: $EXPECTED_FINGERPRINT"
echo ""
fi
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Test 1: Container running
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log "Test 1: Checking if container is running..."
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
UPTIME=$(docker ps --format '{{.Names}}\t{{.Status}}' | grep "^${CONTAINER_NAME}" | awk '{print $2, $3, $4}')
success "Container is running (uptime: $UPTIME)"
else
error "Container is NOT running!"
log "Check logs with: docker logs ${CONTAINER_NAME}"
exit 1
fi
echo ""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Test 2: Configuration source
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log "Test 2: Verifying configuration source..."
LOGS=$(docker logs "${CONTAINER_NAME}" 2>&1 | tail -100)
if echo "$LOGS" | grep -q "Config source: environment"; then
success "Configuration generated from ENV variables (correct)"
elif echo "$LOGS" | grep -q "Config source: mounted"; then
error "Using MOUNTED configuration (should be environment)!"
warn "This means an old torrc file still exists. Run bridge-migration-fix.sh again."
exit 1
else
warn "Could not determine config source from logs"
fi
echo ""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Test 3: Relay mode
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log "Test 3: Verifying relay mode..."
if echo "$LOGS" | grep -q "Relay mode: bridge"; then
success "Bridge mode detected (correct)"
else
error "Bridge mode NOT detected!"
warn "Check if PT_PORT environment variable is set"
exit 1
fi
echo ""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Test 4: Configuration validation
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log "Test 4: Checking configuration validation..."
if echo "$LOGS" | grep -q "Configuration is valid"; then
success "Configuration validated successfully"
else
error "Configuration validation FAILED!"
log "Recent logs:"
echo "$LOGS" | tail -20 | sed 's/^/ /'
exit 1
fi
echo ""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Test 5: Tor startup
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log "Test 5: Verifying Tor started..."
if echo "$LOGS" | grep -q "Starting Tor relay"; then
success "Tor relay started"
else
error "Tor relay did NOT start!"
exit 1
fi
echo ""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Test 6: Bootstrap progress
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log "Test 6: Checking bootstrap progress..."
if echo "$LOGS" | grep -q "Bootstrapped 100% (done): Done"; then
success "Bootstrap completed (100%)"
elif echo "$LOGS" | grep "Bootstrapped" | tail -1 | grep -qE "Bootstrapped [0-9]+%"; then
BOOTSTRAP=$(echo "$LOGS" | grep "Bootstrapped" | tail -1 | sed -n 's/.*Bootstrapped \([0-9]\+%\).*/\1/p')
warn "Bootstrap in progress: $BOOTSTRAP (wait a few more seconds)"
else
warn "Bootstrap progress not yet visible (container may be starting)"
fi
echo ""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Test 7: Fingerprint check
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log "Test 7: Verifying bridge fingerprint..."
sleep 2 # Give Tor a moment to create keys if needed
CURRENT_FINGERPRINT=$(docker exec "${CONTAINER_NAME}" fingerprint 2>/dev/null | grep -oE '[A-F0-9]{40}' | head -1 || echo "")
if [ -z "$CURRENT_FINGERPRINT" ]; then
warn "Could not retrieve fingerprint yet (may need more time to bootstrap)"
else
success "Current fingerprint: $CURRENT_FINGERPRINT"
if [ -n "$EXPECTED_FINGERPRINT" ]; then
if [ "$CURRENT_FINGERPRINT" = "$EXPECTED_FINGERPRINT" ]; then
success "Fingerprint MATCHES original! Bridge identity preserved! 🎉"
else
error "Fingerprint DOES NOT MATCH!"
error "Expected: $EXPECTED_FINGERPRINT"
error "Current: $CURRENT_FINGERPRINT"
warn "Your bridge identity has been lost. You need to restore from backup!"
exit 1
fi
else
log "No expected fingerprint provided (use: ./validate-bridge-migration.sh YOUR_FINGERPRINT)"
fi
log "Tor Metrics: https://metrics.torproject.org/rs.html#search/${CURRENT_FINGERPRINT}"
fi
echo ""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Test 8: Health check
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log "Test 8: Running health check..."
HEALTH=$(docker exec "${CONTAINER_NAME}" health 2>/dev/null || echo "{}")
if echo "$HEALTH" | jq -e '.status' >/dev/null 2>&1; then
STATUS=$(echo "$HEALTH" | jq -r '.status')
BOOTSTRAP=$(echo "$HEALTH" | jq -r '.bootstrap // "unknown"')
MODE=$(echo "$HEALTH" | jq -r '.relay_mode // "unknown"')
success "Health check passed"
log " Status: $STATUS"
log " Bootstrap: $BOOTSTRAP"
log " Mode: $MODE"
if [ "$MODE" != "bridge" ]; then
warn "Relay mode is '$MODE' (expected 'bridge')"
fi
else
warn "Health check not ready yet (container may still be initializing)"
fi
echo ""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Test 9: obfs4 configuration
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log "Test 9: Verifying obfs4 configuration..."
TORRC=$(docker exec "${CONTAINER_NAME}" cat /etc/tor/torrc 2>/dev/null)
if echo "$TORRC" | grep -q "ServerTransportPlugin obfs4"; then
success "obfs4 transport configured"
else
error "obfs4 transport NOT configured!"
exit 1
fi
if echo "$TORRC" | grep -q "ServerTransportListenAddr"; then
OBFS4_PORT=$(echo "$TORRC" | grep "ServerTransportListenAddr" | awk '{print $4}' | cut -d: -f2)
success "obfs4 listening on port: $OBFS4_PORT"
else
warn "Could not determine obfs4 port"
fi
if echo "$TORRC" | grep -q "BridgeRelay 1"; then
success "BridgeRelay enabled"
else
error "BridgeRelay NOT enabled!"
exit 1
fi
echo ""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Test 10: OBFS4V_* variables processed
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log "Test 10: Checking OBFS4V_* variable processing..."
if echo "$TORRC" | grep -q "AddressDisableIPv6"; then
success "OBFS4V_AddressDisableIPv6 processed"
else
warn "OBFS4V_AddressDisableIPv6 not found (check if OBFS4_ENABLE_ADDITIONAL_VARIABLES=1)"
fi
if echo "$TORRC" | grep -q "MaxMemInQueues"; then
success "OBFS4V_MaxMemInQueues processed"
else
warn "OBFS4V_MaxMemInQueues not found"
fi
echo ""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Test 11: No errors in logs
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
log "Test 11: Checking for errors in logs..."
ERROR_COUNT=$(echo "$LOGS" | grep -i "error" | grep -v "no errors" | wc -l)
WARN_COUNT=$(echo "$LOGS" | grep -i "\[warn\]" | wc -l)
if [ "$ERROR_COUNT" -eq 0 ]; then
success "No errors found in logs"
else
warn "Found $ERROR_COUNT error(s) in logs:"
echo "$LOGS" | grep -i "error" | grep -v "no errors" | tail -5 | sed 's/^/ /'
fi
if [ "$WARN_COUNT" -eq 0 ]; then
success "No warnings found in logs"
else
log "Found $WARN_COUNT warning(s) in logs (this may be normal)"
fi
echo ""
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Summary
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
success "Migration validation complete!"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
if [ -n "$CURRENT_FINGERPRINT" ] && [ -n "$EXPECTED_FINGERPRINT" ] && [ "$CURRENT_FINGERPRINT" = "$EXPECTED_FINGERPRINT" ]; then
success "🎉 Bridge identity PRESERVED! Migration successful!"
echo ""
log "Next steps:"
echo " 1. Wait 10-30 minutes for full bootstrap"
echo " 2. Get bridge line: docker exec ${CONTAINER_NAME} bridge-line"
echo " 3. Monitor Tor Metrics: https://metrics.torproject.org/rs.html#search/${CURRENT_FINGERPRINT}"
echo " 4. Share your bridge line with users who need it"
else
log "Next steps:"
echo " 1. Wait for bootstrap to complete (check logs)"
echo " 2. Verify fingerprint: docker exec ${CONTAINER_NAME} fingerprint"
if [ -n "$EXPECTED_FINGERPRINT" ]; then
echo " 3. Expected fingerprint: $EXPECTED_FINGERPRINT"
fi
echo " 4. Get bridge line: docker exec ${CONTAINER_NAME} bridge-line"
echo " 5. Monitor health: docker exec ${CONTAINER_NAME} health | jq ."
fi
echo ""