#!/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 :${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 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 :${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 :${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