//go:build linux package apply import ( "os" "path/filepath" "strings" "testing" "github.com/novox/mesh-host/internal/declaration" "github.com/novox/mesh-host/internal/store" ) // Defends novox/hq issue 358: a mesh directory outside every home is given its mode and owner only through a // descriptor opened from /, so a parent swapped for a link between two applies cannot carry root's chmod and // chown to the link's target — unless root alone could have made that link (/var/run is one on every node). // A link only root could have made — itself root's, in directories no other account may write — is followed, as // /var/run to /run is on every node; relative or absolute. func TestALinkOnlyRootCouldMakeIsFollowed(t *testing.T) { for name, to := range map[string]func(base string) string{ "absolute": func(base string) string { return filepath.Join(base, "elsewhere") }, "relative": func(string) string { return "elsewhere" }, } { t.Run(name, func(t *testing.T) { rootAlone(t, "") base, r, previous := aMeshDirectory(t) target := swapParentForLink(t, base, to(base)) out, err := applyDirectory(r, previous, true) if err != nil { t.Fatalf("a directory behind a root-only link was refused: %v", err) } if got := permOf(t, target); got != 0o700 { t.Errorf("the directory behind a root-only link is mode %o, want 700", got) } if out.Action != "updated" { t.Errorf("the apply said %q, want updated", out.Action) } }) } } // A link another account owns refuses the directory even in directories root alone may write, naming the account. func TestALinkAnotherAccountOwnsIsRefused(t *testing.T) { rootAlone(t, "the account mallory (it owns the link)") base, r, previous := aMeshDirectory(t) target := swapParentForLink(t, base, filepath.Join(base, "elsewhere")) _, err := applyDirectory(r, previous, true) if err == nil || !strings.Contains(err.Error(), "mallory") { t.Fatalf("a link another account owns was not refused naming it: %v", err) } if got := permOf(t, target); got != 0o755 { t.Errorf("the link's target was given mode %o (was 755)", got) } } // A directory that is missing is made through the walk, below a link root alone could make. func TestAMissingDirectoryIsMadeBehindARootOnlyLink(t *testing.T) { rootAlone(t, "") base := t.TempDir() if err := os.Mkdir(filepath.Join(base, "real"), 0o755); err != nil { t.Fatal(err) } if err := os.Symlink("real", filepath.Join(base, "link")); err != nil { t.Fatal(err) } r := &declaration.Directory{ID: "mod.state", Path: filepath.Join(base, "link", "a", "b"), Mode: "0750"} out, err := applyDirectory(r, store.Applied{}, false) if err != nil { t.Fatal(err) } if out.Action != "created" { t.Errorf("the apply said %q, want created", out.Action) } if got := permOf(t, filepath.Join(base, "real", "a", "b")); got != 0o750 { t.Errorf("the made directory is mode %o, want 750", got) } } // rootAlone has every directory count as writable by root alone, as /, /var and /run are on a node, and a link's // owner be linkOwner ("" for root): a test runs below /tmp, which every account may write. func rootAlone(t *testing.T, linkOwner string) { t.Helper() wasDirs, wasLinks := couldReplace, linkReplacer couldReplace = func(string, uint32, uint32, uint32) string { return "" } linkReplacer = func(string, uint32) string { return linkOwner } t.Cleanup(func() { couldReplace, linkReplacer = wasDirs, wasLinks }) }