mesh/merge-gate pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check fail: its merge-check.sh failed: --- FAIL: TestBelowAHomeOnlyThatAccountsDirectoryIsPlaced (0.00s)
mesh/delivery superseded: a newer head of the same pull request
A module's places setting could point a directory anywhere, with an owner it named, and the engine chowned and chmodded whatever it was pointed at as root, through a link too; its accesses setting could mount any path into a container (hq issue 339). The engine now refuses, as a failed resource with its reason, a directory, access or mount source that is or holds one of the machine's roots, anything placed in the kernel's, the boot loader's, root's or the engine's own trees, a non-root directory below /etc, /usr or /run, an access there, another account's directory below a home, and a container mounting anything it refused, judged with every link resolved. A directory whose last component is a link is refused, and one found here that the mesh did not make keeps its owner and mode and is never removed.
68 lines
2.5 KiB
Go
68 lines
2.5 KiB
Go
package apply
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-host/internal/store"
|
|
)
|
|
|
|
// Below a person's or an agent's home, a directory is that account's (novox/hq issue 339): root's, or another
|
|
// account's, there is one the account does not control in a tree whose every parent it does — and a container
|
|
// mounting one the engine refused is refused with it.
|
|
func TestBelowAHomeOnlyThatAccountsDirectoryIsPlaced(t *testing.T) {
|
|
dir := t.TempDir()
|
|
home := filepath.Join(dir, "home", "alice")
|
|
if err := os.MkdirAll(home, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
me := os.Getuid()
|
|
passwd := filepath.Join(dir, "passwd")
|
|
if err := os.WriteFile(passwd, fmt.Appendf(nil, "root:x:0:0::/root:/bin/sh\nalice:x:%d:%d::%s:/bin/sh\n",
|
|
me, os.Getgid(), home), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
before := passwdFile
|
|
passwdFile = passwd
|
|
t.Cleanup(func() { passwdFile = before })
|
|
|
|
_, _, err := applyOnce(t, `{"id":"notes.data","type":"directory","path":"`+home+`/.config/autostart"}`, nil, store.State{})
|
|
refusedFor339(t, "root's directory below alice's home", err)
|
|
_, _, err = applyOnce(t, `{"id":"notes.data","type":"directory","path":"`+home+`/.config/autostart",`+
|
|
`"owner":"`+strconv.Itoa(me+1)+`"}`, nil, store.State{})
|
|
refusedFor339(t, "another account's directory below alice's home", err)
|
|
if _, err := os.Stat(filepath.Join(home, ".config")); err == nil {
|
|
t.Fatal("a refused directory's parent was made")
|
|
}
|
|
// Alice's own is placed.
|
|
if _, _, err := applyOnce(t, `{"id":"notes.data","type":"directory","path":"`+home+`/.config/notes",`+
|
|
`"owner":"`+strconv.Itoa(me)+":"+strconv.Itoa(os.Getgid())+`"}`, nil, store.State{}); err != nil {
|
|
t.Fatalf("alice's own directory: %v", err)
|
|
}
|
|
// A container mounting a refused directory is refused with it, and not run.
|
|
ran := false
|
|
run := func(ctx context.Context, name string, args ...string) (string, error) {
|
|
switch args[0] {
|
|
case "info":
|
|
return "27.0\n", nil
|
|
case "container":
|
|
return "", os.ErrNotExist
|
|
case "run":
|
|
ran = true
|
|
}
|
|
return "", nil
|
|
}
|
|
_, _, err = Apply(context.Background(), archHost(t), parseTrusted(t, `{"declaration":1,"resources":[
|
|
{"id":"notes.data","type":"directory","path":"`+home+`/.ssh"},
|
|
{"id":"notes.server","type":"container","name":"notes","image":"`+pinned+`","volumes":["`+home+`/.ssh:/keys"]}
|
|
]}`), store.State{}, store.OriginCarried, run, nil, nil)
|
|
refusedFor339(t, "a container mounting a refused directory", err)
|
|
if ran {
|
|
t.Fatal("a container mounting a refused directory was run")
|
|
}
|
|
}
|