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 }