From cdbff1777e26011fdc358cf1144415057782ccd0 Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 22 Oct 2026 22:23:04 +0200 Subject: [PATCH] Take a directory in the mesh's own tree that the engine's account owns as the mesh's, not as found (issue 1873) postgres's data directory moved to /var/lib/mesh/postgres. On ace and novox that directory was there, root:root 0700, before the directory resource was applied, so the engine used it as found (issue 339's rule) and left it to root: the postgres provider, running as the operator's account, cannot reach its own secrets, consumers and grants there. /var/lib/mesh is the engine's tree (root, 0755): only the engine's account makes a directory in it. A directory below it owned by that account is now the mesh's and is given its declared owner and mode. Anywhere else, or owned by another account, issue 339's rule stands. --- internal/apply/apply.go | 15 +++++++ internal/apply/mesh_dirs.go | 5 +++ internal/apply/mesh_tree_found_test.go | 62 ++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 internal/apply/mesh_tree_found_test.go diff --git a/internal/apply/apply.go b/internal/apply/apply.go index 5162f19..5c553eb 100644 --- a/internal/apply/apply.go +++ b/internal/apply/apply.go @@ -1482,6 +1482,9 @@ func applyDirectory(r *declaration.Directory, previous store.Applied, wasBefore } if existed && wasBefore { ours := previous.Target != "" && filepath.Clean(previous.Target) == filepath.Clean(r.Path) && !previous.AsFound + // In the mesh's own tree, a directory the engine's own account owns was made by the mesh, on the way to + // something below it or by an earlier apply that did not record it: it is the mesh's (novox/hq issue 1873). + ours = ours || madeInTheMeshsTree(r.Path, before) if !ours { owned, err := ownedBy(r.Path, r.Owner) if err != nil { @@ -1557,6 +1560,18 @@ func applyDirectory(r *declaration.Directory, previous store.Applied, wasBefore return out, nil } +// madeInTheMeshsTree is whether a directory lies in the mesh's own tree (meshDataRoot, below it, never the root +// itself) and belongs to the engine's own account: only that account makes a directory there, so it is the mesh's +// and not found (novox/hq issue 1873). Anywhere else, or owned by any other account, issue 339's rule stands. +func madeInTheMeshsTree(path string, found os.FileInfo) bool { + root := filepath.Clean(meshDataRoot) + if !strings.HasPrefix(filepath.Clean(path), root+string(filepath.Separator)) { + return false + } + uid, _, ok := ownerOf(found) + return ok && uid == os.Geteuid() +} + // applyHandedOver gives a directory the operator handed to the mesh its declared mode and owner (novox/hq issue // 356). **No link, checked again at the moment of use**: the hand-over refused a link when it was recorded, and a // component of the path may have become one since — a parent swapped for a link to /etc would carry root's chmod diff --git a/internal/apply/mesh_dirs.go b/internal/apply/mesh_dirs.go index d21094e..78c2c8d 100644 --- a/internal/apply/mesh_dirs.go +++ b/internal/apply/mesh_dirs.go @@ -124,3 +124,8 @@ func JudgeDirectories(d *declaration.Declaration) string { placed+refused+guarded+homed, placed, refused, guarded, homed) return strings.Join(append([]string{head}, lines...), "\n") } + +// meshDataRoot is the mesh's own tree, where a module placed "mesh" keeps its directory (/mesh/): +// the engine's, 0755, so only the engine's account makes a directory in it (novox/hq issue 1873). A variable so a +// test can place it. +var meshDataRoot = "/var/lib/mesh" diff --git a/internal/apply/mesh_tree_found_test.go b/internal/apply/mesh_tree_found_test.go new file mode 100644 index 0000000..f37a893 --- /dev/null +++ b/internal/apply/mesh_tree_found_test.go @@ -0,0 +1,62 @@ +package apply + +import ( + "context" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/novox/mesh-host/internal/store" +) + +// novox/hq issue 1873: a module's directory in the mesh's own tree (/mesh/, place "mesh") that is +// there before its declaration, owned by the engine's own account, was made by the mesh on the way to something below +// it: it is the mesh's, given its declared mode and owner, never used as found. Only the engine's account makes a +// directory in a tree that is the engine's (0755, root); on ace and novox /var/lib/mesh/postgres was there as +// root:root 0700 before postgres's data directory was applied, and the engine left it to root, so the postgres +// provider, running as the operator's account, could not reach its own files. +func TestADirectoryInTheMeshsOwnTreeMadeByTheEngineIsTheMeshs(t *testing.T) { + root := t.TempDir() + before := meshDataRoot + meshDataRoot = filepath.Join(root, "mesh") + t.Cleanup(func() { meshDataRoot = before }) + made := filepath.Join(meshDataRoot, "postgres") + if err := os.MkdirAll(made, 0o700); err != nil { + t.Fatal(err) + } + if err := os.Chmod(made, 0o700); err != nil { + t.Fatal(err) + } + decl := `{"id":"postgres.data","type":"directory","path":"` + made + `","mode":"0750"}` + var said []string + _, state, err := Apply(context.Background(), archHost(t), declare(t, decl), store.State{}, store.OriginDeclared, + noServices, func(line string) { said = append(said, line) }, nil) + if err != nil { + t.Fatal(err) + } + if strings.Contains(strings.Join(said, "\n"), "WARNING postgres.data") { + t.Errorf("a directory the engine's account made in the mesh's tree was used as found: %q", said) + } + if a, _ := state.Find("postgres.data"); a.AsFound { + t.Error("it is recorded as found") + } + if info, _ := os.Stat(made); info.Mode().Perm() != 0o750 { + t.Errorf("it was not given its declared mode: %04o", info.Mode().Perm()) + } + + // Outside the mesh's tree the rule of issue 339 stands: found, used as found. + other := filepath.Join(root, "var-lib", "notes") + if err := os.MkdirAll(other, 0o700); err != nil { + t.Fatal(err) + } + said = nil + decl = `{"id":"notes.data","type":"directory","path":"` + other + `","mode":"0750"}` + if _, _, err := Apply(context.Background(), archHost(t), declare(t, decl), store.State{}, store.OriginDeclared, + noServices, func(line string) { said = append(said, line) }, nil); err != nil { + t.Fatal(err) + } + if !strings.Contains(strings.Join(said, "\n"), "WARNING notes.data") { + t.Errorf("a directory found outside the mesh's tree was not used as found: %q", said) + } +}