Files
mesh-host/internal/profile/detectors.go
T
jschoubben ee2648188d Repoint ADR references after HQ consolidated 65 records to 23
96 comments across the two repos named records that no longer exist. Each now
points at the consolidated record that holds its reasoning -- ADR 0034 (a test
defends a decision) is 0017, the eight host records are 0005, the four lab
records are 0016.

Worth noting for next time: these are references from outside HQ, so renumbering
there is not free. It cost 38 files here.
2026-08-28 23:33:44 +02:00

207 lines
7.4 KiB
Go

package profile
import (
"context"
"fmt"
"os"
"strings"
)
// Named capabilities. Constants rather than strings at the call site, because a capability
// nothing declares is a capability nothing can require, and a typo would produce exactly that.
const (
CapContainerRuntime = "container-runtime"
CapPackageManager = "package-manager"
CapServiceManager = "service-manager"
CapFirewall = "firewall"
CapOverlay = "overlay"
CapGraphicalSession = "graphical-session"
CapPrivileged = "privileged"
)
// commandCapability is the shape most detectors take: run something, and treat a working
// invocation as evidence.
//
// It runs a command that only succeeds if the thing is FUNCTIONING, never `--version` alone.
// A version string proves a binary is on disk, which is the assumption 04-ISSUES/007 records
// as false: the package was installed and the daemon was not running.
type commandCapability struct {
name string
command string
args []string
// why describes what a success actually proves, and is reported as the detector's `How`.
why string
// interpret decides the verdict from what the command said and how it exited.
//
// Exists because "exit zero" is not a universal answer. A degraded service manager reports
// its state on stdout and exits non-zero — it is running, and reading only the exit code
// declared no service manager on a machine whose init it was. That is 04-ISSUES/007 in the
// mirror: 007 is installed-but-broken reported present; this is working-but-imperfect
// reported absent. Both place work wrongly, and this one was only visible by running
// against a real machine.
//
// nil means the ordinary rule: success is exit zero.
interpret func(stdout string, err error) (present bool, detail string)
runner Runner
}
func (c commandCapability) Name() string { return c.name }
func (c commandCapability) Detect(ctx context.Context) Verdict {
out, err := c.runner(ctx, c.command, c.args...)
interpret := c.interpret
if interpret == nil {
interpret = exitZero
}
present, detail := interpret(out, err)
if strings.TrimSpace(detail) == "" {
// A verdict with no reason is the fault in a new place: something nobody can act on.
// Reached when a command fails silently, which systemctl does.
if present {
detail = "responded"
} else {
detail = fmt.Sprintf("%s gave no reason", c.command)
}
}
return Verdict{Name: c.name, Present: present, Detail: firstLine(detail), How: c.why}
}
// exitZero is the ordinary rule: the command worked, so the capability is there.
func exitZero(stdout string, err error) (bool, string) {
if err != nil {
return false, err.Error()
}
return true, stdout
}
// systemRunning reads what an init system says about itself rather than how it exited.
//
// `is-system-running` exits non-zero for every state except `running` — including `degraded`,
// which means units failed and the init is emphatically present. Treating that as absent made
// a machine running systemd report no service manager.
func systemRunning(stdout string, err error) (bool, string) {
state := strings.TrimSpace(firstLine(stdout))
switch state {
case "running", "degraded", "starting", "maintenance", "stopping":
return true, state
case "":
if err != nil {
return false, err.Error()
}
return false, "said nothing"
default:
// `offline` and `unknown` mean it is not managing this machine.
return false, state
}
}
func firstLine(s string) string {
s = strings.TrimSpace(s)
if i := strings.IndexByte(s, '\n'); i >= 0 {
s = s[:i]
}
if len(s) > 200 {
s = s[:200] + "…"
}
return s
}
// privileged reports whether the host can change this machine at all.
//
// Reported as a capability rather than checked at startup on purpose: a host that cannot act
// is still a host that can report, and novox/hq ADR 0004 says what varies between nodes lives
// here rather than in the definition of a node.
type privileged struct{}
func (privileged) Name() string { return CapPrivileged }
func (privileged) Detect(context.Context) Verdict {
uid := os.Geteuid()
if uid == 0 {
return Verdict{
Name: CapPrivileged, Present: true,
Detail: "effective uid 0",
How: "effective uid — the host changes a machine, which needs root",
}
}
return Verdict{
Name: CapPrivileged, Present: false,
Detail: fmt.Sprintf("effective uid %d, not 0", uid),
How: "effective uid — the host changes a machine, which needs root",
}
}
// graphicalSession reports whether anything could display a window here.
//
// Environment rather than a probe, because a display server is reachable through a socket a
// detector would have to guess at, and the variables are what an application would use anyway.
// Stated so the limit is visible: this detects that a session is ADVERTISED, which is weaker
// than the other detectors here.
type graphicalSession struct{}
func (graphicalSession) Name() string { return CapGraphicalSession }
func (graphicalSession) Detect(context.Context) Verdict {
const how = "DISPLAY / WAYLAND_DISPLAY — weaker than the other checks: advertised, not probed"
if d := os.Getenv("WAYLAND_DISPLAY"); d != "" {
return Verdict{Name: CapGraphicalSession, Present: true, Detail: "wayland: " + d, How: how}
}
if d := os.Getenv("DISPLAY"); d != "" {
return Verdict{Name: CapGraphicalSession, Present: true, Detail: "x11: " + d, How: how}
}
return Verdict{
Name: CapGraphicalSession, Present: false,
Detail: "neither DISPLAY nor WAYLAND_DISPLAY is set",
How: how,
}
}
// Default returns the detectors the host runs when nobody says otherwise.
//
// Each command is chosen to prove the thing WORKS rather than exists:
// - the container runtime is asked for server-side information, which fails when the daemon
// is down even though the client is installed — the exact shape of 04-ISSUES/007;
// - the service manager is asked whether it is the running init, not whether it is present;
// - the firewall is asked to list a ruleset, which needs both the tool and the permission.
func Default(runner Runner) []Detector {
if runner == nil {
runner = ExecRunner
}
return []Detector{
privileged{},
graphicalSession{},
commandCapability{
name: CapContainerRuntime, command: "docker", args: []string{"info", "--format", "{{.ServerVersion}}"},
why: "asks the daemon for its version — a running daemon, not an installed client",
runner: runner,
},
commandCapability{
name: CapPackageManager, command: "pacman", args: []string{"-Q", "pacman"},
why: "queries the package database — a working database, not a binary on disk",
runner: runner,
},
commandCapability{
name: CapServiceManager, command: "systemctl", args: []string{"is-system-running"},
why: "reads the init's own account of its state — degraded is still running",
interpret: systemRunning,
runner: runner,
},
commandCapability{
name: CapFirewall, command: "nft", args: []string{"list", "ruleset"},
why: "lists the ruleset — needs the tool AND the privilege to use it",
runner: runner,
},
commandCapability{
name: CapOverlay, command: "wg", args: []string{"show", "interfaces"},
why: "asks the kernel for interfaces — needs the module, not just the tool",
runner: runner,
},
}
}
// isRoot is the same question `privileged` answers, exposed for tests that must check the
// detector against something other than itself.
func isRoot() bool { return os.Geteuid() == 0 }