Repository navigation
Tenant namespace validation gap: deep nesting can exceed the 63-character RFC 1123 label limit #2374
Description
Summary
The aggregated API validates each individual tenant name against maxHelmReleaseName = 53 minus the tenant- prefix (46 characters), but there is no validation of the computed workload namespace that accumulates the full ancestor chain. A user can therefore successfully kubectl apply a Tenant CR whose name is well under the per-name limit, only to have the platform fail later when Kubernetes refuses the generated Namespace because it exceeds the 63-character RFC 1123 label limit.
Reproduction
-
Create a tenant hierarchy with moderately long names at each level. Using only lowercase letters to stay alphanumeric:
# alpha (36 chars) inside tenant-root kubectl apply -f - <<'EOF' apiVersion: apps.cozystack.io/v1alpha1 kind: Tenant metadata: name: alphaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa namespace: tenant-root spec: {} EOF # Tenant accepted, status.namespace = tenant-alphaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa (42 chars) # beta (25 chars) inside the alpha tenant kubectl apply -f - <<'EOF' apiVersion: apps.cozystack.io/v1alpha1 kind: Tenant metadata: name: betabetabetabetabetabetab namespace: tenant-alphaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa spec: {} EOF
-
The second
TenantCR passesValidateApplicationName(DNS-1035,k8s.io/apimachinery/pkg/util/validation) andvalidateNameLength(25 < 46) inpkg/registry/apps/application/rest.go. The aggregated APICreatecall returns 201. -
cozystack-apipublishesstatus.namespaceastenant-alphaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-betabetabetabetabetabetab— 68 characters, five over the 63-character RFC 1123 label limit. -
The tenant Helm chart then tries to create that
Namespaceand Kubernetes rejects it. The containingHelmReleasesurfaces the rejection as a reconcile failure, but theTenantCR is already in the cluster with astatus.namespacethat can never be created.
Expected behavior
The aggregated API should validate the full computed namespace at Create time and refuse the request with a clear 422 error explaining which ancestor chain would produce an over-long namespace. Users should see the failure at kubectl apply time, not as an opaque HelmRelease reconcile error minutes later.
Relevant code
pkg/registry/apps/application/rest.go::validateNameLengthatrest.go:1054— validates the single application name againstmaxHelmReleaseName - len(prefix), currently53 - 7 = 46for tenants.pkg/registry/apps/application/rest.go::computeTenantNamespaceatrest.go:1298— computes the namespace but is not called from any validation path atCreatetime.packages/apps/tenant/templates/namespace.yaml— creates theNamespaceresource, surfacing the rejection as a Helm apply error.
Suggested fix
In validateNameLength (or a new validateTenantNamespace helper) for kindName == "Tenant", additionally call computeTenantNamespace(app.Namespace, app.Name) and reject the request if the result exceeds 63 characters. The check is essentially free (no API calls) because app.Namespace is already available on the incoming request.
Activity