bastion 0.55.3
ci / personal-data (push) Successful in 5s
ci / test (push) Successful in 3m51s
ci / fuzz (push) Successful in 2m55s

This commit is contained in:
Privatae
2026-10-25 23:16:45 +00:00
parent 77b7853ae1
commit 8822d5df85
136 changed files with 8638 additions and 1641 deletions
+98
View File
@@ -0,0 +1,98 @@
package main
import (
"encoding/base64"
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"syscall"
"testing"
"time"
)
// opNprocTestUID is a uid no account on a test machine uses.
const opNprocTestUID = 61999
// A user-privilege op starts however many tasks other programs already run
// as its user (a busy machine's daemons running as nobody), and the cap on
// its tasks still stops the op's own fork bomb.
func TestAUserOpStartsWhateverElseRunsAsItsUser(t *testing.T) {
if os.Geteuid() != 0 {
t.Skip("needs root")
}
if ok, why := opSandboxAvailable(); !ok {
t.Skip(why)
}
opUnprivilegedIdentityHook = func() (uint32, uint32, error) { return opNprocTestUID, opNprocTestUID, nil }
t.Cleanup(func() { opUnprivilegedIdentityHook = nil })
var others []*exec.Cmd
t.Cleanup(func() {
for _, c := range others {
_ = c.Process.Kill()
_ = c.Wait()
}
})
for i := 0; i < 64; i++ { // the sandbox's cap
c := exec.Command("sleep", "60")
c.SysProcAttr = &syscall.SysProcAttr{Credential: &syscall.Credential{Uid: opNprocTestUID, Gid: opNprocTestUID, NoSetGroups: true}}
if err := c.Start(); err != nil {
t.Fatal(err)
}
others = append(others, c)
}
runOp := func(extra string) (*opReport, error) {
n := time.Now().UnixNano()
root := filepath.Join(opTestRoot, fmt.Sprintf("np%d", n))
opDir := filepath.Join(opTestRoot, fmt.Sprintf("npop%d", n))
for _, p := range []string{root, opDir} {
p := p
t.Cleanup(func() { _ = os.RemoveAll(p) })
}
_ = os.MkdirAll(root, 0o777)
_ = os.Chmod(root, 0o777)
_ = os.MkdirAll(opDir, 0o755)
for _, f := range opFixture(root, extra) {
raw, _ := base64.StdEncoding.DecodeString(f.B64)
_ = os.WriteFile(filepath.Join(opDir, f.Path), raw, os.FileMode(f.Mode))
}
man := strings.Replace(fmt.Sprintf(opTestManifest, root), "privilege: root", "privilege: user", 1)
m, err := parseOpManifest([]byte(man))
if err != nil {
t.Fatal(err)
}
rep, err := opRunSandboxed(opDir, m, opModeDryRun, `{"name":"x","count":1,"kind":"a"}`)
if rep != nil {
t.Cleanup(rep.cleanup)
}
return rep, err
}
rep, err := runOp(`echo "RAN-AS=$(id -u)"; true`)
if err != nil {
t.Fatalf("a user op with 64 other tasks of its user on the machine: %v", err)
}
if !strings.Contains(rep.Output, fmt.Sprintf("RAN-AS=%d", opNprocTestUID)) {
t.Errorf("the op did not run as its user: %q", rep.Output)
}
// The op's own tasks are still capped: a loop starting 100 is stopped,
// and only after about as many as the cap, its own, not the others'.
rep, err = runOp(`i=0; while [ $i -lt 100 ]; do echo "STARTING=$i"; sleep 5 & i=$((i+1)); done; echo DONE; true`)
if err == nil || !strings.Contains(strings.ToLower(err.Error()), "fork") || strings.Contains(rep.Output, "DONE") {
t.Fatalf("100 background tasks were not stopped by the cap: %v, %q", err, rep.Output)
}
last := ""
for _, line := range strings.Split(rep.Output, "\n") {
if strings.HasPrefix(line, "STARTING=") {
last = strings.TrimPrefix(line, "STARTING=")
}
}
if n, _ := strconv.Atoi(last); n < 50 {
t.Errorf("the cap stopped the op after %s of its own tasks: it counts other tasks too", last)
}
t.Logf("the op's fork loop was stopped at task %s", last)
}