fix(01-20): stop doctor from fighting Tor over the setgid bit
fix_tor_permissions() exact-matched stat's output against the literal string "700", but stat -c '%a' omits leading zeros so Tor's own setgid HiddenServiceDir mode (2700) never matched. Every ~5-minute doctor run "fixed" it back to 700, restarted Tor, and Tor immediately set 2700 again — so Tor never survived long enough to build a usable consensus/HSDir cache, breaking the mesh's Tor fallback entirely. - Compare only the last 3 mode digits (owner/group/other), which is the property that actually matters, so 700 and 2700 both pass while 750/707/2755 etc. are still corrected. - Add a 30-minute restart backoff (timestamp file under /var/lib/archipelago/) so no future condition can reproduce a restart storm even if the fix fires repeatedly. - Log clearly in both directions: a debug-level no-op line when a directory is already correct, and an explicit "was NOT fully denied" line when a real fix is applied, plus a line when a restart is skipped by the backoff. Verified statically against temp directories (2700 accepted with 0 restarts; 750/707/2755 corrected with exactly 1 restart; a second real fix inside the backoff window logs a skip instead of restarting). No live Tor/doctor/systemd unit was touched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
56cc94237b
commit
4435f95ef3
@@ -10,7 +10,8 @@
|
||||
# 1. Stale podman ps/stats processes (>10 = pileup)
|
||||
# 2. Orphaned conmon/crun processes holding ports
|
||||
# 3. System tor conflicting with container tor
|
||||
# 4. Tor hidden service directory permissions (must be 700)
|
||||
# 4. Tor hidden service directory permissions (group/other must have no
|
||||
# access; Tor's own setgid 2700 is fine, restart is backed off)
|
||||
# 5. SearXNG read-only root / cap-drop ALL
|
||||
# 6. Bitcoin Knots prune+txindex conflict
|
||||
# 7. Containers stuck with exit code 127 (binary not found)
|
||||
@@ -137,6 +138,26 @@ fix_system_tor_conflict() {
|
||||
}
|
||||
|
||||
# ── Fix 4: Tor hidden service permissions ────────────────────
|
||||
# `stat -c '%a'` omits leading zeros, so a directory Tor manages itself
|
||||
# reads "2700" (setgid bit set) rather than "700" -- but the setgid bit
|
||||
# doesn't grant group/other any access. Comparing the full string against
|
||||
# the literal "700" therefore flagged Tor's own correct setting as broken
|
||||
# on every run, chmod'd it right back to what it already effectively was,
|
||||
# and restarted Tor -- forever, every ~5 minutes (the doctor timer
|
||||
# interval), so Tor never kept its consensus/HSDir cache long enough to
|
||||
# resolve any .onion peer. Only the last 3 digits of the mode are the
|
||||
# owner/group/other permission bits; anything before that is
|
||||
# setuid/setgid/sticky, which is irrelevant to "is group/other access
|
||||
# denied". Compare only those 3 digits so 700 and 2700 both pass, while a
|
||||
# genuinely permissive mode (750, 707, 2755, ...) is still corrected.
|
||||
#
|
||||
# Restart backoff: even a real, legitimate fix must not restart Tor more
|
||||
# than once per TOR_RESTART_BACKOFF_SECONDS, so no future defect in this
|
||||
# (or any other) predicate can reproduce a restart storm. The timestamp
|
||||
# file persists across doctor runs (and doctor is re-invoked from a fresh
|
||||
# shell every run, so this can't just be a shell variable).
|
||||
TOR_RESTART_BACKOFF_SECONDS=1800 # 30 minutes
|
||||
TOR_RESTART_STATE_FILE="/var/lib/archipelago/doctor-tor-last-restart"
|
||||
fix_tor_permissions() {
|
||||
local fixed=false
|
||||
local tor_dirs=("/var/lib/archipelago/tor" "/var/lib/tor")
|
||||
@@ -148,17 +169,33 @@ fix_tor_permissions() {
|
||||
while IFS= read -r dir; do
|
||||
local perms
|
||||
perms=$(stat -c '%a' "$dir" 2>/dev/null)
|
||||
if [ "$perms" != "700" ]; then
|
||||
local mode_bits="${perms: -3}"
|
||||
if [ "$mode_bits" != "700" ]; then
|
||||
chmod 700 "$dir"
|
||||
log "Fixed permissions on $dir ($perms -> 700)"
|
||||
log "Fixed permissions on $dir ($perms -> 700; group/other access was NOT fully denied)"
|
||||
fixed=true
|
||||
elif [ -n "$DOCTOR_DEBUG" ]; then
|
||||
log "DEBUG: $dir already denies group/other access (mode $perms) — no change"
|
||||
fi
|
||||
done < <(find "$base" -maxdepth 1 -name "hidden_service_*" -type d 2>/dev/null)
|
||||
done
|
||||
|
||||
# If we fixed permissions, restart system Tor to pick up the changes
|
||||
# If we fixed a real permission defect, restart system Tor to pick up
|
||||
# the change -- but never more than once per backoff window.
|
||||
if $fixed; then
|
||||
systemctl restart tor@default 2>/dev/null || true
|
||||
local now last_restart elapsed
|
||||
now=$(date +%s)
|
||||
last_restart=$(cat "$TOR_RESTART_STATE_FILE" 2>/dev/null || echo 0)
|
||||
case "$last_restart" in *[!0-9]*|"") last_restart=0 ;; esac
|
||||
elapsed=$((now - last_restart))
|
||||
if [ "$elapsed" -ge "$TOR_RESTART_BACKOFF_SECONDS" ]; then
|
||||
systemctl restart tor@default 2>/dev/null || true
|
||||
mkdir -p "$(dirname "$TOR_RESTART_STATE_FILE")" 2>/dev/null
|
||||
echo "$now" > "$TOR_RESTART_STATE_FILE" 2>/dev/null
|
||||
log "Restarted Tor to apply hidden-service permission fix"
|
||||
else
|
||||
log "Skipped Tor restart: last restart was ${elapsed}s ago (backoff window ${TOR_RESTART_BACKOFF_SECONDS}s) — permissions were still corrected"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
|
||||
Reference in New Issue
Block a user