feat(db): dbctl — reconcile per-peer CONNECT authorization on the shared DB (M2 §4)

New lib/db/dbctl: SyncAuthorization runs after the DB starts (EnsureDatabaseRunning),
ensuring a LOGIN role + CONNECT grant per installed peer and revoking the stock
PUBLIC CONNECT grant on mTLS-armed databases; RevokeForService drops a peer's
CONNECT at uninstall. Admin executes over the in-container unix socket as
postgres (trust). Wired into ensureInfraRunning and UninstallContainer.
Includes unit tests + env-gated live integration test.
This commit is contained in:
2026-08-19 00:22:23 +01:00
parent 73fd96d698
commit 3217dca39f
5 changed files with 466 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
// Copyright (C) 2026 Dcntrlized
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package dbctl
import (
"context"
"os"
"path/filepath"
"testing"
"git.dcntrlized.org/Dcntrlized/Maestro/lib/docker"
)
// TestSyncAuthorizationIntegration exercises the real exec path against a
// running dcntrlized-db container.
//
// 1. docker run -d --name dcntrlized-db -e POSTGRES_DB=dcntrlized \
// -e POSTGRES_USER=postgres -e POSTGRES_HOST_AUTH_METHOD=trust \
// -v $PWD/PKI/ca.crt:/dcntrlized/pki/ca.crt:ro \
// -v $PWD/PKI/tls.crt:/dcntrlized/pki/tls.crt:ro \
// -v $PWD/PKI/tls.key:/dcntrlized/pki/tls.key:ro \
// -v dctl-vol:/var/lib/postgresql/data dcntrlized-db
// 2. seed the data root with install dirs for the peers
// 3. DBCtl_INTEGRATION=1 go test ./lib/db -run TestSyncAuthorizationIntegration -v
func TestSyncAuthorizationIntegration(t *testing.T) {
if os.Getenv("DBCtl_INTEGRATION") != "1" {
t.Skip("set DBCtl_INTEGRATION=1 to run against a live database container")
}
root := t.TempDir()
t.Setenv("DCNTRLIZED_INSTALL_DIR", root)
for _, name := range []string{"keepnote", "dcntrlized-auth"} {
if err := os.MkdirAll(filepath.Join(root, name), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(root, name, "metadata.json"), []byte("{}"), 0o600); err != nil {
t.Fatal(err)
}
}
dc, err := docker.NewDockerClient()
if err != nil {
t.Fatal(err)
}
defer dc.Close()
c := New(dc)
if err := c.SyncAuthorization(context.Background()); err != nil {
t.Fatalf("SyncAuthorization: %v", err)
}
id := c.dbContainerID(context.Background())
if id == "" {
t.Fatal("database container not found")
}
assert := func(t *testing.T, sql, contains string) {
t.Helper()
out, code, err := c.execPSQL(context.Background(), id, sql)
if err != nil || code != 0 {
t.Fatalf("check %q failed (exit %d, err %v): %s", sql, code, err, out)
}
if want := contains; want != "" && out != want {
t.Fatalf("%s => %q, want %q", sql, out, want)
}
}
// Roles exist and hold CONNECT.
assert(t, `SELECT rolname FROM pg_catalog.pg_roles WHERE rolname IN ('maestro','keepnote','dcntrlized-auth') ORDER BY 1`, "dcntrlized-auth\nkeepnote\nmaestro")
assert(t, `SELECT has_database_privilege('keepnote','dcntrlized','CONNECT')`, "t")
// Armed database: PUBLIC grant removed.
assert(t, `SELECT has_database_privilege('public','dcntrlized','CONNECT')`, "f")
}