mirror of
https://github.com/r3bo0tbx1/tor-guard-relay.git
synced 2026-04-06 00:32:04 +02:00
🔧 New tool: gen-family - generate/view Happy Family keys - Supports --force flag to overwrite existing keys without backup prompt 🐳 Dockerfiles: gen-family in both Dockerfile and Dockerfile.edge 🔧 Entrypoint: - Phase 2: detect *.secret_family_key, log found keys (informational only) - Guard/exit config gen: append FamilyId + MyFamily from ENV vars - Bridge intentionally excluded 📊 Status tool: show family key count + Happy Family config state 📚 Docs: - README: Happy Family section (generate / import), persistence table, flowchart - ARCHITECTURE: all mermaid diagrams updated (Phase 2, config gen, tools, dirs) - TOOLS: full gen-family reference with examples and exit codes - DEPLOYMENT, MIGRATION, MIGRATION-V1.1.X, TROUBLESHOOTING: 5 -> 6 tools - FAQ, example configs: version bump + FamilyId/MyFamily placeholders - Directory authority voting: how 9 dirauths vote on relay flags (5/9 consensus) - CIISS v2 ContactInfo: field reference, generator link, proof:uri-rsa verification - All TOR_CONTACT_INFO examples updated to CIISS v2 format across templates and docs 📋 Templates: - Guard/exit/multi-relay compose: TOR_FAMILY_ID + TOR_MY_FAMILY env vars - All cosmos-compose + docker-compose versions -> 1.1.7 👷 CI: validate.yml gen-family in 8 spots (threshold 6), security tests, quick-test 🛡️ SECURITY.md: 1.1.7 active, 1.1.6 maintenance, gen-family in tools list 🔖 Version bump 1.1.6 -> 1.1.7 across 30+ files, tool count 5 -> 6, CHANGELOG entry No breaking changes. TOR_FAMILY_ID and TOR_MY_FAMILY are optional.
250 lines
10 KiB
Bash
250 lines
10 KiB
Bash
#!/bin/sh
|
||
# Generate or display Tor Happy Family key and FamilyId
|
||
# Requires Tor >= 0.4.9.x with --keygen-family support
|
||
|
||
set -e
|
||
|
||
GREEN=$(printf '\033[0;32m')
|
||
BLUE=$(printf '\033[0;34m')
|
||
YELLOW=$(printf '\033[1;33m')
|
||
CYAN=$(printf '\033[0;36m')
|
||
RED=$(printf '\033[0;31m')
|
||
BOLD=$(printf '\033[1m')
|
||
NC=$(printf '\033[0m')
|
||
|
||
TOR_DATA_DIR="${TOR_DATA_DIR:-/var/lib/tor}"
|
||
KEYS_DIR="${TOR_DATA_DIR}/keys"
|
||
FORCE=0
|
||
|
||
for arg in "$@"; do
|
||
case "$arg" in
|
||
--force|-f) FORCE=1 ;;
|
||
esac
|
||
done
|
||
|
||
FAMILY_NAME=""
|
||
for arg in "$@"; do
|
||
case "$arg" in
|
||
--force|-f|--help|-h|--show|-s) ;;
|
||
*) FAMILY_NAME="$arg"; break ;;
|
||
esac
|
||
done
|
||
FAMILY_NAME="${FAMILY_NAME:-TorFamily}"
|
||
|
||
show_usage() {
|
||
echo ""
|
||
echo "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
|
||
echo "${CYAN}║${NC} ${BOLD}Tor Happy Family Key Generator${NC} ${CYAN}║${NC}"
|
||
echo "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
|
||
echo ""
|
||
echo "${BOLD}Usage:${NC}"
|
||
echo " gen-family [name] Generate a new family key"
|
||
echo " gen-family [name] --force Overwrite existing key (no backup prompt)"
|
||
echo " gen-family --show Show existing FamilyId"
|
||
echo " gen-family --help Show this help"
|
||
echo ""
|
||
echo "${BOLD}Examples:${NC}"
|
||
echo " docker exec tor-relay gen-family MyRelays"
|
||
echo " docker exec tor-relay gen-family --show"
|
||
echo ""
|
||
echo "${BOLD}What is Happy Family?${NC}"
|
||
echo " Tor 0.4.9+ replaces MyFamily with a cryptographic key-based"
|
||
echo " system. All relays in a family share one secret key file."
|
||
echo " See: https://community.torproject.org/relay/setup/post-install/family-ids/"
|
||
echo ""
|
||
}
|
||
|
||
show_existing() {
|
||
echo ""
|
||
echo "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
|
||
echo "${CYAN}║${NC} ${BOLD}Tor Happy Family - Current Status${NC} ${CYAN}║${NC}"
|
||
echo "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
|
||
echo ""
|
||
|
||
FOUND=0
|
||
for keyfile in "$KEYS_DIR"/*.secret_family_key; do
|
||
[ -f "$keyfile" ] || continue
|
||
FOUND=1
|
||
BASENAME=$(basename "$keyfile" .secret_family_key)
|
||
echo "${GREEN}✓${NC} Found family key: ${BOLD}${BASENAME}${NC}"
|
||
echo " File: ${BLUE}${keyfile}${NC}"
|
||
done
|
||
|
||
if [ "$FOUND" -eq 0 ]; then
|
||
echo "${YELLOW}⚠${NC} No family keys found in ${KEYS_DIR}"
|
||
echo ""
|
||
echo " Generate one with: ${BOLD}gen-family MyRelays${NC}"
|
||
echo ""
|
||
exit 1
|
||
fi
|
||
|
||
TORRC="${TOR_CONFIG:-/etc/tor/torrc}"
|
||
if [ -f "$TORRC" ]; then
|
||
echo ""
|
||
FAMILY_IDS=$(grep -i "^FamilyId " "$TORRC" 2>/dev/null || true)
|
||
if [ -n "$FAMILY_IDS" ]; then
|
||
echo "${GREEN}✓${NC} FamilyId in torrc:"
|
||
echo "$FAMILY_IDS" | while read -r line; do
|
||
echo " ${BLUE}${line}${NC}"
|
||
done
|
||
else
|
||
echo "${YELLOW}⚠${NC} No FamilyId line found in torrc"
|
||
echo " Add the FamilyId line to your torrc to activate Happy Family"
|
||
fi
|
||
fi
|
||
|
||
if [ -f "$TORRC" ]; then
|
||
MY_FAMILY=$(grep -i "^MyFamily " "$TORRC" 2>/dev/null || true)
|
||
if [ -n "$MY_FAMILY" ]; then
|
||
echo ""
|
||
echo "${YELLOW}ℹ${NC} MyFamily lines also present (transition period - this is expected)"
|
||
MY_FAMILY_COUNT=$(echo "$MY_FAMILY" | wc -l)
|
||
echo " ${MY_FAMILY_COUNT} MyFamily entries configured"
|
||
fi
|
||
fi
|
||
|
||
echo ""
|
||
}
|
||
|
||
generate_key() {
|
||
echo ""
|
||
echo "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
|
||
echo "${CYAN}║${NC} ${BOLD}Tor Happy Family Key Generator${NC} ${CYAN}║${NC}"
|
||
echo "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
|
||
echo ""
|
||
|
||
TOR_VERSION=$(tor --version 2>/dev/null | head -n1 || echo "unknown")
|
||
echo "${BOLD}Tor version:${NC} ${TOR_VERSION}"
|
||
echo ""
|
||
|
||
if [ -f "${KEYS_DIR}/${FAMILY_NAME}.secret_family_key" ]; then
|
||
if [ "$FORCE" -eq 1 ]; then
|
||
echo "${YELLOW}⚠${NC} Removing existing family key: ${BOLD}${FAMILY_NAME}${NC}"
|
||
rm -f "${KEYS_DIR}/${FAMILY_NAME}.secret_family_key"
|
||
echo "${GREEN}✓${NC} Old key removed"
|
||
echo ""
|
||
else
|
||
echo "${YELLOW}⚠${NC} Family key '${FAMILY_NAME}' already exists!"
|
||
echo " File: ${BLUE}${KEYS_DIR}/${FAMILY_NAME}.secret_family_key${NC}"
|
||
echo ""
|
||
echo " ${BOLD}To replace without backup:${NC}"
|
||
echo " ${BOLD}gen-family ${FAMILY_NAME} --force${NC}"
|
||
echo ""
|
||
echo " ${BOLD}To replace safely:${NC}"
|
||
echo " 1. Back it up first:"
|
||
echo " ${BOLD}docker cp <container>:${KEYS_DIR}/${FAMILY_NAME}.secret_family_key ./${FAMILY_NAME}.secret_family_key.bak${NC}"
|
||
echo " 2. Remove the old key:"
|
||
echo " ${BOLD}docker exec -u 0 <container> rm ${KEYS_DIR}/${FAMILY_NAME}.secret_family_key${NC}"
|
||
echo " 3. Run gen-family again:"
|
||
echo " ${BOLD}gen-family ${FAMILY_NAME}${NC}"
|
||
echo ""
|
||
echo " ${RED}⚠️ WARNING:${NC} Replacing a family key means ALL relays using it"
|
||
echo " must be updated with the new key and FamilyId. Back up first!"
|
||
echo ""
|
||
echo " ${BOLD}Or use a different name:${NC} gen-family NewFamilyName"
|
||
echo " ${BOLD}View existing keys:${NC} gen-family --show"
|
||
echo ""
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
mkdir -p "$KEYS_DIR" 2>/dev/null || true
|
||
|
||
echo "${BOLD}Generating family key: ${CYAN}${FAMILY_NAME}${NC}"
|
||
echo ""
|
||
|
||
KEYGEN_OUTPUT=$(cd "$KEYS_DIR" && tor --keygen-family "$FAMILY_NAME" 2>&1) || {
|
||
echo "${RED}✗${NC} Failed to generate family key!"
|
||
echo ""
|
||
echo " Output: ${KEYGEN_OUTPUT}"
|
||
echo ""
|
||
echo " This requires Tor >= 0.4.9.2-alpha"
|
||
echo " Your version: ${TOR_VERSION}"
|
||
echo ""
|
||
exit 1
|
||
}
|
||
|
||
FAMILY_ID=$(echo "$KEYGEN_OUTPUT" | grep "^FamilyId " | head -1)
|
||
|
||
if [ -z "$FAMILY_ID" ]; then
|
||
FAMILY_ID=$(echo "$KEYGEN_OUTPUT" | grep "FamilyId" | head -1 | sed 's/.*\(FamilyId .*\)/\1/')
|
||
fi
|
||
|
||
echo "${GREEN}✓${NC} Family key generated successfully!"
|
||
echo ""
|
||
|
||
if [ -n "$FAMILY_ID" ]; then
|
||
echo "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo "${BOLD}1. Add this line to your torrc:${NC}"
|
||
echo "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo ""
|
||
echo " ${GREEN}${FAMILY_ID}${NC}"
|
||
echo ""
|
||
else
|
||
echo "${YELLOW}⚠${NC} Could not extract FamilyId from output."
|
||
echo " Full output:"
|
||
echo " ${KEYGEN_OUTPUT}"
|
||
echo ""
|
||
fi
|
||
|
||
echo "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo "${BOLD}2. Key file location:${NC}"
|
||
echo "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo ""
|
||
echo " ${BLUE}${KEYS_DIR}/${FAMILY_NAME}.secret_family_key${NC}"
|
||
echo ""
|
||
echo "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo "${BOLD}3. For multi-relay setups:${NC}"
|
||
echo "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo ""
|
||
echo " Copy this key to ALL your relays. Each relay needs:"
|
||
echo " • The ${BOLD}.secret_family_key${NC} file in its keys directory"
|
||
echo " • The ${BOLD}FamilyId${NC} line in its torrc"
|
||
echo ""
|
||
echo " Extract from this container:"
|
||
echo " ${BOLD}docker cp <container>:${KEYS_DIR}/${FAMILY_NAME}.secret_family_key .${NC}"
|
||
echo ""
|
||
echo " Copy to another container's volume:"
|
||
echo " ${BOLD}docker cp ${FAMILY_NAME}.secret_family_key <other>:${KEYS_DIR}/${NC}"
|
||
echo ""
|
||
echo "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo ""
|
||
echo "${CYAN}📝 Next steps:${NC}"
|
||
echo " 1. Add the FamilyId line to your torrc (all relays)"
|
||
echo " 2. Copy the .secret_family_key to all relay keys directories"
|
||
echo " 3. Restart your relays: ${BOLD}docker restart tor-relay${NC}"
|
||
echo " 4. Keep MyFamily lines during the transition period"
|
||
echo ""
|
||
echo "${CYAN}⚠️ Important:${NC}"
|
||
echo " • Keep your .secret_family_key ${RED}SECRET${NC} - treat it like a private key"
|
||
echo " • Back up the key file - losing it means regenerating for all relays"
|
||
echo " • The key persists in your Docker volume (${BOLD}/var/lib/tor${NC})"
|
||
echo " • During transition, keep BOTH MyFamily AND FamilyId configured"
|
||
echo ""
|
||
echo "${CYAN}📖 Documentation:${NC}"
|
||
echo " https://community.torproject.org/relay/setup/post-install/family-ids/"
|
||
echo ""
|
||
}
|
||
|
||
ACTION=""
|
||
for arg in "$@"; do
|
||
case "$arg" in
|
||
--help|-h) ACTION="help"; break ;;
|
||
--show|-s) ACTION="show"; break ;;
|
||
--force|-f) ;;
|
||
*) ACTION="generate"; break ;;
|
||
esac
|
||
done
|
||
|
||
case "${ACTION:-help}" in
|
||
help)
|
||
show_usage
|
||
;;
|
||
show)
|
||
show_existing
|
||
;;
|
||
generate)
|
||
generate_key
|
||
;;
|
||
esac
|