docs(postgres): name the trust anchor as the tenant actually sees it (#3463)

## What this PR does

Two documentation defects that both landed with the CA trust-anchor work
in #3407.

The Postgres README told a tenant to fetch `<release>.tenant-ca`. The
release name carries the `postgres-` prefix the application definition
applies, so the object is `postgres-<name>.tenant-ca`, and the postgres
chainsaw test shows exactly that for a resource named `test`. A tenant
following the README got `NotFound`. I confirmed the real name on a live
cluster while converting another engine onto the same contract.

The second is a comment in the controller and one in its RBAC chart,
both promising a companion `ValidatingAdmissionPolicy` that pins
sentinel writes to helm-controller. No such policy ships. It was
proposed in #3410 and rejected: authorizing by
`request.userInfo.username` duplicates RBAC in a more brittle form, and
the RBAC boundary already holds because no tenant role has any verb on
`internal.cozystack.io`. The comments now say that instead of pointing
at a guard that does not exist.

### Downstream repositories

Documentation only, no API or chart behaviour changes.
`cozystack/website` regenerates the Postgres reference page from this
README on the next stable tag, so no manual follow-up is needed there.

- [x] No downstream repository is affected by this change
- [ ] [cozystack/website](https://github.com/cozystack/website) -
follow-up:

### Release note

```release-note
docs(postgres): the tenant CA trust anchor is `postgres-<name>.tenant-ca`, not `<release>.tenant-ca`.
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Updated PostgreSQL TLS setup instructions with the correct tenant CA
Secret name and retrieval command.
* Clarified that tenant-accessible Secrets contain only the CA
certificate, while the original Secret contains the private key.
* Clarified CA sentinel access controls, including tenant isolation and
helm-controller-managed behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
myasnikovdaniil authored and GitHub committed 2026-08-17 14:43:54 +05:00
commit 4bb141f654
3 files changed
+7 -7

No files matched your search

+2 -2
View File
@@ -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
//
+3 -3
View File
@@ -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 `<release>.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-<name>.tenant-ca`, where `<name>` 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 <ctx> --namespace <tenant> \
get tenantsecret <release>.tenant-ca \
get tenantsecret postgres-<name>.tenant-ca \
--output jsonpath='{.data.ca\.crt}' | base64 --decode
```
`<release>.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 `<release>-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 `<release>-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.
@@ -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"]