// 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)" )