bastion 0.53.2
ci / personal-data (push) Successful in 5s
ci / test (push) Successful in 3m4s
ci / fuzz (push) Successful in 3m45s

This commit is contained in:
Privatae
2026-10-20 04:42:14 +00:00
commit e4c9b69b46
471 changed files with 159794 additions and 0 deletions
+573
View File
@@ -0,0 +1,573 @@
//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)
}
}