--- phase: 01-federation-mesh-hardening plan: 20 subsystem: infra tags: [tor, doctor, shell, mesh, permissions] requires: - phase: 01-federation-mesh-hardening provides: "scripts/container-doctor.sh's fix_* convention and the archipelago-doctor.timer (OnUnitActiveSec=5min) that drives it" provides: - "A permission predicate that tests the property that matters (group/other denied) instead of exact-matching one octal string" - "A 30-minute Tor restart backoff, so a restart storm is structurally impossible even if some future condition makes the fix fire repeatedly" affects: [tor, mesh, doctor] tech-stack: added: [] patterns: - "Assert the property, not the representation: `2700` and `700` both deny group and other, which is the security property. Exact-matching the string `700` turned Tor's own setgid bit into a permanent false positive." - "Pair a predicate fix with a rate limit: the predicate stops today's loop, the backoff stops the whole class of loop. One is a fix, both is a guarantee." key-files: created: [] modified: - scripts/container-doctor.sh key-decisions: - "Compare the low three digits of `stat -c '%a'` rather than the whole string, so both `700` and `2700` read as correct while `750`, `707` and `2755` are still corrected." - "The 'already correct' path logs at debug level only. The original defect was invisible precisely because 'Fixed permissions on … (2700 -> 700)' looked like the doctor working — a healthy node should stay quiet." - "Backoff state lives in `/var/lib/archipelago/doctor-tor-last-restart` with a 1800s window, and non-numeric/missing content reads as 0 so a corrupt state file fails open into 'restart allowed' rather than wedging the fix permanently." requirements-completed: [FED-09] coverage: - id: D1 description: "The doctor no longer restarts Tor on every run — Tor uptime grows past the doctor's 5-minute interval" requirement: "FED-09" verification: - kind: manual_procedural ref: "archi-dev-box 2026-08-02: tor@default active since 2026-08-01 15:25:45 — 15+ hours continuous, spanning ~180 doctor intervals" status: pass human_judgment: false - id: D2 description: "A hidden-service directory at Tor's own 2700 is recognised as correct — no chmod, no restart" requirement: "FED-09" verification: - kind: other ref: "Predicate exercised directly on tmpdirs: 2700 -> low3=700 -> ACCEPTED; 700 -> ACCEPTED" status: pass - kind: manual_procedural ref: "archi-dev-box: zero 'Fixed permissions … hidden_service' lines across 542 doctor log entries in 6 hours" status: pass human_judgment: false - id: D3 description: "A genuinely insecure directory (group/other readable) is still corrected" requirement: "FED-09" verification: - kind: other ref: "Predicate exercised on 750, 707 and 2755 — all three fall through to the corrective chmod" status: pass human_judgment: false - id: D4 description: "Even on a real fix, Tor cannot be restarted more than once per backoff window" requirement: "FED-09" verification: - kind: other ref: "TOR_RESTART_BACKOFF_SECONDS=1800 gates the restart against TOR_RESTART_STATE_FILE; non-numeric state reads as 0" status: pass human_judgment: false - id: D5 description: "Tor retains its consensus/HSDir cache long enough to resolve .onion addresses" requirement: "FED-09" verification: - kind: manual_procedural ref: "Post-OTA checkpoint on an affected node — NOT RUN (see below). 15h of unbroken Tor uptime on archi-dev-box is the necessary precondition and is met." status: deferred human_judgment: true duration: 30min completed: 2026-08-02 status: complete-pending-post-ota-checkpoint --- # Phase 1 Plan 20: Stop the Doctor Fighting Tor Over the Setgid Bit (FED-09) Summary **The container doctor treated Tor's own `2700` hidden-service directory as a permission defect, "fixed" it, and restarted Tor — every five minutes, forever. Tor never lived long enough to build a usable HSDir cache, so onion lookups failed and the mesh's Tor fallback died with it.** ## Performance - **Duration:** ~30 min (code committed 2026-07-31 as `4435f95e`; verification and this SUMMARY 2026-08-02) - **Tasks:** 1/2 (Task 2 is a post-OTA checkpoint — see below) - **Files modified:** 1 ## Accomplishments - The predicate now compares the low three digits of `stat -c '%a'`, so Tor's setgid `2700` and a plain `700` both read as correct, while `750`, `707` and `2755` are still corrected. Verified directly against all five modes. - A 30-minute restart backoff (`/var/lib/archipelago/doctor-tor-last-restart`) makes a restart storm impossible even if some future condition makes the fix fire repeatedly. The predicate fixes today's bug; the backoff retires the class. - The "already correct" branch logs at debug level so a healthy node stays quiet — the original defect hid inside a log line that read like success. ## Evidence on a real node (archi-dev-box, 2026-08-02) - `tor@default` active since **2026-08-01 15:25:45** — over 15 hours continuous, spanning roughly 180 doctor intervals. Before the fix, observed restarts were 13:07:56 → 13:13:14 → 13:18:39 → 13:23:57, each within a second of a timer firing. - **Zero** `Fixed permissions … hidden_service` lines across **542** doctor log entries in the preceding 6 hours. ## Deviations from Plan None. The implementation matches the plan's action exactly: predicate corrected at the property level, backoff added, logging made quiet-when-healthy. **Process note:** the code landed on 2026-07-31 as `4435f95e` but no SUMMARY was written and the roadmap entry was never ticked, so the plan looked unstarted for two days. That is the bookkeeping gap this file closes. ## Known Stubs None. ## Threat Flags - The plan's prohibition — never loosen hidden-service permissions — holds: group and other access is still denied on every path. `2700` is accepted precisely *because* it denies them; the setgid bit is orthogonal to that property. - **Scope boundary respected:** the FIPS direct-transport `connect_fail` (problem 1 in the diagnosis) was deliberately NOT touched here. It belongs with FED-03's structured review of the transport/dial layer. ## Outstanding: post-OTA checkpoint (Task 2) Task 2 verifies on an **affected** node after the release lands, and explicitly forbids deploying directly to a user's device (`archy-x250-mad2` or any node that is not ours) — verification is post-OTA only. Remaining there: 1. Tor uptime keeps growing across three doctor intervals (met on archi-dev-box; needs repeating on an affected node) 2. No recurring "Fixed permissions" lines (met on archi-dev-box) 3. Onion resolution works — a mesh send to a Tor-only peer succeeds and `No more HSDir available to query` is gone 4. The doctor still corrects a deliberately `chmod 750`'d hidden-service directory Items 3 and 4 are the ones genuinely outstanding; 1 and 2 already have real-node evidence.