mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-10 06:24:09 +09:00
3d2dd4271a2ce5567a37494f3d127a52f74b5708
1367
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3d2dd4271a |
fix(mariadb): align TLS wiring with the operator's own TLS surface
Two things the chart got wrong about how mariadb-operator handles TLS. The operator only reconciles on changes to the Secrets behind serverCASecretRef and serverCertSecretRef when those Secrets carry its watch label; the watches are predicate-gated on it. Neither Secret had the label, and cert-manager owns them rather than the MariaDB resource, so ownership does not trigger a reconcile either. The leaf rotates its private key on every renewal, so a renewal would land in the Secret and the running pods would keep serving the superseded certificate until the next full resync. Label both Secrets. The operator also reconciles a trust bundle of its own, named <release>-ca-bundle, whenever TLS is enabled. It contains only ca.crt, never a private key, and it spans a CA rollover. That is exactly the trust anchor the tenant needs, and it already exists in the namespace, so grant it to the tenant instead of stating that no CA is available. The previous wording told operators to skip verification or to turn enforcement off, which weakened security to work around a problem that was not real. Point the backup job at the bundle too. It was projecting ca.crt out of the leaf Secret, which only ever holds the CA that signed the current certificate and needlessly couples backups to leaf rotation. Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
b7bde37832 |
docs(mariadb): document TLS connections and the migration path
Enabling TLS sets require_secure_transport=ON, and external instances get that automatically, so every plaintext client stops connecting the moment the change lands. Nothing user-facing said so, and the recovery HowTo still gave commands that fail against a TLS-enforcing server. Describe how to connect with verification, note that the verification flags are MariaDB spellings rather than the MySQL ones, state that CA distribution to the tenant is not available yet, and give the staged migration using tls.required: false for instances that already have clients. Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
73ac690f1d |
fix(mariadb): close SAN gap and guard the external hostname lookup
The headless wildcard was listed at three resolution depths while every named service was listed at four. A client resolving the missing 3-label form presents that name during the handshake, and a SAN gap fails verification, so the wildcard now covers the same depths as the rest of the list. The external hostname was also read straight off _namespace, which raises a raw nil pointer error naming neither the value nor the reason when the key is absent. Route it through required, and tolerate an absent or null _namespace so the explanatory message is what surfaces rather than an index error. Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
6e51e31510 |
chore(mariadb): drop duplicate test targets from the package Makefile
The target was defined three times, so make emitted an override warning on every invocation and the last definition silently won. All three recipes were identical, so collapsing them to the one declared .PHONY changes no behaviour. Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
b43f00d2bb |
feat(mariadb): publish a key-free CA projection to the tenant
The tenant needs the CA certificate to verify TLS connections, but the cert-manager Secrets that back the chain are key-bearing: <name>-ca-tls holds the CA private key and <name>-tls holds the server private key. The dashboard Role granted the tenant read access to both whenever TLS was on, which handed out signing material where only a trust anchor was required. Drop both from the tenant grant and mark the CA Secret as the source of a key-free projection instead. The chart owns the cert-manager chain here, so it labels the CA Secret directly, inside the existing TLS gate: with TLS disabled the chain is not rendered and nothing is marked. Only the CA carries the labels, never the leaf. The selector added to the ApplicationDefinition is deliberately specific. An empty selector would compile to a match-everything label selector and project every Secret in the namespace to the tenant. The source Secret and the projection are matched by two different labels on purpose: the source stays key-bearing and internal, while the published copy is key-free. Selecting the source directly from the consumer would defeat the split and leak the CA private key. Note that the labels and the selector are inert until the controller that produces the projection exists: nothing reads the labels yet, and the selector matches no Secret. Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
bafaca0991 |
fix(mariadb): use MariaDB TLS verification flags in backup script
The backup image is alpine plus mariadb-client, so the backup script is
parsed by the MariaDB client. That client implements no ssl-mode option:
it is MySQL 8 syntax, and the MariaDB client rejects it while parsing
arguments ("unknown variable", exit 7) before it ever opens a
connection. Every TLS-enabled backup run therefore failed at the first
client invocation.
Replace it with the MariaDB equivalent, --ssl-ca combined with
--ssl-verify-server-cert, which validates the chain against the CA and
checks the server hostname against the certificate SANs.
Also align the cluster-domain fallback in the certificate template with
the rest of the catalog, which defaults to cozy.local rather than the
upstream Kubernetes cluster.local. These names go into the leaf
certificate SAN list that the backup client now verifies against, so a
mismatched fallback would produce certificates that fail hostname
verification.
The previous assertions only checked that an SSL string appeared in the
rendered script, so they stayed green against a script the client
refuses to parse. They now pin the client dialect instead: the MariaDB
verification flags must be present, the MySQL-only option must never be
emitted in any TLS state, and both client invocations must carry the
options.
Signed-off-by: Aleksei Sviridkin <f@lex.la>
|
||
|
|
e4280292fc |
refactor(mariadb): drop null from tls.enabled schema, use kindIs invalid pattern
The tls.enabled field was previously patched in the Makefile via jq to
emit "type": ["boolean","null"], which breaks cozystack-api's
apiextv1.JSONSchemaProps.Type (single string field) and silently
disables server-side defaulting.
cozyvalues-gen correctly emits "type": "boolean" from the @param {*bool}
annotation. The jq patch and its precondition/postcondition checks are
no longer needed and are removed from the Makefile.
The tri-state behavior (unset tls.enabled inherits external) is
preserved through the kindIs "invalid" check in the _tls.tpl helper,
which catches the unset case. Explicitly passing null is rejected by
schema validation, which is the correct behavior for a boolean field.
Assisted-by: LLM
Signed-off-by: Arsolitt <arsolitt@gmail.com>
|
||
|
|
ec9f9863be |
feat(mariadb): support TLS in backup CronJob via --ssl-mode=VERIFY_IDENTITY
When mariadb-operator sets require_secure_transport=ON (tls.required: true), the server rejects plaintext TCP connections. The backup CronJob used plain mysql and mysqldump commands with no SSL flags, causing all backups to fail silently. The leaf certificate SAN list already covers the primary and secondary service names that the backup uses via MYSQL_HOST, so --ssl-mode=VERIFY_IDENTITY (strictest mode with hostname validation) is correct here. The fix conditionally adds: - SSL_OPTS variable in the backup script with --ssl-ca and --ssl-mode - A tls-ca volume mounting ca.crt from the leaf TLS Secret - A corresponding volumeMount at /etc/mysql-ssl in the backup container The TLS path is activated through the same mariadb.tls.enabled helper used elsewhere in the chart, so it follows the external inheritance tri-state correctly. Assisted-by: LLM Signed-off-by: Arsolitt <arsolitt@gmail.com> |
||
|
|
83852538e3 |
refactor(mariadb): switch CA and leaf private keys to ECDSA P-256
ECDSA P-256 keys offer equivalent or better security than RSA-4096 with significantly smaller key material and faster handshake operations. Both CA and leaf certificates are updated; existing rotation policies are preserved (Never for CA, Always for leaf). Note: cert-manager will re-issue both CA and leaf on next reconcile. Existing trust anchors that pin the CA cert will break; tenants must refresh their ca.crt after upgrading. Assisted-by: LLM Signed-off-by: Arsolitt <arsolitt@gmail.com> |
||
|
|
810a0d5c46 |
feat(mariadb): add short-form SAN variants for ndots-shortened clients
Some DNS clients shorten hostnames before resolving them based on the ndots setting. For example, a client inside the same namespace with ndots:5 may try "svc-name.namespace" before the full "svc-name.namespace.svc.cluster.local" form. Without the short-form SANs in the certificate, the TLS handshake fails for those clients even though they reach the correct endpoint. Adds "*.name-internal" and "name.namespace" (without .svc suffix) variants to the leaf certificate SAN list. Assisted-by: LLM Signed-off-by: Arsolitt <arsolitt@gmail.com> |
||
|
|
fa2f1918dc |
feat(mariadb): add crl sign to CA cert usages
Adds the crl sign key usage to the CA certificate for conventional compliance with RFC 5280. CA certificates that may sign CRLs should advertise this usage even when CRL distribution is not currently configured. Assisted-by: LLM Signed-off-by: Arsolitt <arsolitt@gmail.com> |
||
|
|
30c5d7b95e |
feat(mariadb): add digital signature to leaf cert usages
TLS 1.3 and TLS 1.2 ECDHE require the server certificate to have the digital signature key usage so that ephemeral key-exchange parameters can be signed. Without it, strict TLS stacks (Go crypto/tls, BoringSSL) reject the handshake at the ServerKeyExchange step. Assisted-by: LLM Signed-off-by: Arsolitt <arsolitt@gmail.com> |
||
|
|
62f778be19 |
fix(mariadb): include base service hostname in leaf cert SAN list
The leaf cert now covers the base ClusterIP service `<release>` alongside the existing `-primary` and `-secondary` services so a client connection that uses the unqualified release name (e.g. via cozystack platform DNS) passes hostname verification. The headless `-internal` wildcard and the external LoadBalancer hostname are unaffected. Signed-off-by: Arsolitt <arsolitt@gmail.com> |
||
|
|
924e856878 |
fix(mariadb): move version to its own section and expose tls.required default
Add a @section Version parameters marker in values.yaml so that the version parameter is grouped separately from TLS parameters in the generated README and schema. Explicitly set tls.required: true in values.yaml so UI clients and schema consumers see the correct default (true, matching the template behaviour documented in the description field). Also add [\"spec\",\"tls\",\"enabled\"] and [\"spec\",\"tls\",\"required\"] to keysOrder in mariadb-rd so TLS child fields render in a stable order in the dashboard. Regenerate codegen artifacts accordingly. Assisted-by: LLM Signed-off-by: Arsolitt <arsolitt@gmail.com> |
||
|
|
6b5c31979e |
test(mariadb): assert serverCASecretRef and new SAN entries
Add helm-unittest assertions covering the chart changes: - spec.tls.serverCASecretRef.name in both TLS-enabled MariaDB CR cases - localhost and wildcard headless-service SAN entries in the leaf certificate SAN tests for both external=true and explicit tls.enabled=true scenarios Signed-off-by: Arsolitt <arsolitt@gmail.com> |
||
|
|
63dde5cb91 |
fix(mariadb): require serverCASecretRef and add localhost/headless SANs
mariadb-operator's admission webhook rejects MariaDB CRs that set spec.tls.serverCertSecretRef without serverCASecretRef. Point the latter at the cert-manager-issued CA secret. The agent sidecar performs startup/health probes against localhost:3306 and pod-to-pod replication uses the headless internal service. Add localhost and wildcard pod-FQDN entries to the leaf certificate SAN list so both paths complete the TLS handshake. Signed-off-by: Arsolitt <arsolitt@gmail.com> |
||
|
|
228635c44b |
test(mariadb): add TLS test coverage
- Add helm unittest suite covering all tri-state TLS scenarios (unset, false, true, required) - Verify cert-manager resource creation and naming consistency across RBAC and cert objects - Pin leaf certificate EKU composition and absence of CA-only key usages - Cover dashboard-resourcemap tri-state conditional secret exposure Signed-off-by: Arsolitt <arsolitt@gmail.com> |
||
|
|
9df49190f7 |
feat(mariadb): implement TLS in Helm templates
- Add _tls.tpl helper with tri-state resolver for tls.enabled (*bool) supporting unset/false/true - Add certmanager.yaml to provision CA Issuer, CA cert, and leaf cert via cert-manager - Expose TLS secrets to dashboard through dashboard-resourcemap.yaml - Wire tls.required and TLS secret references into MariaDB CR spec in mariadb.yaml Signed-off-by: Arsolitt <arsolitt@gmail.com> |
||
|
|
9ae332cf29 |
feat(mariadb): add TLS schema and generated artifacts
- Add tls.enabled (*bool) and tls.required fields to API types and values - Update JSON schema with nullable tls.enabled tri-state and required flag - Extend Makefile with test target wiring helm-unittest into CI - Update cozyrds descriptor to expose new TLS fields - Update README to document tls.required semantics and nullable type Signed-off-by: Arsolitt <arsolitt@gmail.com> |
||
|
|
3a70f91423 |
fix(vm-disk): fail on storageClass/source edit of existing disk (#3969)
## What this PR does Fixes #2985. Editing a `VMDisk`'s `storageClass` or `source` after its DataVolume already exists was silently ignored: `packages/apps/vm-disk/templates/dv.yaml` looks up the existing DataVolume and, when found, reused its `spec` verbatim (because `DataVolume.spec` is immutable in CDI). The edit therefore rendered as a no-op — the typed CR accepted it and the HelmRelease reported success, while the DataVolume/PVC kept the original spec and the source of truth silently diverged from the cluster. The template now builds the desired `source` into a shared `$desiredSource` variable (used both for rendering and comparison) and, when a DataVolume already exists, calls `fail()` with an explicit message if the requested `storageClass` or `source` differs from the immutable existing spec — mirroring the explicit size handling already done in `pvc-resize-hook.yaml`. Storage growth is unaffected and still flows through the resize hook. `source` is also marked `@immutable` in `values.yaml`, so the generated schema now carries the same `x-kubernetes-validations` transition rule (`self == oldSelf`) that `storageClass` already had; `types.go`, `values.schema.json` and the generated `vm-disk-rd` CRD are regenerated to match. ### Verification - `helm unittest` covers both the immutability guard (via a mocked existing DataVolume through `kubernetesProvider`, since `lookup` needs a cluster) and initial-creation source rendering for every source type — 12/12 pass. - Verified live on a dev cluster with `helm install --dry-run=server` against a real DataVolume: an unchanged edit renders, a `storageClass` change fails, a `source` change fails, and a storage grow still renders the resize hook without tripping the guard. ### Scope note This PR only makes the drift visible and marks `source` immutable. Two deeper platform findings sit outside its scope: (1) #2657 (pre-existing) — the `apps.cozystack.io` aggregated apiserver does not evaluate the `x-kubernetes-validations` CEL rules on update, so `@immutable` is a no-op at admission for every Application kind, which is exactly why the chart-level `fail()` — not the CEL rule — is the substantive guard here; (2) #3971 — that same apiserver does not honour `--dry-run=server` and persists the change (surfaced while testing this fix). ### Downstream repositories Walked the trigger map in `docs/agents/contributing.md` against the diff file by file. The diff touches only `packages/apps/vm-disk/` (template, `values.yaml`, regenerated `values.schema.json`, tests), the regenerated `packages/system/vm-disk-rd/cozyrds/vm-disk.yaml`, and `api/apps/v1alpha1/vmdisk/types.go`. No package was added, renamed or removed; no `values.schema.json` field was added, renamed or removed (only a validation rule on the existing `source` field); no default changed; `README.md` is unchanged; the `cozyrds` file was regenerated, not renamed. The terraform provider models fields, defaults and enums but not CEL immutability, so it is unaffected. No downstream repository is reached by this change. - [x] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: - [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up: - [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up: - [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) - follow-up: - [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) - follow-up: - [ ] [cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server) - follow-up: - [ ] [cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) - follow-up: - [ ] [cozystack/examples](https://github.com/cozystack/examples) - follow-up: ### Release note ```release-note fix(vm-disk): editing storageClass or source on an existing VMDisk now fails with a clear message instead of being silently ignored; delete and recreate the disk to change these immutable fields. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added support for blank, HTTP, image, disk, and upload VM disk sources. * VM disk sources and storage classes are now protected from unintended changes, while storage expansion remains supported. * Existing disks preserve their configured source and storage settings. * **Bug Fixes** * Invalid or conflicting source configurations are rejected. * Prevented silent no-op updates and incorrect immutability errors for existing disks. * **Documentation** * Documented source and storage class immutability, including the need to recreate disks when changing either setting. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
86fe8e5a7e |
feat(valkey): add managed Valkey service (#3380)
## What this PR does Adds a **managed Valkey** service to the catalog, alongside managed Redis. > **Stacked on #3406** (redis-operator migration to freshworks-oss). This PR targets that branch and depends on it; review/merge #3406 first. ### Why Redis relicensed at 8.x to a source-available family (AGPLv3 / SSPL / RSALv2), and the managed Redis app currently ships `v8: 8.4.0` with no BSD-licensed option ≤ 7.2. That is a licensing risk for CNCF Incubation. [Valkey](https://valkey.io) is the BSD-3-Clause fork of Redis 7.2.4, governed by the Linux Foundation, and a drop-in replacement for Redis. This is the same move Argo CD made — [`cncf/foundation#750`](https://github.com/cncf/foundation/issues/750) was closed with "no need for exception" after they migrated to Valkey. ### How it works Valkey runs on the same operator as managed Redis. #3406 migrates that operator from the archived `spotahome/redis-operator` to the actively-maintained **freshworks-oss/redis-operator** fork, which adds a native `spec.engine: Valkey` switch. With it, the operator runs `valkey-server` / `valkey-cli` natively (in the launch command and in its liveness / readiness / shutdown probes), so the chart uses the official `valkey/valkey` image directly — **no Redis-compatibility shim image**. The app mirrors managed Redis: same shape/values (`replicas`, `resources`/`resourcesPreset`, `size`, `storageClass`, `external`, `version`, `authEnabled`), Sentinel-based HA, metrics exporter, `WorkloadMonitor`s, dashboard resource map, and `ApplicationDefinition` (`kind: Valkey`). Versions offered: `v8 → 8.1.8` (default) and `v7 → 7.2.13` (the BSD-3-Clause Redis 7.2 line, most conservative drop-in). ### How it was verified - `helm template` renders cleanly for `v8`, `v7`, and `external: true`; the `RedisFailover` carries `spec.engine: Valkey` and `image: valkey/valkey:<version>`. `helm lint` shows only the pre-existing icon-URL / `cozy-lib` warnings that managed Redis emits too. - `helm template` of `valkey-rd` renders a valid `ApplicationDefinition` (`kind: Valkey`, `plural: valkeys`, `category: PaaS`). - Generated artifacts (`values.schema.json`, `README.md`, `types.go`, `zz_generated.deepcopy.go`, the ApplicationDefinition `openAPISchema`) reproduce with the pinned `cozyvalues-gen` v1.6.0 and `controller-gen` v0.16.4 — root `make generate` and per-app generate both produce no drift; `go build ./valkey/...` passes. - A live-cluster e2e was not run from this environment; the Chainsaw suite `hack/e2e-chainsaw/valkey/` (mirroring the redis suite) is included and `hack/select-e2e.sh` maps the `valkey-application` source to it, so it runs in CI once the freshworks operator image is built. ### Notes - The Valkey logo (`packages/apps/valkey/logos/valkey.svg`) is a placeholder in Valkey's brand colors, pending the official brand asset. - A Grafana `db/valkey` dashboard was intentionally left out of this initial PoC; it can follow. ### Screenshots N/A — no UI changes. ### Downstream repositories This adds a new managed-app kind, which likely warrants follow-ups in `cozystack/website` (user-facing docs for the new service) and possibly `cozystack/terraform-provider-cozystack`. No follow-up PRs are opened yet — leaving these for a maintainer to decide, per the template guidance. - [ ] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: - [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up: - [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up: - [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) - follow-up: - [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) - follow-up: - [ ] [cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server) - follow-up: - [ ] [cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) - follow-up: - [ ] [cozystack/examples](https://github.com/cozystack/examples) - follow-up: ### Release note ```release-note feat(valkey): add managed Valkey service — a BSD-3-Clause, Linux Foundation-governed alternative to managed Redis, running on the freshworks-oss redis-operator via native spec.engine: Valkey (versions 8.1.8 and 7.2.13) ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added managed Valkey service support with high availability and configurable replicas. * Supports Valkey versions 7 and 8, persistent storage, resource presets, authentication, and optional external access. * Added monitoring metrics and dashboard integration. * Added platform package integration and tenant administrator access. * **Documentation** * Added Valkey configuration guidance and storage-class immutability documentation. * **Tests** * Added end-to-end coverage for deployment readiness, storage, and replicas. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
c94a1da5bb |
feat(mongodb): add TLS support via PSMDB operator-managed certificates (#2692)
## What this PR does Enables operator-managed TLS for MongoDB and stops handing tenants any Secret that carries private key material. - Percona Server for MongoDB serves TLS unconditionally (`mode: preferTLS`), so this PR drops the misleading `tls.enabled` toggle rather than pretending the chart controls it. - The tenant no longer receives the operator's CA Secret (`<release>-ca-cert`, which holds `ca.key`) or the leaf (`<release>-ssl`). Both were previously reachable, and either one is a private key in tenant hands, the CA one is a full trust-chain compromise. - The chart declares a `TenantProjection` sentinel naming its CA source Secret (`<release>-ssl`, the operator-managed leaf, which carries `ca.crt`), and the ResourceDefinition selects the resulting key-free `<release>.tenant-ca` projection by the engine-agnostic `internal.cozystack.io/tenant-ca` label. The CA-extraction controller reads the sentinel and publishes the projection holding only `ca.crt`. The tenant gets what it needs to verify the server and nothing else. ### Why the include also carries an exclude list The trust-anchor selector matches by label with no name constraint, so any Secret in the namespace that acquired that label would be promoted with its data copied verbatim. The enumerable dangerous set is therefore excluded explicitly: the CA, both leaves, the rotation snapshots (`-ssl-old`, `-ssl-internal-old`, same server keys as the leaves, and unlike them they carry no owner reference, so an interrupted rotation can leave them behind after the release is gone), the system-user passwords, the at-rest encryption key, the keyfile, the object-storage credentials, and both forms of the operator's internal Secret. That backstop is bounded, and the comment says so: matching is by exact name with no globs, so a Secret a future operator version introduces, or a transient object an issuer creates mid-renewal, stays in scope of the label-only include. The per-user Secrets cannot be enumerated at all, since their names carry the username. The primary defence remains that the controller stamps only its own projection; this closes the enumerable set, not the whole surface. ### Release note ```release-note feat(mongodb): stop exposing TLS private key material to tenants and publish the CA as a key-free trust anchor ``` |
||
|
|
daa867c545 |
fix(valkey): label RedisFailover with operator-group so it is reconciled
The freshworks-oss redis-operator filters its RedisFailover List and Watch with a server-side selector on OPERATOR_GROUP_ID (cozystack). A RedisFailover without the matching operator-group label is never seen by the operator, so no data pods, sentinels, PVCs or services are created. Match the redis app chart, which already sets the label. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Andrey Kolkov <androndo@gmail.com> |
||
|
|
edd767ea0f |
feat(nats): publish ca.crt-only trust anchor to tenants (#3340)
## What this PR does Converges nats onto the key-free CA trust-anchor contract. A TLS-enabled nats release renders a `TenantProjection` sentinel naming the chart's cert-manager CA Secret as its source, and the nats ResourceDefinition selects the resulting `<release>.tenant-ca` object by label. The projection is built by the CA extraction controller in `internal/controller/cacert`, which parses the source certificate and re-encodes it into a single `ca.crt` key, so a tenant that enables TLS gets something to verify the server against without ever getting a private key. No key-bearing Secret is granted to a tenant, by name or by label. `packages/apps/nats/templates/certmanager.yaml` is not touched. An earlier draft of this change stamped publish labels onto the CA `Certificate`'s `secretTemplate`; the controller that shipped consumes a sentinel object instead, so the chart now declares its source rather than marking it. The functional change is one new template and one `matchLabels` entry in the ResourceDefinition. The sentinel is gated on the chart's effective TLS state, which is tri-state: left unset it follows `external`. That gate is the single divergence from the postgres implementation of the same contract, and it exists because the nats CA is only issued while TLS resolves true. An ungated sentinel would sit `Ready=False/SourceNotFound` forever on every plaintext release. The CA extraction controller is already on main, so this is live on merge rather than dormant until a follow-up lands. The `do-not-merge/hold` label predates that and no longer guards anything. ## Verified against a running cluster The premise this rests on is that cert-manager populates `ca.crt` on the Secret of a `selfSigned`, `isCA: true` Certificate. Nothing in this repository witnesses it: `hack/e2e-chainsaw/` has no nats suite, and nats appears in no e2e workflow, so every unit test here encodes the same assumption the chart encodes and the two cannot catch each other being wrong. I closed it against a live cluster instead, running cert-manager v1.20.2, the version this repo ships, on two independent nats releases: - `nats-<name>-ca`, the chart's CA Secret, holds `ca.crt`, `tls.crt` and `tls.key`. The declared source key exists. - `nats-<name>.tenant-ca`, the projection, holds exactly one key, `ca.crt`. - The projection's SHA-256 certificate fingerprint equals the source's, so it carries that CA and not some other certificate. - The projection carries `internal.cozystack.io/tenant-ca: "true"` and `internal.cozystack.io/tenantresource: "true"`. The second is stamped by the lineage webhook, computed from the ResourceDefinition selector this PR adds, so its presence means the whole chain held. - `kubectl get tenantsecret nats-<name>-ca` returns NotFound. The key-bearing Secret is not served through the tenant API. What is still missing is an automated regression witness for that premise. Adding one means a new chainsaw suite for nats, which has no e2e coverage of any kind today, and that is a larger change than this one. ## What the tests pin 61 helm-unittest cases and 3 bats cases. The sentinel's absence of `ownerReferences` (an owner would short-circuit the lineage walk before it reaches the Flux label the attribution depends on), the length of `spec.projections` as well as its single entry (the controller refuses a multi-entry sentinel, so a second entry is a silent loss of the anchor rather than a leak), both implied legs of the tri-state TLS gate, and `notExists` on either visibility label for both key-bearing Certificates. The bats guard covers the two grant surfaces nothing else asserts on. The ResourceDefinition is rendered, by the `nats-rd` chart inlining `cozyrds/*`, but that chart has neither a tests directory nor the `test` target `hack/helm-unit-tests.sh` looks for, and no `packages/system/*-rd` package does. The dashboard Role is rendered by a chart that has both, and no suite under `packages/apps/nats/tests/` asserts on it. Both are compared whole rather than filtered down to the entries that mention a Secret, because every such filter admits the spelling it did not anticipate. A name whitelist passes an added label selector, and `controller.cert-manager.io/fao` is stamped on both key-bearing Secrets, so a selector on it is a one-line grant of the CA private key. On the Role side, a rule with no `resourceNames` names no Secret, a rule with `resources: ["*"]` names no resource, and a ClusterRole is not a Role, while all three grant read access to the CA key. Comparing the rendered output whole needs no filter and leaves no spelling to find. ## Downstream repositories Walked the trigger map in `docs/agents/contributing.md` against the diff, file by file. No trigger fires, and no follow-up PR is owed. `packages/apps/nats/templates/tenant-projection.yaml` is a new template and `packages/apps/nats/tests/` holds chart tests. No package is added, renamed or removed; `values.yaml` and `values.schema.json` are untouched, so neither the hand-written Terraform schema, its version enums, nor its restated defaults have anything to track. `kind`, `plural` and `release.prefix` are unchanged, and no output Secret or Service is renamed, so the provider's concatenated name lookups still resolve. `packages/system/nats-rd/cozyrds/nats.yaml` is edited in place rather than renamed, so the ccp external-app skill's dependency-contract anchor still resolves. The provider offers no typed access to the new projection, which is the position it already holds for every Secret it does not model, and not a break. `packages/apps/nats/README.md` is covered by the one automated path that does exist: `nats` is present in the website `Makefile` `APPS` list, so the docs bot regenerates the managed-apps reference page from this `README.md` on the next stable tag. This is the silent-skip failure mode the trigger map warns about, so I checked the list rather than assuming; an app missing from it gets no page at all, which is what happened to `opensearch`. `hack/check-nats-rd-secrets.bats` is a new file, not a move or a rename, and it changes no make target's contract: `Makefile` globs `hack/*.bats` into `BATS_UNIT_FILES` automatically. The ccp skills anchor on `hack/package.mk` and `hack/common-envs.mk`, neither of which is touched. - [x] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: - [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up: - [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up: - [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) - follow-up: - [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) - follow-up: - [ ] [cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server) - follow-up: - [ ] [cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) - follow-up: - [ ] [cozystack/examples](https://github.com/cozystack/examples) - follow-up: Part of #2814. ### Release note ```release-note feat(nats): expose the CA certificate to tenants as a key-free `<release>.tenant-ca` Secret ``` |
||
|
|
65e700486b |
feat(qdrant): publish ca.crt-only trust anchor to tenants (#3341)
## What this PR does Converges qdrant onto the key-free CA trust-anchor contract (#2814), the same way #3340 does it for nats. The chart declares a `TenantProjection` sentinel naming its cert-manager CA Secret. The CA-extraction controller reads that declaration and publishes a key-free copy, `ca.crt` and nothing else, as `<release>.tenant-ca`. The qdrant ResourceDefinition selects that projection by label so it shows up in the tenant's secret list. No key-bearing Secret gains a tenant-visible label. The sentinel is gated on the chart's TLS condition. Engines whose operator mints a CA unconditionally do not need that, but qdrant only has a CA while TLS is on, and an ungated sentinel would sit `Ready=False`/`SourceNotFound` on every plaintext release. This was held while the CA-extraction controller was still open. That controller landed in #3407, so the sentinel is honoured as soon as this merges and the hold is gone. Tests pin the invariants that otherwise fail silently. The sentinel carries no `ownerReferences`, so the lineage walk falls through to the Flux release label. Neither key-bearing Certificate stamps a tenant-visible label on its Secret. The two objects that decide which Secrets a tenant may actually read, the dashboard Role with its binding and the ResourceDefinition, are pinned whole rather than checked for known-bad names: a rule granting secrets with no `resourceNames` is unrestricted rather than narrow, and a label selector can reach a key-bearing Secret while every name in the file stays correct, so neither adds a name for a probe to notice. Part of #2814. ### Follow-ups What this PR does not do, and where each belongs instead. - nats converges the same contract in #3340 and has no `dashboard-resourcemap_test.yaml`, so the dashboard Role pin belongs there too. Tracked in #3701 along with postgres, which already carries the label selector with no guard behind it. - The qdrant e2e fixture runs plaintext (`hack/e2e-chainsaw/qdrant/qdrant.yaml` sets `external: false`), so nothing in CI walks the path from the sentinel to `tenantsecrets`. postgres has a `verify-tenant-ca-projection` step to copy. - `hack/check-qdrant-rd-secrets.bats` is engine-agnostic apart from two literals, the chart path and the expected Secret set. The third copy of it is the point to parameterise over `packages/system/*-rd/cozyrds/` instead of copying again. ### Downstream repositories Walked the trigger map in `docs/agents/contributing.md` against the diff, file by file. Nothing needs a manual follow-up, but not for the reason a values-only walk would give: the website reference page for an app is regenerated from that package's `README.md`, and this PR does change it. `qdrant` is in the app list hardcoded in the website `Makefile`, so the docs bot picks the new section up on the next stable tag and there is nothing to open by hand. The Terraform provider's schema is hand-written against `values.schema.json`, which is untouched, and the provider offers no typed access to the new projection, the same position it holds for every other Secret it does not model. No package is added, renamed or removed, and the `cozyrds` file is edited in place rather than renamed, so the ccp dependency-contract anchor still resolves. User-facing documentation for consuming the trust anchor ships with the chart README, matching #3340. - [x] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: - [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up: - [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up: - [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) - follow-up: - [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) - follow-up: - [ ] [cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server) - follow-up: - [ ] [cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) - follow-up: - [ ] [cozystack/examples](https://github.com/cozystack/examples) - follow-up: ### Release note ```release-note feat(qdrant): expose the CA certificate to tenants as a key-free `<release>.tenant-ca` Secret ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Tenant environments can securely access the Qdrant TLS CA certificate without receiving the private key. * TLS CA access is automatically enabled when TLS or external access is configured. * **Documentation** * Expanded TLS guidance explains certificate validation and provides commands for retrieving the tenant CA certificate. * **Tests** * Added coverage for TLS projections, secret visibility, dashboard resources, and secure fail-closed secret exposure. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
8d386c2cbc |
docs(mongodb): name the trust anchor as the tenant sees it
Every object this chart creates is named after the Helm release, not after the MongoDB resource, so a MongoDB named catest yields mongodb-catest.tenant-ca. The retrieval commands used a bare <release> placeholder, which reads as the resource name and produces a command that returns nothing. Spell the prefix out in the two commands meant to be copied, and define the placeholder once for the prose. Split the sentinel's shape test in two. The absence of ownerReferences is the invariant — it breaks the walk from the projection back to the release, and the tenant stops receiving the anchor. An explicit namespace or a chart-added label breaks nothing by itself; grouping all three under one name presented them as equally load-bearing. Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
04112dacdf |
test(mongodb): stop the tenant-Role guards passing on the wrong document
The assertions withholding the key-bearing TLS Secrets from the tenant Role addressed it as document 0 and, in two cases, asserted only with notContains. Neither survives a rearrangement. notContains passes when its path does not resolve, so a test built from it alone goes green against the wrong document. Reversing the order of the two documents in the template moved the RoleBinding into position 0, and "does not grant the key-bearing TLS secrets when external is enabled" and "does not grant the tenant-ca projection by name" both stayed green while the Role granted <release>-ca-cert by name — the Secret holding the CA private key. Select the Role by kind, and pair every notContains with a positive assertion on the same path so a rule reordering that moves rules[1] reddens too. Indexing survives neither rearrangement; selecting survives the first; the positive anchor catches the second. Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
8b6867dc47 |
fix(mongodb): correct the trust-anchor prose and two e2e assertions
The name-clash note claimed the shared TLS helper documents <release>-ca-cert as the conventional key-free anchor. It documents the opposite: the canonical name is <release>.tenant-ca, and it names this engine as the reason, because MongoDB already claims <release>-ca-cert with a key-bearing Secret. There is no clash to warn about and the chart follows the convention rather than deviating from it. The paragraph above already says <release>-ca-cert holds the CA private key, so drop the note instead of inverting it. Guard the ownerReference assertions against an absent list. length() of null is a type error rather than a failed comparison, and the harness returns it as an error, which aborts the poll instead of retrying — so the timeout never applies while the operator is still patching the reference on. Restore the two-variable form of the fingerprint comparison. Inlining both command substitutions into the test swallows a non-zero exit: with no openssl on PATH both sides are empty, they compare equal, and the check passes having compared nothing. Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
01cfb5fda0 |
fix(mongodb): correct what a missing extraction controller does
The README said the sentinel would sit inert with nothing else affected. The CRD is a template of the controller's own chart, so a cluster without the controller has no TenantProjection kind and the release fails to render at all. The caveat also guarded a state that cannot occur, since the controller is a mandatory platform component — drop it rather than restate it, matching the postgres chart, and keep the paragraph explaining the choice of source Secret. Enumerate all three key-bearing Secrets in the registry negative. Two of the three were listed, which leaves the third free to acquire the label and leak a key, the exact reasoning the step above it already spells out. Collect TenantProjection status on failure. The controller reports SourceNotFound and its siblings there, and without it a failed projection surfaces only as an absent Secret with no reason given. Rename the source-shape step, which no longer looks at the CA Secret, and cross-reference the two halves of the leaf Secret's name. Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
fc57e928e1 |
test(mongodb): drop a document count the sentinel invalidates
The cert-manager exclusion suite justified its `any: true` requirement with a specific number of rendered documents. The argument only needs "more than one"; the number was already tied to one value set and the sentinel adds another document. Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
29d2955241 |
fix(mongodb): declare the trust anchor with a TenantProjection
A tenant verifying a MongoDB endpoint needs the CA certificate, and every Secret that holds one also holds a private key: the operator's <release>-ca-cert carries the CA key, the leaf <release>-ssl the server key. Granting either would hand over the key along with the anchor. Render a TenantProjection naming the source, so the CA-extraction controller can publish the key-free <release>.tenant-ca, and select that projection by label from the managed-service definition. The sentinel carries no ownerReferences: it is resolved back to its release through the label Flux stamps, and an owner reference short-circuits that walk and silently costs the tenant its anchor. The sentinel is ungated. The operator issues the certificate chain whatever the chart asks for, so the source exists for every release and there is no plaintext leg to skip. The source is <release>-ssl rather than <release>-ca-cert. It is the name this chart itself sets in spec.secrets.ssl, so the reference cannot drift from the CR; the operator writes it on both its cert-manager path and the self-signed fallback it uses when cert-manager is absent, where no <release>-ca-cert exists at all; and it holds the merged old and new CA through a rotation, where <release>-ca-cert holds only the new one and would stop verifying pods still serving the previous leaf. Withhold the key-bearing Secrets from the tenant explicitly as well. The label selector matches on no name, so a single mis-stamp would promote a private key; the exclude list is evaluated first and closes the enumerable set. Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
4092f59528 |
docs(mongodb): scope the upgrade-availability claim to quorum-capable sizes
The upgrade-restart note said the replica set stays available through the rollout. That holds at three members and fails below them, and the chart permits both smaller sizes: replicas 1 and 2 render unsafeFlags.replsetSize and are not excluded from the SAN change, so they gain horizons and roll on upgrade exactly as a three-member release does. At one replica the rollout takes the only mongod down; at two the write majority is still two, so restarting either member stalls writes until it returns. An operator running a single-replica external release would have read the old text and declined to book a window. Cover the two replicas case, which was untested, and assert horizons and the sub-quorum marker together at both sizes, so gating horizons to sizes that can absorb a restart turns the tests red and sends someone back to this paragraph. The default-size case carries the pair as a positive control: the marker proves nothing unless it is absent where a restart is survivable. Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
7ef09cc963 |
docs(mongodb): show how to verify a connection against the trust anchor
The CA retrieval command was documented without the other half: what to do with the bundle once it is in hand. A tenant reading only the retrieval step has the certificate and no worked example of a verified connection. Pass the two TLS inputs as mongosh flags rather than folding tls=true into the connection string, because the correct separator differs by topology -- the replica-set URI already carries a ?replicaSet=rs0 query and the sharded one carries none, so a single copyable form would be wrong for one of them. Call out that omitting the flags is silent. Under preferTLS the server accepts the plaintext connection and nothing reports the downgrade, so a working connection is not evidence that TLS was negotiated. Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
8af39eea22 |
docs(mongodb): correct the external SAN version gate and the leaf lifetime
The external-hostname note claimed the operator and the CR version were "both shipped by this chart". Only crVersion is: the PSMDB operator is a separate platform component with its own lifecycle, and the SAN fold is gated on both. On a platform carrying an older operator nothing reports the unmet gate — the chart still renders splitHorizons, the CR is still accepted and the cluster still comes up healthy, while the external hostnames never enter the SAN list and a verifying client fails the handshake. State which half the chart owns and point at the operator version as the thing to check. The leaf certificate lifetime was stated as a flat 90 days. It is an operator default this chart deliberately does not pin: nothing in the tenant contract keys off it, the operator reissues without client action, and pinning would freeze the chart against upstream shortening it. Attribute the number to the operator version and pin the field's absence, so "inherited rather than chosen" stays falsifiable — the same treatment allowInvalidCertificates already gets. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
90eb5c6815 |
test(mongodb): make the cert-manager exclusion suite discriminate
Every assertion in the suite passed while checking nothing. containsDocument without `any: true` asserts that EVERY rendered document matches, so negating it asks whether "not every document is a Certificate" — true of any chart rendering more than one object, and this one renders eight. A cert-manager Certificate appended to templates/mongodb.yaml, the most plausible place for one to appear, left the suite fully green. Add `any: true` to each containsDocument so the negation means "no document anywhere is of this kind". Unscoped assertions are ANDed across template files, which is what makes the negatives correct: they go red if any file renders the forbidden kind. Add a positive control asserting the chart does render the PerconaServerMongoDB CR through the same matcher shape, scoped to the one template that emits it. The four negatives are all satisfied by a matcher that never matches, which is exactly how this suite failed; the control fails if that returns. Verified by injecting a Certificate into the chart: the suite now fails and previously passed. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
ea7bc00016 |
docs(mongodb): correct the TLS secret ownership and external SAN claims
The uninstall section promised that no manual cleanup of the TLS Secrets is needed, reversing the instruction it replaced. That promise rests on an ownerReference the operator patches on at reconcile time rather than sets at creation, and two paths skip the patch: a dry-run reconcile returns before it, and a Secret already present under the same name with a mismatched cert-manager.io/certificate-name annotation is counted toward success and returned on unpatched. The deletion finalizer does not cover the gap — it enumerates only the users, internal-<release>, internal-<release>-users and encryption-key Secrets. So a Secret predating the release at one of these names can stay unowned and survive uninstall, and for <release>-ca-cert that orphans the CA private key. State the reconcile-time mechanism and tell operators to confirm the CA Secret is gone. Pin the outcome with a chainsaw step asserting the CR owns all three TLS Secrets on a normal install, so the claim goes red if the operator stops patching instead of rotting as prose. The external-hostname note described a permanently empty _namespace.host as a bootstrap window that self-heals. A tenant with no host of its own and no ancestor supplying one resolves to an empty host for good, leaving external TLS hostname verification silently unavailable. Name that case alongside the transient one, and say why the render is not failed closed: it would break existing hostless tenants already running external access over IP. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
5be4e417b3 |
docs(mongodb): correct the operator-behaviour claims and the upgrade impact
The TLS notes made several claims the operator's own behaviour contradicts. The CA is issued for a year and renewed about a month early, not for a decade with rotation disabled — so a bundle copied once stops verifying, and the retrieval steps now say to re-read it. Sharded mongos SANs are complete, because the operator emits them for every topology; the real gap is that no SAN covers the single external LoadBalancer fronting mongos, so external hostname verification is unsupported there. preferTLS is the operator's own default rather than a posture adopted to accommodate backups, which run under the same certificates. External access in replica set mode is one LoadBalancer per member with driver-side primary discovery, not a single load-balanced endpoint — the old description contradicted the external SAN note. And the posture section named only one of its two inherited weaknesses: unverified clients are accepted as well as plaintext ones. The lifecycle section was wrong in both directions. The operator owns its TLS Secrets and its cert-manager objects through the CR, so they are collected on uninstall rather than left behind. The rotation snapshots are the opposite of what was written: the operator deletes them itself once the merged CA has landed, and reads the previous CA back out of them while a rotation is in flight — so the advice to delete them by hand could destroy the only copy of the old CA. Only an interrupted rotation strands them. Document what an upgrade actually does to a release already running with external access: the new hostnames become certificate SANs, the leaf is reissued, and the replica set rolls. Gate the retrieval steps on the component that publishes the trust anchor and on cert-manager, without which the declared source does not exist and the command returns nothing. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
2670f7efc1 |
build(mongodb): run the chart's unit tests
The unit-test runner discovers charts by probing for a test target, and this chart had none — so its suites had never run in CI. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
612ae08e5f |
fix(mongodb): omit splitHorizons where it would render empty
With no external hostname to advertise — zero replicas, or a tenant host the controller has not populated yet — the horizons range produced nothing and the key was emitted with a null value. The CRD types it as an object without nullable, so the API server rejected the whole PerconaServerMongoDB and the HelmRelease with it. The values schema permits zero replicas, so this was reachable. Guard on both and omit the key entirely. Pin the surrounding invariants, which were asserted in comments and enforced nowhere. The chart must render no cert-manager objects of its own, since the operator owns the chain and anything rendered here would be orphaned while looking like it participated. It must set neither of the two fields that switch TLS off at defaulting time while the rendered mode still reads preferTLS. And the external-access case now supplies a tenant host, so it isolates the flag it names rather than passing on the host guard. Correct the header comment, which fused two false claims: that setting spec.secrets.ssl is what keeps TLS on, and that it is what engages the cert-manager integration. The operator defaults that name unconditionally and runs the cert-manager path regardless. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
27f71bfbe9 |
fix(mongodb): deliver the TLS trust anchor without private key material
The tenant-facing paths handed over both Secrets the PSMDB operator creates for TLS. <release>-ca-cert holds the CA private key, so read access is a full trust-chain compromise; <release>-ssl holds the server private key, so read access allows impersonation. A client verifying the endpoint needs ca.crt and nothing else, so neither object belongs on a tenant-facing path. Declare the trust anchor by name instead. The operator creates its CA Secret itself and offers no way to label it, so the ApplicationDefinition names it and the CA-extraction controller lifts ca.crt alone into the key-free projection. The include selector matches that projection by label, and the dashboard Role drops both key-bearing names without gaining the projection by name: a name grant conveys whatever occupies the name, while the label path conveys only what the platform vouched for. Witness both delivery paths against a running cluster, since the two that matter are separate. The Role is asserted as an exact grant with bounded rule counts, because a rule with no resourceNames — or one granting every resource — would otherwise pass an absence check while conveying every Secret in the namespace. The label path is asserted as a negative on all three key-bearing Secrets. A bats check pins the declaration's names and keys, which nothing else covers: the field is pruned at admission until the controller ships, so drift there would surface as an empty trust anchor with nothing red. Drop the mongodb-rd unit test. That package has no test target, so the file never ran, and what it asserted was the leak. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> |
||
|
|
7f8f32b1ba |
fix(mongodb): always enable operator-managed TLS, drop tls.enabled field
Address review feedback from lexfrei (B3): the tri-state tls.enabled field could not actually disable TLS. The PSMDB operator defaults spec.tls to preferTLS whenever it is unset, so tls.enabled: false rendered no spec.tls while the operator still served TLS — a misleading no-op. Remove the field and unconditionally render spec.secrets.ssl and spec.tls.mode: preferTLS to match operator behaviour. splitHorizons SAN injection now keys off external alone. Also drop the Makefile jq patch that re-injected the tls schema property. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Arsolitt <arsolitt@gmail.com> |
||
|
|
e9f276195f |
test(mongodb): cover TLS modes, splitHorizons, and RBAC
Signed-off-by: Arsolitt <arsolitt@gmail.com> Assisted-By: Claude <noreply@anthropic.com> |
||
|
|
f6816e1f44 |
feat(mongodb): add operator-managed TLS with external hostname SANs
Signed-off-by: Arsolitt <arsolitt@gmail.com> Assisted-By: Claude <noreply@anthropic.com> |
||
|
|
785b0feb68 |
feat(valkey): use native engine:Valkey instead of a redis-compat image
With the operator on freshworks-oss/redis-operator, RedisFailover supports spec.engine: Valkey — the operator runs valkey-server/valkey-cli natively. Drop the thin Redis-compat wrapper image entirely and point the chart at the official valkey/valkey image directly: - RedisFailover: set spec.engine: Valkey, image valkey/valkey:<version> - remove images/ (Dockerfile + per-version tags), the image build target, the root build wiring and the images helmignore entry Depends on the freshworks-oss operator migration. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io> |
||
|
|
a32f26df84 |
fix(valkey): address review feedback
- servicescrape: fix target_label -> targetLabel (snake_case was ignored by the VM operator), drop the empty metricRelabelConfigs and annotations keys - service: drop the redundant inner external conditional (the whole manifest is already gated on .Values.external) - update-versions.sh: write through the trap-tracked temp file and cat back to preserve values.yaml permissions - regenerate README.md parameter table with the pinned cozyvalues-gen v1.6.0 (column widths differed from the committed version) Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io> |
||
|
|
6660a44376 |
feat(valkey): add managed Valkey application package
Add a managed Valkey service mirroring the managed Redis app. Valkey is the BSD-3-Clause, Linux Foundation-governed fork of Redis 7.2 and a drop-in replacement for it, offered as a governance-clean alternative to the now source-available (AGPLv3/SSPL/RSALv2) Redis 8.x. Valkey runs on the same Spotahome redis-operator: the operator hardcodes the redis-server command and redis-cli in its (non-overridable) probe scripts, so the chart deploys a thin compat image (FROM valkey/valkey:<ver>) that adds the redis-* symlinks over the official upstream binaries — no Valkey code rebuilt. Versions offered: v8 (8.1.8, default) and v7 (7.2.13, the BSD Redis 7.2 line). Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <andrei.kvapil@aenix.io> |
||
|
|
06624bacd0 |
fix(vm-disk): fail on storageClass/source edit of existing disk
Editing a VMDisk's storageClass or source after its DataVolume already
exists was silently ignored: dv.yaml reused the existing DataVolume.spec
verbatim (DataVolume.spec is immutable in CDI), so the edit rendered as a
no-op while the HelmRelease reported success and the CR diverged from the
cluster.
When a DataVolume already exists, fail with a clear message if the
requested storageClass or source differs from what the disk was created
with. Both compare the operator's input recorded in an annotation at
creation (vm-disk.cozystack.io/storage-class and .../source), written only
at creation and preserved verbatim afterwards — never overwritten with an
unapplied edit. Recording the input rather than reading the live object
matters on both axes: the image-clone PVC name carries a chart-version
prefix (vm-image- was renamed to vm-default-images-, commit
|
||
|
|
b9a95a0c4a |
fix(postgres): let in-place restore re-bootstrap instead of wedging (#3959)
## What this PR does An in-place `RestoreJob` (one whose `targetApplicationRef` is omitted, so the app is restored back into itself) re-bootstraps the Postgres app under its own name. On stock v1.6.2 it never comes back up — the cluster is deleted and then hangs in `Setting up primary` with `*-full-recovery-*` pods crash-looping, or the HelmRelease upgrade fails outright. There are two independent causes, both in the same restore patch, and both surface only in place because a to-copy restore targets a different app. **1. Archive-vs-recovery `serverName` collision.** The driver set the restored cluster's WAL-archive `serverName` equal to the recovery source (`bootstrap.serverName = bootstrap.oldName = sourceServerName`), while the chart pinned the archiver `serverName` to the release name. In place those coincide, so the cluster archived onto the source's own WAL prefix and `barman-cloud-check-wal-archive` refused to start with `Expected empty archive`. Fixed by giving the restored cluster a fresh `bootstrap.newServerName`, keyed by the `RestoreJob` UID (unique per restore, stable across reconciles); the chart archives there while `externalClusters` still recovers from `bootstrap.serverName`. This is what manual `CNPG` strategies hit (their `serverName` defaults to the cluster name, including the shipped `examples/backups/postgres/10-cnpg-strategy.yaml`); the `cozy-default` flow dodged it because its `serverName` is `<namespace>-<app>`. **2. `useSystemBucket` guard.** The restore populates explicit S3 coordinates (`destinationPath` / `s3CredentialsSecret` / `endpointCA`) but left `backup.useSystemBucket=true`, so the chart's `bootstrap.enabled is incompatible with useSystemBucket` guard failed the HelmRelease upgrade and the cluster never re-rendered. This is what the default `cozy-default` (system-bucket) flow hits on an in-place restore. Fixed by clearing `useSystemBucket` in the restore patch, since the app is now on the explicit-creds path. Verified live on a v1.6.2 cluster: with `useSystemBucket` cleared, the same in-place restore re-bootstraps to a healthy cluster and the sentinel row survives. Both are exercised only by an in-place restore; the postgres backup e2e drove only to-copy, so neither surfaced in CI. A step 50 in-place restore now guards cause 1 (manual-strategy flow); cause 2 is covered by a Go unit assertion (a system-bucket in-place e2e needs the platform bucket wired in CI and is left as a follow-up). This is a bug in released v1.6.2 (chart and driver are byte-identical on `main`), so it is a backport candidate. ### Screenshots Not applicable (no UI changes). ### Downstream repositories The only externally visible schema surface is one new field, `spec.bootstrap.newServerName`, in the postgres `values.schema.json` / CRD; it is set by the CNPG backup driver on restore and never authored by tenants, exactly like the existing `bootstrap.serverName` / `bootstrap.oldName`. (The `useSystemBucket` clearing is internal to the Go driver — `backup.useSystemBucket` already exists in the schema.) The chart `README.md` is regenerated from `values.yaml` automatically by the website docs bot on release, and `terraform-provider-cozystack` models user-authored spec (its guard tracks top-level fields, not driver-set nested ones), so neither needs a change in practice. Leaving every box unticked per the trigger-map guidance so a maintainer confirms rather than acting on a speculative follow-up. - [ ] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: - [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up: - [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up: - [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) - follow-up: - [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) - follow-up: - [ ] [cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server) - follow-up: - [ ] [cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) - follow-up: - [ ] [cozystack/examples](https://github.com/cozystack/examples) - follow-up: ### Release note ```release-note fix(postgres): in-place restore no longer wedges — the restored cluster archives WAL under a fresh serverName distinct from the recovery source, and a system-bucket app is switched to explicit credentials so the bootstrap/useSystemBucket guard no longer fails its HelmRelease. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added support for specifying a distinct WAL archive destination when restoring PostgreSQL clusters. - In-place restores now automatically use a fresh archive location while continuing to recover from the original source. - Added configuration and schema support for the optional `bootstrap.newServerName` setting. - **Bug Fixes** - Prevented archive conflicts that could cause in-place restores to fail. - **Documentation** - Documented the new restore archive configuration and behavior. - **Tests** - Added coverage for unique restore destinations and in-place restore validation. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
ac00577eb7 |
fix(clickhouse): scheme the backup S3_ENDPOINT on the system-bucket flow (#3963)
## What this PR does On the default `cozy-default` / `useSystemBucket` backup flow, the ClickHouse chart pointed the `clickhouse-backup` sidecar's `S3_ENDPOINT` straight at the projected `cozy-backups-creds.endpoint`, which the backup credentials projector stores as a **bare host** (no scheme) by design. `altinity/clickhouse-backup` runs `S3_ENDPOINT` through the aws-sdk-go-v2 endpoint resolver, which rejects a scheme-less value with `Custom endpoint 's3.host' was not a valid URI` — so every ClickHouse backup and restore on the default flow failed. Reproduced live on a v1.6.2 cluster: a `cozy-default` `BackupJob` against a `useSystemBucket: true` ClickHouse hangs, and the sidecar logs `S3 ResolveEndpoint ... was not a valid URI`. The fix prepends `https://` on the `useSystemBucket` path via a helper env plus `$(VAR)` expansion (a `secretKeyRef` value can't be string-interpolated), mirroring the platform's `backupstrategy-controller.endpoint` helper which force-prepends `https://` for the always-TLS external S3 ingress. The legacy and bring-your-own-Secret paths keep reading `endpoint` verbatim, since that value is tenant-supplied and already carries its own scheme. ClickHouse was the only `cozy-default` app hit by this. I audited every default-flow app: the CNPG/postgres, etcd and velero strategies already get a scheme'd URL from the endpoint helper, and mariadb and foundationdb deliberately pair a bare host with a separate TLS flag; mongodb reads a full URL from its own chart values. So this is the single scheme-class fix needed. Not addressed here (separate, non-scheme gaps surfaced during the audit, flagged for follow-up): MongoDB has no `useSystemBucket` wiring at all (its `cozy-default` strategy assumes a manually-configured `backup.endpointURL` + per-app S3 creds), and FoundationDB's self-signed-CA trust to the operator pod is a known gap. ### Screenshots Not applicable (no UI changes). ### Downstream repositories No schema or API change (only a chart template and its helm-unittest). `values.yaml` / `values.schema.json` / the CRD are untouched, so nothing downstream is affected. Leaving every box unticked per the trigger-map guidance. - [ ] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: - [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up: - [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up: - [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) - follow-up: - [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) - follow-up: - [ ] [cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server) - follow-up: - [ ] [cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) - follow-up: - [ ] [cozystack/examples](https://github.com/cozystack/examples) - follow-up: ### Release note ```release-note fix(clickhouse): backup and restore now work on the default system-bucket (useSystemBucket) flow — the clickhouse-backup sidecar receives a scheme'd https:// S3_ENDPOINT instead of the projected bare host, which the S3 client rejected as an invalid URI. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Corrected ClickHouse backup endpoint configuration when using the system bucket. * Ensured backup connections consistently use the required HTTPS endpoint format. * Improved handling of backup credentials across system bucket, legacy, and custom-secret configurations. * **Tests** * Added coverage validating endpoint and credential wiring for supported backup configurations. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |
||
|
|
9dd2841c74 |
feat(clickhouse): add configurable version parameter (#3476)
## What this PR does The ClickHouse server and Keeper images were hardcoded to `24.9.2.42` in the chart templates, so the chart exposed no top-level `version` values key. The Cozystack API returns a managed database's engine version by passing the HelmRelease's `spec.values.version` straight through into the Application object, so ClickHouse instances carried no `spec.version` in the API — unlike postgres, mariadb, mongodb, opensearch, rabbitmq and redis, which all ship a `version` parameter. This closes the ClickHouse half of the gap tracked in #1246. This adds the same version-selection scaffolding the other managed-database charts already use: - `files/versions.yaml` maps a `major.minor` key to the full image tag, shared by the ClickHouse server and Keeper images (both are pinned to one version). - `templates/_versions.tpl` (`clickhouse.versionMap`) resolves the tag and fails the render on an unsupported version. - `values.yaml` gains a `version` enum (`v25.8`, `v25.3`, `v24.9`), consumed by `templates/clickhouse.yaml` and `templates/chkeeper.yaml`. - `hack/update-versions.sh` + `make update` refresh the map from Docker Hub, keeping only tags published for *both* the server and Keeper images and preserving the current default so a regeneration never bumps the major of existing deployments. The default stays `v24.9` → `24.9.2.42`, so existing installations render byte-for-byte identically and are never upgraded to a new major implicitly. The generated `values.schema.json`, `README.md`, the Go API type and the `clickhouse-rd` `openAPISchema` are regenerated with `cozyvalues-gen` v1.6.0. A new `tests/version_test.yaml` helm-unittest suite covers the server/Keeper mapping for each version and the schema-level rejection of an unsupported value. ### Downstream repositories The diff changes `packages/apps/clickhouse/values.schema.json` by adding a new `version` field with an `enum`. Per the trigger map, this reaches `terraform-provider-cozystack`, whose ClickHouse resource is hand-written and would need a matching `version` attribute with a `stringvalidator.OneOf` list and its expand/flatten pair. There is no existing PR or issue there for it yet. This is left for a maintainer decision rather than a speculative cross-repo PR, so the box below is intentionally left unticked and flagged here. No other downstream repository is reached (the website reference page is regenerated from `README.md` by the release docs bot; ClickHouse is already in its app list). - [ ] No downstream repository is affected by this change - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: - [ ] [cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack) - follow-up: needs a `version` attribute on the ClickHouse resource (maintainer decision — see note above) - [ ] [cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack) - follow-up: - [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up: - [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up: - [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) - follow-up: - [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) - follow-up: - [ ] [cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server) - follow-up: - [ ] [cozystack/external-apps-example](https://github.com/cozystack/external-apps-example) - follow-up: - [ ] [cozystack/examples](https://github.com/cozystack/examples) - follow-up: ### Release note ```release-note feat(clickhouse): add a configurable `version` parameter (v25.8, v25.3, v24.9; default v24.9) so the deployed ClickHouse version is selectable and returned through the API. Existing installations are unchanged. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a configurable ClickHouse version setting for both server and Keeper images, defaulting to `v24.9`. * Supported versions are `v25.8`, `v25.3`, and `v24.9`, with compatible image patches selected automatically. * **Documentation** * Documented the version setting, default, and supported values. * **Validation** * Unsupported versions are rejected with an error listing allowed options. * **Tests** * Added coverage for version mapping, defaults, invalid values, and update behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> |