Files
tor-guard-relay/tools/gen-family
rE-Bo0t.bx1 be4f2bc125 feat(v1.1.7): Happy Family support (Tor 0.4.9+ FamilyId)
🔧 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.
2026-03-02 16:23:10 +08:00

250 lines
10 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
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/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