Files
Privatae 48899b41a9
ci / personal-data (push) Successful in 4s
ci / test (push) Successful in 4m58s
ci / fuzz (push) Successful in 3m33s
bastion 0.63.1
2026-11-03 22:56:18 +00:00

175 lines
7.8 KiB
Go

package main
// TestMain — NO TEST IN THIS PACKAGE READS THE DEVELOPER'S REAL BASTION STATE.
//
// This happened, and it was not a theory. The machine the suite ran on got its
// own bastion anchored to a real host (it is a working machine like any other).
// Within the hour, three tests that had passed all day went red:
//
// TestMaybeContainKillsTreeAndRefusesApps killed [], want [101 102 103 202]
// TestBackupFetchRefusesPrivateAndLocalTargets "no verified pairing grant …"
// TestBackupFetchRefusesBadArguments the same
//
// Nothing in the code had changed. Those tests call handleMgmtConn and
// maybeContain, which reach capsAllow -> currentAnchor() -> configDir(), and
// configDir() is $HOME-derived. Before the box was anchored there was no
// anchor.json to find, so capsAllow took its unanchored path and gated nothing.
// The moment a REAL anchor existed on the machine, every one of those lanes
// went default-deny and the suite started failing for a reason that had nothing
// to do with the code under test.
//
// That is the worst shape a test failure can take, and this repo has seen it
// twice: a suite that accuses working code because of state outside the
// repository. The other one was a disk-map test walking the developer's
// actual disk. Same lesson both times —
// A TEST THAT READS THE MACHINE IT RUNS ON IS NOT A TEST, IT IS A MEASUREMENT
// OF THAT MACHINE.
//
// The per-test helper (uiTestConfigDir) already existed and is used widely; the
// failure was that using it was a CONVENTION, so any test that forgot it
// silently read the real thing. This makes it structural: the whole package
// runs against a temp config dir, and a test that wants its own still calls the
// helper on top. Nothing to remember and nothing to forget.
import (
"fmt"
"os"
"strings"
"testing"
"time"
)
// installShStandInRun is the argument that makes this test binary stand in
// for a running bastion (TestMain).
const installShStandInRun = "install-sh-stand-in-run"
func TestMain(m *testing.M) {
// The op sandbox re-execs its own binary (ops_exec_linux.go); under
// `go test` that binary is this one, so the child must be answered here
// before the runner would start the suite again inside the namespaces.
if len(os.Args) >= 3 && os.Args[1] == opSandboxSubcommand {
os.Exit(opSandboxChildMain(os.Args[2]))
}
if len(os.Args) >= 3 && os.Args[1] == opSandboxScopeSubcommand {
os.Exit(opSandboxScopeMain(os.Args[2]))
}
if len(os.Args) >= 3 && os.Args[1] == opActionSubcommand {
os.Exit(opActionOuterMain(os.Args[2]))
}
if len(os.Args) >= 3 && os.Args[1] == opActionChildSubcommand {
os.Exit(opActionChildMain(os.Args[2]))
}
// The suite never asks the build machine's own loopback for a model
// server (llmlocal.go): a llama.cpp left running on 8080 there would make
// every lane test depend on it. llmlocal_test.go sets its own.
llmProbe = func(string) ([]string, bool) { return nil, false }
if len(os.Args) >= 3 && os.Args[1] == opActionProbeSubcommand {
os.Exit(opActionProbeMain(os.Args[2]))
}
// The root-command watch tests run this binary as the command that
// connects (rootexec_watch_linux_test.go).
if len(os.Args) >= 3 && os.Args[1] == rootExecConnectSubcommand {
os.Exit(rootExecConnectMain(os.Args[2:]))
}
// install_sh_test.go runs a copy of this binary named privatae-bastion as
// a bastion left running outside its service: it only waits to be
// stopped, and never starts the suite.
if len(os.Args) >= 2 && os.Args[1] == installShStandInRun {
time.Sleep(2 * time.Minute)
os.Exit(0)
}
// Likewise when git runs this binary as the bastion's credential helper
// (claims_git_test.go): answer as the helper, with the config dir the
// test named, and never start the suite.
if os.Getenv("PRIVATAE_TEST_GIT_HELPER_CONFIG") != "" {
os.Exit(gitHelperChildMain(os.Getenv("PRIVATAE_TEST_GIT_HELPER_CONFIG"), os.Args[1:]))
}
dir, err := os.MkdirTemp("", "bastion-suite-config-")
if err != nil {
fmt.Fprintf(os.Stderr, "could not make an isolated config dir: %v\n", err)
os.Exit(1)
}
// The same three variables uiTestConfigDir sets, for the same reasons:
// XDG_CONFIG_HOME is what os.UserConfigDir reads on linux, HOME on darwin,
// AppData on windows. Setting all three means the isolation holds on every
// platform this binary is built for rather than only the one CI runs.
for _, kv := range [][2]string{
{"XDG_CONFIG_HOME", dir},
{"HOME", dir},
{"AppData", dir},
} {
if err := os.Setenv(kv[0], kv[1]); err != nil {
fmt.Fprintf(os.Stderr, "could not isolate %s: %v\n", kv[0], err)
os.Exit(1)
}
}
// THE GUARD HAS TO GUARD ITSELF. "Remove TestMain -> RED" only holds on a
// machine that HAS an anchor: in a clean CI container there is no real
// state directory to read, so the isolation could be
// deleted and every test would stay green — the protection would rot
// silently on exactly the machine nobody is watching.
//
// So assert the thing itself rather than a downstream symptom: whatever
// os.UserConfigDir() resolves to on THIS platform must now sit under the
// temp directory. A failure here is loud and immediate, and it does not
// depend on the state of the box.
if got, err := os.UserConfigDir(); err != nil {
fmt.Fprintf(os.Stderr, "config dir is unresolvable, so the suite cannot prove "+
"it is isolated: %v\n", err)
os.Exit(1)
} else if !strings.HasPrefix(got, dir) {
fmt.Fprintf(os.Stderr, "THE SUITE IS NOT ISOLATED: os.UserConfigDir() is %q, "+
"outside the temp dir %q. Tests reaching configDir() would read the real "+
"bastion state on this machine.\n", got, dir)
os.Exit(1)
}
// THE FREEZE IS A SIDE EFFECT TOO. Tests that stub killProcess with fake
// pids (101, 102...) predate killTree's SIGSTOP; without this they would
// stop whatever real process holds those numbers on the machine running
// the suite. Tests that exercise the freeze set their own stubs.
stopProcess = func(int) error { return nil }
resumeProcess = func(int) error { return nil }
// Fake pids have no identity: without this the real reader would consult
// /proc/101 on the machine running the suite and the decision would depend
// on whatever process holds that number. The real-signal test opts back in.
procIdentity = func(int) (string, bool, bool) { return "", false, false }
// The reads go to the machine's own bastion when it runs as a system
// service and the suite does not run as root (reads.go): never from the
// suite, which reads its own isolated state. The lane's tests opt in.
readsGoThroughLane = func() bool { return false }
// THE SWEEP IS ONE TOO. A test runs the ops' startup sweep for real, and it
// stops every op unit its names match: the suite's names are its own, so
// it never stops an op the machine's own bastion is running.
opIsolateOpsUnits()
// THE FIREWALL IS THE MACHINE'S. A door the suite binds asks for its own
// inbound rule (doorfw.go): never in the firewall of the machine running
// the suite. The firewall tests stand their own firewalls in.
doorFirewallFor = func(string) (doorFirewall, string) { return nil, "" }
doorFirewallByID = func(string) doorFirewall { return nil }
doorFWExec = func(name string, _ ...string) (string, error) {
return "", fmt.Errorf("the suite never runs the machine's firewall (%s)", name)
}
code := m.Run()
opIsolatedOpsUnitsDone()
_ = os.RemoveAll(dir)
os.Exit(code)
}
// gitHelperChildMain is this test binary acting as the bastion's credential
// helper, started by git in the end-to-end test below (TestMain routes to it),
// so git talks to the real cmdGitCredential.
func gitHelperChildMain(configHome string, args []string) int {
_ = os.Setenv("XDG_CONFIG_HOME", configHome)
action := "get"
if len(args) > 0 {
action = args[len(args)-1]
}
if err := cmdGitCredential("", action); err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
}
return 0
}