v0.88.0: controller-driven escrow ceremony — --output=json machine mode (escrowCeremony extraction, text mode byte-identical), the ONE fixed argv (escrow.CeremonyArgs, shared by exec+manifest+FELHOM_ESCROW sudoers, pin-tested), localapi ceremony job (single-flight, 60s) + one-shot in-memory R claim (10min TTL, unclaimed_void) + preflight; escrow-ceremony capability (Critical, pbs_dr-gated)
This commit is contained in:
@@ -2,9 +2,12 @@ package capability
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-agent/internal/escrow"
|
||||
)
|
||||
|
||||
// sudoersPath is the in-repo allowlist, relative to this test file (internal/capability/).
|
||||
@@ -217,6 +220,50 @@ func TestRedProof_DroppedControllerSwapTeeFailsCheck(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestEscrowCeremonyArgvPinned locks the ceremony argv copies together (Scenario G, v0.88.0).
|
||||
// The exec runner and the manifest entry both consume escrow.CeremonyArgs() (one shared source),
|
||||
// and TestManifestCoveredBySudoers proves manifest ⊆ sudoers — so pinning the shared source to
|
||||
// the EXPECTED literal here transitively locks all three: runner == manifest == sudoers.
|
||||
// Red-proof: mutate one element of the argv in internal/escrow/ceremony.go and THIS test fails
|
||||
// (and so does the sudoers coverage); a sudoers-side mutation is caught by the existing
|
||||
// TestRedProof_* machinery.
|
||||
func TestEscrowCeremonyArgvPinned(t *testing.T) {
|
||||
wantBinary := "/usr/local/bin/felhom-agent"
|
||||
wantArgs := []string{"--config", "/etc/felhom-agent/agent.json", "--selftest=escrow-create", "--upload", "--output=json"}
|
||||
|
||||
if escrow.CeremonyBinary != wantBinary {
|
||||
t.Errorf("escrow.CeremonyBinary = %q, want %q", escrow.CeremonyBinary, wantBinary)
|
||||
}
|
||||
if got := escrow.CeremonyArgs(); !reflect.DeepEqual(got, wantArgs) {
|
||||
t.Errorf("escrow.CeremonyArgs() = %q, want %q (the sudoers line + manifest entry must stay byte-identical)", got, wantArgs)
|
||||
}
|
||||
|
||||
var entry Capability
|
||||
for _, c := range Manifest() {
|
||||
if c.Name == "escrow-ceremony" {
|
||||
entry = c
|
||||
}
|
||||
}
|
||||
if entry.Name == "" {
|
||||
t.Fatal("manifest missing escrow-ceremony")
|
||||
}
|
||||
if entry.Binary != escrow.CeremonyBinary || !reflect.DeepEqual(entry.ReprArgs, escrow.CeremonyArgs()) {
|
||||
t.Errorf("manifest escrow-ceremony argv diverged from the shared constant: %s %q", entry.Binary, entry.ReprArgs)
|
||||
}
|
||||
if !entry.Critical {
|
||||
t.Error("escrow-ceremony must be Critical (the wizard's whole run path is this one grant)")
|
||||
}
|
||||
if entry.GatedBy != GatePBSDR {
|
||||
t.Errorf("escrow-ceremony GatedBy = %q, want %q (no PBS key → no ceremony; inactive, never red, on a DR-off box)", entry.GatedBy, GatePBSDR)
|
||||
}
|
||||
// CeremonyArgs must return a COPY — a caller mutating its slice must not poison the source.
|
||||
mutated := escrow.CeremonyArgs()
|
||||
mutated[0] = "--poisoned"
|
||||
if got := escrow.CeremonyArgs(); !reflect.DeepEqual(got, wantArgs) {
|
||||
t.Error("escrow.CeremonyArgs() shares its backing array — callers can mutate the source")
|
||||
}
|
||||
}
|
||||
|
||||
// TestWGCapabilityCriticality pins the exact S4 (v0.66.0) Critical set for the FELHOM_WG entries:
|
||||
// the backup path (conf install, unit enable/restart, handshake read) is operator-alert-worthy now
|
||||
// that offsite backups ride the tunnel; the one-time apt install and the deliberate disable
|
||||
|
||||
Reference in New Issue
Block a user