Files
mesh-controller/vendor/github.com/novox/mesh-host/rootsearch/rootsearch.go
T
jochen cdaba36eca
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery-group group fix/361-the-setuid-search-runs-to-completion checking: 1 of 2 member(s) ready
mesh/delivery superseded: a newer head of the same pull request
Pin the node-engine at its pull request's resumable walk (hq issue 361)
2026-10-10 01:01:18 +02:00

45 lines
2.9 KiB
Go

// Package rootsearch holds what the node-engine's search for setuid programs and the controller that reads its
// verdicts must agree on (novox/hq ADR 0266, issue 361): how often a full search runs, how long a complete one
// judges, how long the controller stays quiet while one runs, and the words a verdict starts with while it has
// no answer. Public, so the controller imports these rather than keeps copies that could drift.
//
// **Why a complete search may judge for a day and more.** The search looks for a setuid- or setgid-root program
// no package owns, a way for the agent account to become root. Only root can make such a file: the agent account
// can neither set the setuid bit on a file root owns nor give a file it owns to root. So a search that walked
// everything stays sound until root acts — and root's acts through the mesh (an apply that changes an account, a
// group, a sudo rule, a package or runs an action) start a new search at once. What is left is root acting by
// hand, which is the operator; FreshFor bounds how long that goes unseen.
//
// **And why no bound on one search.** On a machine with millions of files the walk takes longer than any bound
// worth stating (the control-node's did not finish in fifteen minutes, issue 361), and a search ended early
// judges nothing, so a bound made such a machine never judged. The search runs to its end at idle priority,
// once at a time, keeping how far its walk got every minute so an engine restarted midway carries it on; an
// incomplete search is never "free". A walk resumed more than FreshFor after it started is begun again.
package rootsearch
import "time"
const (
// Every is how often a full search runs at most, counted from the start of the last complete one; an
// apply that changes what root controls starts one sooner.
Every = 24 * time.Hour
// FreshFor is how long a complete search judges, counted from its start: after it, the verdict is "not
// judged yet" until a newer search completes. Every plus Quiet, so a daily search that finishes within
// the controller's quiet leaves no hour unjudged.
FreshFor = Every + Quiet
// Quiet is how long the controller raises nothing while an agent account waits for a search, counted
// from when it first saw it waiting: the longest a full search is expected to take at idle priority on
// the largest machine. Past it, waiting is itself the urgent condition. It never makes a machine free.
Quiet = 12 * time.Hour
)
// The reasons of a root verdict the search could not answer yet.
const (
// ReasonPending starts the reason while no complete search judges: none has finished since the engine's
// state was kept, or the last one is older than FreshFor, and one runs.
ReasonPending = "not judged yet (search running)"
// ReasonIncomplete starts the reason when no complete search judges and the last one failed: it is tried
// again after a back-off.
ReasonIncomplete = "not judged (search incomplete)"
)