//go:build !windows // Tagged !windows. These tests were ALREADY unix-only — they use // helpers that live behind `//go:build !windows` (sessionShellsPath, // withConfigDir, withMarkerAt, setupBox) — so this file has never compiled for // Windows and `GOOS=windows go vet ./...` has been red on it since it was // written. The tag removes no coverage; it states a dependency that was already // there so vet can run clean on that target, which is what makes a REAL Windows // regression visible instead of being lost in a failure everyone scrolls past. // Windows-safe coverage of the machine-authority gate lives in privilege_test.go. package main import ( "encoding/json" "errors" "os" "path/filepath" "strings" "syscall" "testing" ) // withMarkerAt points the machine marker at a temp path for the duration of a test. func withMarkerAt(t *testing.T, path string) { t.Helper() old := markerPathOverride markerPathOverride = path t.Cleanup(func() { markerPathOverride = old }) } // writeMarkerNaming puts a marker on disk that claims `stateDir` owns the machine. func writeMarkerNaming(t *testing.T, path, stateDir string) { t.Helper() withMarkerAt(t, path) if err := writeMarker(&machineMarker{StateDir: stateDir, Mode: modeLocal}); err != nil { t.Fatalf("writeMarker: %v", err) } } func TestSingletonGuardAllowsWhenThereIsNoMarker(t *testing.T) { if !singletonSupported() { t.Skip("singleton is Linux-only in step 1") } withMarkerAt(t, filepath.Join(t.TempDir(), "absent", "bastion.json")) // Marker absent ⇒ exactly today's behaviour. This is what makes the change // safe to roll onto live installs: nothing changes until a root run writes one. if err := singletonGuardForPair(); err != nil { t.Fatalf("no marker must not refuse a pairing: %v", err) } } func TestSingletonGuardRefusesASecondStateDirAndNamesBoth(t *testing.T) { if !singletonSupported() { t.Skip("singleton is Linux-only in step 1") } dir := withConfigDir(t) // this process's state dir is now under a temp HOME mine, err := configDirPath() if err != nil { t.Fatal(err) } other := filepath.Join(dir, "..", "someone-else", "privatae-bastion") if err := os.MkdirAll(other, 0o700); err != nil { // a real second bastion, present on disk t.Fatal(err) } writeMarkerNaming(t, filepath.Join(t.TempDir(), "bastion.json"), other) err = singletonGuardForPair() if err == nil { t.Fatal("a marker naming another state dir must refuse the pairing") } // Both dirs in the message: a legitimate `sudo -H pair` refused because of a // stray XDG_CONFIG_HOME has to be able to see WHY. "already paired" would send // the reader hunting in the wrong place. if !strings.Contains(err.Error(), filepath.Clean(other)) || !strings.Contains(err.Error(), mine) { t.Fatalf("the refusal must name BOTH directories, got: %v", err) } } func TestSingletonGuardAllowsItsOwnStateDir(t *testing.T) { if !singletonSupported() { t.Skip("singleton is Linux-only in step 1") } withConfigDir(t) mine, err := configDirPath() if err != nil { t.Fatal(err) } writeMarkerNaming(t, filepath.Join(t.TempDir(), "bastion.json"), mine) if err := singletonGuardForPair(); err != nil { t.Fatalf("the machine's own bastion must still be able to pair: %v", err) } } // The ordering property, and the reason the guard is the FIRST statement in // pairWithToken: asking "which state dir would I use" must not bring that // directory into existence. configDir() -> MkdirAll is the damage, so a guard // that called it would have already forked the state it refuses. func TestSingletonGuardDoesNotCreateTheForkedStateDir(t *testing.T) { if !singletonSupported() { t.Skip("singleton is Linux-only in step 1") } home := t.TempDir() t.Setenv("XDG_CONFIG_HOME", home) t.Setenv("HOME", home) mine, err := configDirPath() if err != nil { t.Fatal(err) } if _, err := os.Stat(mine); !os.IsNotExist(err) { t.Fatalf("precondition: %s should not exist yet (%v)", mine, err) } elsewhere := filepath.Join(home, "elsewhere") if err := os.MkdirAll(elsewhere, 0o700); err != nil { // present on disk: a real other bastion t.Fatal(err) } writeMarkerNaming(t, filepath.Join(t.TempDir(), "bastion.json"), elsewhere) if err := singletonGuardForPair(); err == nil { t.Fatal("expected a refusal") } if _, err := os.Stat(mine); !os.IsNotExist(err) { t.Fatalf("the guard CREATED the forked state dir %s — that is the damage it exists to prevent", mine) } // And prove the trap is real: the call the guard must not make does create it. if _, err := configDir(); err != nil { t.Fatal(err) } if _, err := os.Stat(mine); err != nil { t.Fatalf("configDir() is supposed to create the dir; the test's premise is wrong: %v", err) } } // The marker must be readable by a NON-root `pair`, or the second-user refusal — // the whole point — never fires. atomicWrite would make it 0600 under a 0700 dir. func TestMarkerIsWorldReadableUnderATraversableDir(t *testing.T) { if !singletonSupported() { t.Skip("singleton is Linux-only in step 1") } path := filepath.Join(t.TempDir(), "machine", "bastion.json") writeMarkerNaming(t, path, "/some/state/dir") fi, err := os.Stat(path) if err != nil { t.Fatal(err) } if fi.Mode().Perm() != 0o644 { t.Fatalf("marker must be 0644 so a non-root pair can read it, got %v", fi.Mode().Perm()) } di, err := os.Stat(filepath.Dir(path)) if err != nil { t.Fatal(err) } if di.Mode().Perm() != 0o755 { t.Fatalf("marker dir must be 0755 traversable, got %v", di.Mode().Perm()) } // It must also round-trip. m, err := readMarker() if err != nil || m == nil || m.StateDir != "/some/state/dir" { t.Fatalf("round-trip failed: %+v %v", m, err) } } func TestCorruptMarkerDoesNotBrickPairing(t *testing.T) { if !singletonSupported() { t.Skip("singleton is Linux-only in step 1") } path := filepath.Join(t.TempDir(), "bastion.json") withMarkerAt(t, path) if err := os.WriteFile(path, []byte("{not json"), 0o644); err != nil { t.Fatal(err) } if _, err := readMarker(); err == nil { t.Fatal("a corrupt marker should be reported as an error by readMarker") } // ...but the guard must ALLOW. Refusing every pairing on this machine because // one file got truncated would be a self-inflicted outage with no upside. if err := singletonGuardForPair(); err != nil { t.Fatalf("a corrupt marker must not refuse pairing: %v", err) } // An empty state dir is "no marker", not "a marker that owns nothing". if err := os.WriteFile(path, []byte(`{"state_dir":" "}`), 0o644); err != nil { t.Fatal(err) } m, err := readMarker() if err != nil || m != nil { t.Fatalf("a marker with no state dir must read as absent, got %+v %v", m, err) } } // The conditional rewrite. An UNCONDITIONAL rewrite plus the lock lets whichever // root daemon wins after a primary dies repoint the canonical state dir at its // own — a run from a forked dir that takes the lock first would flip the marker, // and the unit's restart seconds later would find itself the standby. func TestMarkerRewriteIsConditional(t *testing.T) { mine := "/root/.config/privatae-bastion" if !markerShouldBeWritten(nil, mine) { t.Fatal("no marker yet: must write one, or a config-dir rename would refuse every pair on the fleet") } if !markerShouldBeWritten(&machineMarker{StateDir: mine}, mine) { t.Fatal("a marker naming this same dir must be refreshed") } if !markerShouldBeWritten(&machineMarker{StateDir: mine + "/"}, mine) { t.Fatal("trailing-slash differences are the same directory") } // A marker naming a directory that no longer exists protects nothing and // must not block this machine from pairing again. gone := filepath.Join(t.TempDir(), "moved-away") if !markerShouldBeWritten(&machineMarker{StateDir: gone}, mine) { t.Fatal("a marker whose state directory is gone must be rewritten") } if err := singletonGuardAgainst(&machineMarker{StateDir: gone}); err != nil { t.Fatalf("a marker whose state directory is gone must not refuse pair: %v", err) } other := filepath.Join(t.TempDir(), "other", "privatae-bastion") if err := os.MkdirAll(other, 0o700); err != nil { t.Fatal(err) } if markerShouldBeWritten(&machineMarker{StateDir: other}, mine) { t.Fatal("a marker naming ANOTHER dir must never be overwritten — that is the repoint attack") } } // The lock is the deliberate singleton. It must actually exclude, and it must be // unopenable by a non-root uid: flock needs no write access, so at 0644 any local // user could take LOCK_EX through a read-only fd and hold the root daemon out. func TestSingletonLockExcludesAndIsPrivate(t *testing.T) { if !singletonSupported() || !processIsRoot() { t.Skip("the lock is root-only on Linux in step 1") } dir := t.TempDir() old := runtimeDirOverride runtimeDirOverride = filepath.Join(dir, "run") t.Cleanup(func() { runtimeDirOverride = old }) lock, err := acquireSingletonLock() if err != nil { t.Fatalf("first acquire must succeed: %v", err) } if lock == nil { t.Fatal("expected a held lock") } fi, err := os.Stat(lockPath()) if err != nil { t.Fatal(err) } if fi.Mode().Perm() != 0o600 { t.Fatalf("lock must be 0600 — at 0644 any local uid can take it through an O_RDONLY fd, got %v", fi.Mode().Perm()) } // A second holder is refused while the first holds it. f2, err := os.OpenFile(lockPath(), os.O_RDWR, 0o600) if err != nil { t.Fatal(err) } defer f2.Close() if err := flockFileNB(f2); err == nil { t.Fatal("two daemons took the machine lock at once — it excludes nothing") } // Released on close, and the file is NEVER unlinked (unlink-and-recreate lets // two daemons hold LOCK_EX on two different inodes at one path). lock.release() if _, err := os.Stat(lockPath()); err != nil { t.Fatalf("the lock file must survive release, got %v", err) } if err := flockFileNB(f2); err != nil { t.Fatalf("after release the lock must be available: %v", err) } } func TestSingletonLockRecordsItsHolder(t *testing.T) { if !singletonSupported() || !processIsRoot() { t.Skip("the lock is root-only on Linux in step 1") } dir := t.TempDir() old := runtimeDirOverride runtimeDirOverride = filepath.Join(dir, "run") t.Cleanup(func() { runtimeDirOverride = old }) lock, err := acquireSingletonLock() if err != nil { t.Fatal(err) } defer lock.release() raw, err := os.ReadFile(lockPath()) if err != nil { t.Fatal(err) } if strings.TrimSpace(string(raw)) == "" { t.Fatal("the holder's pid must be recorded, or the 'already running' message cannot name it") } } // The marker carries what a reader needs to act: which state dir owns the machine, // the mode, and where the binary lives. func TestMarkerCarriesTheMachineFacts(t *testing.T) { if !singletonSupported() { t.Skip("singleton is Linux-only in step 1") } path := filepath.Join(t.TempDir(), "bastion.json") withMarkerAt(t, path) // The binary path as it is on a real install (verified on a real machine). if err := writeMarker(&machineMarker{ StateDir: "/root/.config/privatae-bastion", Mode: modeNetworked, BinaryPath: filepath.Join("/usr/local/bin", "privatae-bastion"), }); err != nil { t.Fatal(err) } raw, _ := os.ReadFile(path) var got map[string]any if err := json.Unmarshal(raw, &got); err != nil { t.Fatal(err) } for _, k := range []string{"mode", "state_dir", "binary_path", "written_at"} { if _, ok := got[k]; !ok { t.Fatalf("marker is missing %q: %s", k, raw) } } } // The protection for the protection. The first run of this suite as root wrote a // real marker on the build box naming a t.TempDir() that no longer existed, which // would have made that machine refuse every future pair. The test responsible only // called setBoxMode; it never mentioned the marker, and a future one won't either. // // It asserts the REASON rather than the absence of a file: once a real marker // exists on the box running the suite, "no marker appeared" passes even with the // guard removed, because maybeWriteMarker would take the another-state-dir branch // instead. func TestATestCannotClaimTheRealMachine(t *testing.T) { if !singletonSupported() || !processIsRoot() { t.Skip("the marker is root-only on Linux in step 1") } // The rule itself, tested directly and safely: a write with no override set // is refused. This can never touch the machine. if err := refuseRealMachineWrite("", "machine marker"); err == nil { t.Fatal("a test with no override must be refused the real machine marker") } if err := refuseRealMachineWrite("/tmp/somewhere", "machine marker"); err != nil { t.Fatalf("a test that DID point it at a temp path must be allowed: %v", err) } // And the wiring: writeMarker must consult it before touching anything. // NON-DESTRUCTIVE BY CONSTRUCTION — if the guard is ever removed this would // create a real marker, so it removes what it created before failing. Measured // the hard way: a mutation run with the guard stripped wrote // /var/lib/privatae/bastion/machine.json on the build box and broke an // unrelated anchor test in the same run. old := markerPathOverride markerPathOverride = "" t.Cleanup(func() { markerPathOverride = old }) existedBefore := false if _, err := os.Stat(markerPath()); err == nil { existedBefore = true } if err := writeMarker(&machineMarker{StateDir: "/tmp/whatever"}); err != nil { t.Fatalf("writeMarker must refuse softly, not error: %v", err) } if _, err := os.Stat(markerPath()); err == nil && !existedBefore { _ = os.Remove(markerPath()) // clean up before failing t.Fatalf("writeMarker is not consulting the test guard — it created %s", markerPath()) } // The guard covers the lock too, not just the marker. oldRT := runtimeDirOverride runtimeDirOverride = "" t.Cleanup(func() { runtimeDirOverride = oldRT }) if _, err := acquireSingletonLock(); err == nil { t.Fatal("a test just took the REAL machine lock") } } // The positive path. A no-op maybeWriteMarker passed the entire suite. func TestMaybeWriteMarkerWritesTheMachinesOwnStateDir(t *testing.T) { if !singletonSupported() || !processIsRoot() { t.Skip("the marker is root-only on Linux in step 1") } withConfigDir(t) withMarkerAt(t, filepath.Join(t.TempDir(), "machine.json")) mine, err := configDirPath() if err != nil { t.Fatal(err) } if got := maybeWriteMarker(); got != "written" { t.Fatalf("first root run must write the marker, got %q", got) } m, err := readMarker() if err != nil || m == nil { t.Fatalf("marker should exist: %+v %v", m, err) } if m.StateDir != mine { t.Fatalf("marker must name this process's state dir: got %q want %q", m.StateDir, mine) } // Idempotent: nothing changed, so nothing is rewritten. A crash-looping // unpaired box runs this every two seconds. if got := maybeWriteMarker(); got != "unchanged" { t.Fatalf("second call must be a no-op, got %q", got) } // And it refuses to take a machine another state dir already owns — one // that is present on disk. A marker naming a directory that is gone is // stale and is rewritten (covered in TestMarkerRewriteIsConditional). foreign := filepath.Join(t.TempDir(), "someone-else", "privatae-bastion") if err := os.MkdirAll(foreign, 0o700); err != nil { t.Fatal(err) } if err := writeMarker(&machineMarker{StateDir: foreign}); err != nil { t.Fatal(err) } if got := maybeWriteMarker(); got != "another state dir owns this machine" { t.Fatalf("must not repoint a foreign marker, got %q", got) } } // The guard's POSITION in pairWithToken. Moving it below loadPairings passed // the whole suite; nothing pinned the ordering through the real call path. func TestPairWithTokenRefusesBeforeItTouchesAnything(t *testing.T) { if !singletonSupported() { t.Skip("singleton is Linux-only in step 1") } home := t.TempDir() t.Setenv("XDG_CONFIG_HOME", home) t.Setenv("HOME", home) mine, err := configDirPath() if err != nil { t.Fatal(err) } elsewhere := filepath.Join(home, "elsewhere") if err := os.MkdirAll(elsewhere, 0o700); err != nil { // present on disk: a real other bastion t.Fatal(err) } writeMarkerNaming(t, filepath.Join(t.TempDir(), "machine.json"), elsewhere) // A deliberately invalid token: if the guard is late, decodeToken or the // redeem fails FIRST and the test would pass for the wrong reason. errors.Is // pins which refusal happened. _, _, err = pairWithToken("not-a-real-token", false, false) if !errors.Is(err, errForkedState) { t.Fatalf("pair must refuse with errForkedState before anything else, got %v", err) } if _, statErr := os.Stat(mine); !os.IsNotExist(statErr) { t.Fatalf("pairWithToken created %s before refusing", mine) } } // The branch selector. os.ModeCharDevice is TRUE for /dev/null, and every // installed daemon has stdin on /dev/null, so the first draft took the fail-fast // branch in the only deployment shape that exists and the blocking standby was // unreachable. func TestStdinIsTerminalIsFalseForDevNull(t *testing.T) { if !singletonSupported() { t.Skip("Linux-only in step 1") } devnull, err := os.Open(os.DevNull) if err != nil { t.Fatal(err) } defer devnull.Close() realStdin := os.Stdin os.Stdin = devnull t.Cleanup(func() { os.Stdin = realStdin }) if stdinIsTerminal() { t.Fatal("/dev/null must NOT read as a terminal — that is how the service " + "took the fail-fast branch and crash-looped instead of waiting") } } // The marker must never change the mode of a directory it did not create. // // The first draft chmod'd its parent to 0755 on every write. Measured on real // installs: /etc/privatae — which holds secrets — is 0750 on most boxes, with // a dedicated owning group on some. Loosening a hardened directory as a side effect of a bastion // write is not something a marker gets to do, so the marker moved to a directory // the bastion creates and owns, and this pins the second half of that fix. func TestWriteMarkerNeverChmodsAnExistingDirectory(t *testing.T) { if !singletonSupported() { t.Skip("singleton is Linux-only in step 1") } dir := filepath.Join(t.TempDir(), "hardened") if err := os.Mkdir(dir, 0o750); err != nil { t.Fatal(err) } if err := os.Chmod(dir, 0o750); err != nil { // defeat umask t.Fatal(err) } withMarkerAt(t, filepath.Join(dir, "machine.json")) if err := writeMarker(&machineMarker{StateDir: "/root/.config/privatae-bastion"}); err != nil { t.Fatal(err) } fi, err := os.Stat(dir) if err != nil { t.Fatal(err) } if fi.Mode().Perm() != 0o750 { t.Fatalf("writeMarker changed an existing directory from 0750 to %v — on the fleet that directory holds secrets", fi.Mode().Perm()) } // And when it DOES create the directory, it makes it traversable, or a // non-root pair cannot reach the marker and the refusal never fires. fresh := filepath.Join(t.TempDir(), "made-by-us") withMarkerAt(t, filepath.Join(fresh, "machine.json")) if err := writeMarker(&machineMarker{StateDir: "/root/.config/privatae-bastion"}); err != nil { t.Fatal(err) } if fi, err := os.Stat(fresh); err != nil || fi.Mode().Perm() != 0o755 { t.Fatalf("a directory the bastion creates must be 0755 traversable, got %v %v", fi.Mode().Perm(), err) } } // MkdirAll applies the process UMASK to every ANCESTOR it creates, and only // the leaf was being fixed. /var/lib/privatae was absent on 19 of 20 boxes measured, // so the first marker write creates it almost everywhere; under umask 027 or 077 // that parent lands 0750 or 0700, a non-root `pair` cannot traverse to the // marker, and the guard would fail open on exactly the hardened boxes this // location was chosen for. func TestMarkerAncestorsAreTraversableUnderAnyUmask(t *testing.T) { if !singletonSupported() { t.Skip("singleton is Linux-only in step 1") } for _, um := range []int{0o022, 0o027, 0o077} { root := t.TempDir() parent := filepath.Join(root, "privatae") dir := filepath.Join(parent, "bastion") withMarkerAt(t, filepath.Join(dir, "machine.json")) old := syscall.Umask(um) err := writeMarker(&machineMarker{StateDir: "/root/.config/privatae-bastion"}) syscall.Umask(old) if err != nil { t.Fatalf("umask %#o: %v", um, err) } for _, d := range []string{parent, dir} { fi, err := os.Stat(d) if err != nil { t.Fatal(err) } if fi.Mode().Perm()&0o001 == 0 { t.Fatalf("umask %#o: %s is %v — a non-root pair cannot traverse to the marker, so the guard fails OPEN", um, d, fi.Mode().Perm()) } } } } // An UNREADABLE marker is not an ABSENT one. "Marker absent ⇒ today's // behaviour" means ENOENT. EACCES from a locked-down ancestor means we cannot // tell — and this directory is created only by the bastion, so something here // owns the machine. The first draft allowed, which failed open on precisely the // hardened boxes. func TestUnreadableMarkerRefusesWhileCorruptOneAllows(t *testing.T) { if !singletonSupported() { t.Skip("singleton is Linux-only in step 1") } // Asserted on the MAPPING, not on a real unreadable file: root bypasses DAC, // so a chmod 0000 directory proves nothing inside this process. (The real // wiring was driven from a probe with the capability bounding set cleared — // readable-other-dir refuses, unreadable refuses, absent allows, corrupt // allows — which is the end-to-end witness this unit test cannot be.) if err := singletonGuardForPairFromReadError(os.ErrPermission); err == nil { t.Fatal("an unreadable marker must REFUSE — allowing is the fail-open the fleet measurement found") } else if !errors.Is(err, errForkedState) { t.Fatalf("the refusal must wrap errForkedState, got %v", err) } else if !strings.Contains(err.Error(), markerPath()) { t.Fatalf("the refusal must name the marker path, got %v", err) } // A truncated file is not a locked one: refusing every pairing on the machine // over one corrupt file would be a self-inflicted outage with no upside. if err := singletonGuardForPairFromReadError(errors.New("unexpected EOF")); err != nil { t.Fatalf("a corrupt marker must still allow: %v", err) } }