Repository navigation
Description
What happened
backupstrategy-controller renders every cozy-default backup Strategy (etcd, cnpg/postgres, mariadb, foundationdb…) with the internal system-bucket endpoint:
# packages/system/backupstrategy-controller/values.yaml
backupStorage:
endpoint: http://seaweedfs-s3.tenant-root.svc.cozy.local:8333
A backup/snapshot pod that runs in a child tenant's namespace cannot reach that ClusterIP: the tenant's Cilium network isolation (allow-to-* egress allowlist, plus spec.isolated on the root tenant that hosts the SeaweedFS) blocks cross-tenant traffic, so the pod gets dial tcp <clusterip>:8333: i/o timeout.
This surfaced via the etcd v1alpha2 adoption safety-snapshot (migration 50): the migration correctly stages cozy-backups-creds into each adopted namespace (credentials are fine), but the per-namespace snapshot Job's upload to S3 times out from a child tenant. The useSystemBucket schema on the app charts (clickhouse/postgres/…) documents that any tenant app can back up to the system cozy-backups bucket via the cozy-default BackupClass with S3_PATH=<ns>/<release> isolation — i.e. cross-tenant access to the system bucket is by design — but the network path to reach it isn't provisioned.
Impact
Any useSystemBucket backup (and the etcd-adoption snapshot) initiated from a network-isolated child tenant fails at S3 upload. Same-namespace backups (e.g. the root tenant's own etcd/apps, which live alongside the SeaweedFS) work, which hides it. Not etcd-specific.
Repro
On a cluster with an isolated root tenant and a child tenant, from a pod in the child tenant namespace:
nc -zv seaweedfs-s3.tenant-root.svc.cozy.local 8333 # timeout
nc -zv s3.<domain> 443 # open (ingress, reachable via allow-to-ingress)
Suggested fix
The layer that owns the system cozy-backups bucket / tenant networking should provision cross-tenant reachability — not the migration. Two options:
- (A, simplest) Point
backupStorage.endpointat the tenant-reachable S3 ingress the system bucket is already published on (https://s3.<domain>), consistent with how per-tenant backup strategies already reach S3. Downside: hairpin + TLS trust + requires the system bucket be ingress-published. - (B, keeps traffic in-cluster) Keep the internal endpoint but ship a
CiliumNetworkPolicyfrom the tenant chart (packages/apps/tenant/templates/networkpolicy.yaml) — anallow-to-backupsegress to the systemseaweedfs-s3for every tenant, plus the matching ingress allow on the SeaweedFS side in the isolated root tenant.
Found during a 1.5.2 → 1.6.0-rc.1 upgrade / etcd-adoption test on a 3-node cluster.
Activity
fix(migrations): derive the etcd-adoption snapshot target from the pr…
androndo commentedon Aug 3, 2026
Fixed on
main. Closing after verifying against04b742670.Option (A) from the report was implemented:
packages/system/backupstrategy-controller/templates/_helpers.tplnow definesbackupstrategy-controller.endpoint, which for a provisioned bucket reads the COSI system-credentials Secret and returnsprintf "https://%s"of the advertised host — the external S3 ingress with an ACME cert, i.e. thes3.<domain>:443path this issue confirmed is already open to child tenants viaallow-to-ingress. Every default Strategy CR, the Velero BackupStorageLocation and the controller Deployment consume that helper, so no strategy stampsseaweedfs-s3.tenant-root.svc:8333any more.Landed in
e160a0dc8"fix(backups): derive default S3 endpoint from the provisioned bucket" (PR #3270, 2026-07-15) with review follow-ups in4af5a8279. The reporter's own repro path was taught the same derivation separately: migration 50 now reads the projectedendpointand refuses to guess a scheme (PR #3335).Option (B) — an
allow-to-backupsCiliumNetworkPolicy in the tenant chart — was not taken and is no longer needed; there is no such policy onmain.Two residuals, neither of them the bug reported here:
backupStorage.endpoint: http://seaweedfs-s3.tenant-root.svc.cozy.local:8333survives invalues.yamlas the deliberate fallback forprovisionBucket: falseand for offline/pre-reconcile renders, so a fresh install can briefly stamp the unreachable value until Flux re-renders with the Secret present.https://unconditionally is tracked separately in fix(bucket): endpoint helper unconditionally forces https:// — original scheme lost for provisioned buckets #3317.Found during an audit of all open backup issues.