Host power-off for the household (R-998): POST /host/poweroff
gates / gates (push) Successful in 34s
gates / gates (push) Successful in 34s
The guest's controller asks; the agent refuses in the first 10 min after boot, a second time in one boot (persisted, keyed by /proc/stat btime), and while a backup, restore test, OS step, controller update, drive format, NAS verify, escrow ceremony or vzdump runs (each a named 409 reason). On acceptance: the record first, a host report carrying deliberate_off accepted by the hub (bounded 60 s), then pct shutdown --timeout 120, then systemctl poweroff. Sudoers: two exact vectors (pct ^shutdown N --timeout N$, systemctl poweroff) in FELHOM_POWEROFF; nine injection cases. New TestEveryAliasIsGranted: a defined alias missing from the grant line passed the coverage check before. Every new test red-proofed.
This commit is contained in:
@@ -115,6 +115,8 @@ var manifest = []Capability{
|
||||
{"guesthook-register", "pre-start hook register", "/usr/sbin/pct", []string{"set", "9201", "--hookscript", "local:snippets/felhom-guest-hook.sh"}, false, ""},
|
||||
{"guesthook-delete-mp", "dead mountpoint slot delete (C1 net)", "/usr/sbin/pct", []string{"set", "9201", "--delete", "mp0"}, false, ""},
|
||||
{"guest-reboot", "enroll activate-binds reboot", "/usr/sbin/pct", []string{"reboot", "9201"}, false, ""},
|
||||
{"guest-shutdown", "household power-off: guest shutdown (R-998)", "/usr/sbin/pct", []string{"shutdown", "9201", "--timeout", "120"}, false, ""},
|
||||
{"host-poweroff", "household power-off: host power-off (R-998)", "/usr/bin/systemctl", []string{"poweroff"}, false, ""},
|
||||
|
||||
// ---- LAN split-horizon resolver (dnsmasq) ----
|
||||
{"dnsmasq-install", "dnsmasq package install", "/usr/bin/apt-get", []string{"install", "-y", "-q", "dnsmasq"}, false, ""},
|
||||
|
||||
@@ -311,3 +311,39 @@ func TestWGCapabilityCriticality(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestEveryAliasIsGranted closes a gap found while adding FELHOM_POWEROFF (R-998): the coverage check above reads
|
||||
// every Cmnd_Alias body whether or not the alias appears on the grant line, so a manifest command whose alias was
|
||||
// never GRANTED still passed — and failed with „a password is required" on the box. Every defined alias must be
|
||||
// named on the `felhom-agent ALL=(root) NOPASSWD:` line. Red-proof: drop FELHOM_POWEROFF from that line.
|
||||
func TestEveryAliasIsGranted(t *testing.T) {
|
||||
data, err := os.ReadFile(sudoersPath)
|
||||
if err != nil {
|
||||
t.Fatalf("read sudoers: %v", err)
|
||||
}
|
||||
var defined []string
|
||||
granted := map[string]bool{}
|
||||
grantLines := 0
|
||||
for _, ln := range strings.Split(string(data), "\n") {
|
||||
tr := strings.TrimSpace(ln)
|
||||
if strings.HasPrefix(tr, "Cmnd_Alias ") {
|
||||
if f := strings.Fields(tr); len(f) >= 2 {
|
||||
defined = append(defined, f[1])
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(tr, "felhom-agent ") && strings.Contains(tr, "NOPASSWD:") {
|
||||
grantLines++
|
||||
for _, a := range strings.Split(tr[strings.Index(tr, "NOPASSWD:")+len("NOPASSWD:"):], ",") {
|
||||
granted[strings.TrimSpace(a)] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if grantLines == 0 || len(defined) < 10 {
|
||||
t.Fatalf("parsed %d grant line(s), %d aliases — parser broke", grantLines, len(defined))
|
||||
}
|
||||
for _, a := range defined {
|
||||
if !granted[a] {
|
||||
t.Errorf("Cmnd_Alias %s is defined but never granted to felhom-agent", a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,16 @@ var r861Injections = []string{
|
||||
"/usr/sbin/pct exec 9201 -- tee /etc/felhom-controller-image",
|
||||
"/usr/local/sbin/felhom-priv-apply controller-image 9201 9202",
|
||||
"/usr/local/sbin/felhom-priv-apply controller-image 9201;id",
|
||||
// R-998 (host power-off): the two new grants take exactly their fixed shape — no extra option, no other target
|
||||
"/usr/sbin/pct shutdown 9201 --timeout 120 --forceStop 1",
|
||||
"/usr/sbin/pct shutdown 9201 --forceStop 1 --timeout 120",
|
||||
"/usr/sbin/pct shutdown 9201",
|
||||
"/usr/sbin/pct shutdown 9201 --timeout -1",
|
||||
"/usr/bin/systemctl poweroff --force",
|
||||
"/usr/bin/systemctl poweroff -ff",
|
||||
"/usr/bin/systemctl reboot",
|
||||
"/usr/bin/systemctl halt",
|
||||
"/usr/bin/systemctl kexec",
|
||||
}
|
||||
|
||||
func TestSudoersRefusesTheR861Injections(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user