From 3d076099a2cfaee8c5c5157ba5f1187eb91515f5 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Mon, 27 Jul 2026 22:21:43 +0300 Subject: [PATCH 1/2] docs(postgres): name the trust anchor as the tenant actually sees it The README told a tenant to fetch .tenant-ca, but the release name carries the postgres- prefix the application definition applies, so the object is postgres-.tenant-ca. Following the old text returned NotFound. Also drop two comments promising a companion ValidatingAdmissionPolicy that pins sentinel writes to helm-controller. No such policy ships: the proposal for it was rejected because authorization by username duplicates RBAC in a more brittle form. The RBAC boundary is the whole control. Signed-off-by: Aleksei Sviridkin --- internal/controller/cacert/reconciler.go | 4 ++-- packages/apps/postgres/README.md | 6 +++--- packages/system/cozystack-controller/templates/rbac.yaml | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/controller/cacert/reconciler.go b/internal/controller/cacert/reconciler.go index 1794465e6..ff27c3c49 100644 --- a/internal/controller/cacert/reconciler.go +++ b/internal/controller/cacert/reconciler.go @@ -33,8 +33,8 @@ limitations under the License. // release. The sentinel is the reconcile unit, and its spec names the source // Secret and the key to lift. No tenant RBAC role grants any verb on // internal.cozystack.io, so a tenant cannot forge a sentinel to publish a CA of -// their choosing; that RBAC boundary is the primary control here. A companion -// ValidatingAdmissionPolicy pins writes to helm-controller as defence in depth. +// their choosing. That RBAC boundary is the whole control: the group is out of +// tenant reach, which is why the sentinel lives in it. // // # Why the owner is the sentinel, not the HelmRelease // diff --git a/packages/apps/postgres/README.md b/packages/apps/postgres/README.md index f6e279e4d..0112a6983 100644 --- a/packages/apps/postgres/README.md +++ b/packages/apps/postgres/README.md @@ -177,15 +177,15 @@ The tri-state `tls.enabled` controls whether the chart injects `serverAltDNSName **Retrieving the CA bundle** for client verification: -The trust anchor is published as `.tenant-ca`: an object holding `ca.crt` and nothing else, created for every release and delivered to tenants through the `core.cozystack.io/tenantsecrets` API that the base tenant roles already grant. +The trust anchor is published as `postgres-.tenant-ca`, where `` is the name of the Postgres resource: an object holding `ca.crt` and nothing else, created for every release and delivered to tenants through the `core.cozystack.io/tenantsecrets` API that the base tenant roles already grant. The `postgres-` prefix comes from the release naming this application definition applies, so a Postgres named `foo` gets `postgres-foo.tenant-ca`. ```bash kubectl --context --namespace \ - get tenantsecret .tenant-ca \ + get tenantsecret postgres-.tenant-ca \ --output jsonpath='{.data.ca\.crt}' | base64 --decode ``` -`.tenant-ca` is the only object that hands over the CA certificate without also handing over a private key, which is why it exists. CNPG creates its own `-ca` Secret, but that one stores the CA **private key** (`ca.key`) alongside the certificate — read access to it would let the holder issue certificates for anything, so it is never granted to a tenant. +That object is the only one that hands over the CA certificate without also handing over a private key, which is why it exists. CNPG creates its own `-ca` Secret, but that one stores the CA **private key** (`ca.key`) alongside the certificate — read access to it would let the holder issue certificates for anything, so it is never granted to a tenant. It is reached through `tenantsecrets` rather than by reading the Secret directly, and that is deliberate: `tenantsecrets` surfaces only objects the platform has vouched for (those the application's definition selects), whereas a direct grant on the name would convey whatever happens to occupy that name. diff --git a/packages/system/cozystack-controller/templates/rbac.yaml b/packages/system/cozystack-controller/templates/rbac.yaml index 68417e4cd..4b20c2f03 100644 --- a/packages/system/cozystack-controller/templates/rbac.yaml +++ b/packages/system/cozystack-controller/templates/rbac.yaml @@ -24,8 +24,8 @@ rules: verbs: ["get", "list", "watch", "patch", "update"] # CACertReconciler reconciles TenantProjection sentinels a chart renders and # writes their Ready status. It never creates or deletes a sentinel; that is -# the chart's (helm-controller's) job, with a companion ValidatingAdmissionPolicy -# as defence in depth. +# the chart's (helm-controller's) job. No tenant role grants any verb on +# internal.cozystack.io, which is what keeps sentinels out of tenant reach. - apiGroups: ["internal.cozystack.io"] resources: ["tenantprojections"] verbs: ["get", "list", "watch"] From 9513227fc05f237935a31c4ea5572f14f1d424c9 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 14 Aug 2026 17:11:23 +0300 Subject: [PATCH 2/2] chore(ci): re-run with diagnostics enabled Signed-off-by: Aleksei Sviridkin