Keep the store's superuser file in the postgres module's own directory, not in /var/lib/postgres (issue 1873)

The postgres module's own secrets move from /var/lib/postgres, the postgresql
package's directory, to its directory under /var/lib/mesh (mesh-catalog,
fix/1873-system-state-dirs). Genesis writes the store's superuser file at the
module's own-secret path, so the store it raises is the one the module adopts in
place: StoreSuperuserFile follows, to /var/lib/mesh/postgres/superuser.secret.

A directory genesis makes on the way is 0755, the mode of the mesh's directories,
and only the credential's own is 0700: MkdirAll at 0700 would have closed
/var/lib/mesh on a machine that had none yet.
This commit is contained in:
2026-10-22 21:36:38 +02:00
parent 4065b45ae8
commit de809d7e0a
3 changed files with 58 additions and 6 deletions
+31 -3
View File
@@ -45,8 +45,10 @@ import (
// onward, and the permanent controller is given it by `secret accept` (control.go deliverStores).
const (
// StoreSuperuserFile is where the store's superuser password lives on the machine — the
// postgres module's own-secret path, so genesis and adoption write the same file.
StoreSuperuserFile = "/var/lib/postgres/superuser.secret"
// postgres module's own-secret path, so genesis and adoption write the same file: in the module's
// directory under <data root>/mesh, not in /var/lib/postgres, the postgresql package's own (novox/hq
// issue 1873).
StoreSuperuserFile = "/var/lib/mesh/postgres/superuser.secret"
// StoreDataDir is where the store keeps its data — the postgres module's data directory, so
// genesis raises the store where the module adopts it (novox/hq ADR 0338 §1).
StoreDataDir = "/var/lib/mesh-store"
@@ -207,7 +209,7 @@ func keptOrMade(path string, dryRun bool) (value string, made bool, err error) {
// writeCredentialFile writes a credential as genesis keeps it.
func writeCredentialFile(path, value string) error {
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
if err := makeCredentialDir(filepath.Dir(path)); err != nil {
return err
}
// Written whole and renamed into place, at 0600, owned by whoever runs the installer — root,
@@ -227,6 +229,32 @@ func writeCredentialFile(path, value string) error {
return os.Rename(tmp, path)
}
// makeCredentialDir makes a credential's directory 0700 and every missing one above it 0755, the mode the mesh's
// directories have (/var/lib/mesh above a module's own: novox/hq issue 1873), so a module's account still reaches its
// own directory below one genesis made. A directory already there is left as it is.
func makeCredentialDir(dir string) error {
dir = filepath.Clean(dir)
if _, err := os.Stat(dir); err == nil {
return nil
} else if !os.IsNotExist(err) {
return err
}
if parent := filepath.Dir(dir); parent != dir {
if _, err := os.Stat(parent); os.IsNotExist(err) {
if err := makeCredentialDir(parent); err != nil {
return err
}
if err := os.Chmod(parent, 0o755); err != nil {
return err
}
}
}
if err := os.Mkdir(dir, 0o700); err != nil && !os.IsExist(err) {
return err
}
return os.Chmod(dir, 0o700)
}
// readCredentialFile is a credential as genesis keeps it: the value alone, its line ending gone.
// Missing is reported as os.IsNotExist so a caller can tell "not made yet" from "unreadable".
func readCredentialFile(path string) (string, error) {
+24
View File
@@ -249,3 +249,27 @@ func TestTheFoundationFiltersBeforeAnythingListens(t *testing.T) {
}
}
}
// novox/hq issue 1873: the store's superuser file is the postgres module's own secret in its directory under
// <data root>/mesh (/var/lib/mesh/postgres), no longer in /var/lib/postgres, the postgresql package's own. A
// directory genesis makes on the way keeps the mode the mesh's directories have (0755, so every module's account
// reaches its own below it); only the credential's own directory is closed, 0700.
func TestTheStoreSuperuserFileLiesInTheModulesOwnDirectory(t *testing.T) {
if StoreSuperuserFile != "/var/lib/mesh/postgres/superuser.secret" {
t.Errorf("the store's superuser file is %s, not the postgres module's /var/lib/mesh/postgres/superuser.secret", StoreSuperuserFile)
}
dir := t.TempDir()
store := dir + "/mesh/postgres/superuser.secret"
if _, err := rootSecretsAt(store, dir+"/genesis/bus", dir+"/genesis/bus.bcrypt", false); err != nil {
t.Fatal(err)
}
for path, want := range map[string]os.FileMode{dir + "/mesh": 0o755, dir + "/mesh/postgres": 0o700} {
st, err := os.Stat(path)
if err != nil {
t.Fatal(err)
}
if st.Mode().Perm() != want {
t.Errorf("%s is made %04o, not %04o", path, st.Mode().Perm(), want)
}
}
}
+3 -3
View File
@@ -66,11 +66,11 @@ func TestTheBundleRaisesTheStoreAsTheCataloguesPostgresModuleDeclaresIt(t *testi
if _, has := ours.Env["POSTGRES_PASSWORD"]; has {
t.Error("the bundle's store takes its password from its environment; the module reads a file")
}
// The module's own directory for its state is <data root>/<module>, where genesis keeps the
// superuser's file (StoreSuperuserFile).
// The module's own directory is <data root>/mesh/<module>, where genesis keeps the superuser's
// file (StoreSuperuserFile; novox/hq issue 1873).
var theirs []string
for _, v := range r.Volumes {
theirs = append(theirs, strings.ReplaceAll(v, "${dir:state}", "/var/lib/postgres"))
theirs = append(theirs, strings.ReplaceAll(v, "${dir:data}", "/var/lib/mesh/postgres"))
}
if strings.Join(ours.Volumes, " ") != strings.Join(theirs, " ") {
t.Errorf("the bundle's store mounts %v; the module's %v", ours.Volumes, theirs)