Raise agent-root where who can become root is not measured, so the router never reads a missing measure as a no (hq ADR 0259 §8)
A machine where the router or a verified channel runs and the sudo module is absent or does not answer made the probe fail to run, which raises nothing the router reads, so it went on approving there. Each such machine now raises the same urgent condition, saying it was not measured.
This commit is contained in:
@@ -32,8 +32,10 @@ import (
|
|||||||
// or the bus's discovery hearing the verb answered there — so a withheld setting the machine has not acted
|
// or the bus's discovery hearing the verb answered there — so a withheld setting the machine has not acted
|
||||||
// on yet, or a holder answering against its setting, is never taken for closed.
|
// on yet, or a holder answering against its setting, is never taken for closed.
|
||||||
//
|
//
|
||||||
// The measurement is the sudo module's on that machine (`sudo_escalation`); a machine it does not run on, or
|
// The measurement is the sudo module's on that machine (`sudo_escalation`). **What cannot be measured is not a
|
||||||
// that does not answer, is a probe that could not run — said, never taken for "no".
|
// pass**: a machine it does not run on, or that does not answer, raises the same urgent condition, saying it
|
||||||
|
// was not measured — the router reads the condition, and a probe that merely failed to run would leave it
|
||||||
|
// approving there (hq ADR 0259 §8, as amended on 2026-10-09).
|
||||||
|
|
||||||
// kindAgentRoot is the condition an agent able to become root where a trusted party runs raises.
|
// kindAgentRoot is the condition an agent able to become root where a trusted party runs raises.
|
||||||
const kindAgentRoot = "agent-root"
|
const kindAgentRoot = "agent-root"
|
||||||
@@ -183,10 +185,8 @@ func probeAgentRoot(ctx context.Context, d *doctor) ([]conditions.Observation, e
|
|||||||
// Whether the login shell's execute is served where a trusted party runs (novox/hq ADR 0268): the
|
// Whether the login shell's execute is served where a trusted party runs (novox/hq ADR 0268): the
|
||||||
// holder's setting for that machine, and what the bus hears answered there. A discovery that could not be
|
// holder's setting for that machine, and what the bus hears answered there. A discovery that could not be
|
||||||
// asked leaves only the setting, which is said: the probe then cannot show the verb withheld.
|
// asked leaves only the setting, which is said: the probe then cannot show the verb withheld.
|
||||||
|
// A discovery that could not be asked counts execute as served (loginShellServed), and says so.
|
||||||
heard, derr := discoverSeatVerbs(ctx, d.js.Conn())
|
heard, derr := discoverSeatVerbs(ctx, d.js.Conn())
|
||||||
if derr != nil {
|
|
||||||
unread = append(unread, "the bus's discovery could not be asked whether the login shell's execute is served: "+derr.Error())
|
|
||||||
}
|
|
||||||
for _, e := range entries {
|
for _, e := range entries {
|
||||||
if !e.Manifest.ClaimsSeat(loginShellSeat) {
|
if !e.Manifest.ClaimsSeat(loginShellSeat) {
|
||||||
continue
|
continue
|
||||||
@@ -200,8 +200,7 @@ func probeAgentRoot(ctx context.Context, d *doctor) ([]conditions.Observation, e
|
|||||||
if _, declared := e.Manifest.Settings[loginShellVerb]; declared {
|
if _, declared := e.Manifest.Settings[loginShellVerb]; declared {
|
||||||
layers, err := inv.SettingsFor(ctx, node, e.Manifest.Module)
|
layers, err := inv.SettingsFor(ctx, node, e.Manifest.Module)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
unread = append(unread, node+": "+e.Manifest.Module+"'s settings could not be read: "+err.Error())
|
f.LoginShell, f.LoginShellWhy = true, e.Manifest.Module+"'s setting there could not be read: "+err.Error()
|
||||||
f.LoginShell, f.LoginShellWhy = true, "its holder's setting could not be read"
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, s := range catalogue.Effective(e.Manifest, layers) {
|
for _, s := range catalogue.Effective(e.Manifest, layers) {
|
||||||
@@ -265,8 +264,35 @@ func probeAgentRoot(ctx context.Context, d *doctor) ([]conditions.Observation, e
|
|||||||
}
|
}
|
||||||
out = append(out, agentRootObservations(*f)...)
|
out = append(out, agentRootObservations(*f)...)
|
||||||
}
|
}
|
||||||
if len(unread) > 0 {
|
// Each machine whose measure failed is said as not measured, the same condition: never a pass.
|
||||||
return out, fmt.Errorf("who can become root could not be measured: %s", strings.Join(unread, "; "))
|
for _, m := range machines {
|
||||||
|
var why []string
|
||||||
|
for _, u := range unread {
|
||||||
|
if strings.HasPrefix(u, m+": ") {
|
||||||
|
why = append(why, strings.TrimPrefix(u, m+": "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(why) > 0 {
|
||||||
|
out = append(out, agentRootUnmeasured(*facts[m], strings.Join(why, "; ")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// agentRootUnmeasured is the condition of a machine where a trusted party runs and who can become root was
|
||||||
|
// not measured: urgent, like a measured yes, because the router believes a proof only where it is a no.
|
||||||
|
func agentRootUnmeasured(f agentRootFacts, why string) conditions.Observation {
|
||||||
|
trusted := append([]string(nil), f.Trusted...)
|
||||||
|
sort.Strings(trusted)
|
||||||
|
return conditions.Observation{Scope: conditions.ScopeMachine, ID: f.Machine, Token: kindAgentRoot,
|
||||||
|
Machine: f.Machine, Kind: kindAgentRoot, Severity: conditions.Urgent,
|
||||||
|
Summary: fmt.Sprintf("whether an agent can become root on %s without a person is not measured, where %s "+
|
||||||
|
"run as accounts of their own; until it is, the router approves nothing proven there (novox/hq ADR "+
|
||||||
|
"0259 §8): %s", f.Machine, strings.Join(trusted, ", "), why),
|
||||||
|
Headline: "Root not checked on " + f.Machine,
|
||||||
|
Needs: "let the mesh check who can become root on " + f.Machine + ": assign the sudo module there, or bring it back.",
|
||||||
|
Explanation: "The modules that prove your answers from your phone run on " + f.Machine + ", and the mesh " +
|
||||||
|
"could not check whether a program working for you there can become root without asking you. Until " +
|
||||||
|
"it can, answers from your phone can only acknowledge.",
|
||||||
|
Resolved: "The mesh checks who can become root on " + f.Machine + " again"}
|
||||||
|
}
|
||||||
|
|||||||
@@ -100,3 +100,24 @@ func TestTheLoginShellCountsOnlyWhereExecuteIsServed(t *testing.T) {
|
|||||||
t.Errorf("execute still answered: %+v", got)
|
t.Errorf("execute still answered: %+v", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hq ADR 0259 §8 as amended on 2026-10-09: a machine where a trusted party runs and who can become root is not
|
||||||
|
// measured raises the same urgent condition, so the router, which reads the condition, approves nothing there.
|
||||||
|
func TestRootNotMeasuredWhereTheRouterRunsIsSaidAndNeverAPass(t *testing.T) {
|
||||||
|
o := agentRootUnmeasured(agentRootFacts{Machine: "anchor", Trusted: []string{"telegram", "messenger"}},
|
||||||
|
"the sudo module is not assigned there, so who can become root is not measured")
|
||||||
|
if o.Kind != kindAgentRoot || o.Severity != conditions.Urgent || o.Machine != "anchor" ||
|
||||||
|
!strings.Contains(o.Summary, "not measured") || !strings.Contains(o.Summary, "messenger, telegram") {
|
||||||
|
t.Errorf("%+v", o)
|
||||||
|
}
|
||||||
|
if why, ok := conditions.PlainWords(conditions.Words{Headline: o.Headline, Explanation: o.Explanation,
|
||||||
|
Needs: o.Needs, Resolved: o.Resolved}, "anchor"); !ok {
|
||||||
|
t.Errorf("not plain: %s", why)
|
||||||
|
}
|
||||||
|
// The same key as a measured yes, so the router's one rule reads both.
|
||||||
|
measured := agentRootObservations(agentRootFacts{Machine: "anchor", Trusted: []string{"telegram"}, Runtime: "ops",
|
||||||
|
Agent: "ops", Answers: map[string]escalation{"ops": {Root: true, Why: "x"}}})[0]
|
||||||
|
if o.Key() != measured.Key() {
|
||||||
|
t.Errorf("keys differ: %s, %s", o.Key(), measured.Key())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user