Skip to content

Tenant namespace validation gap: deep nesting can exceed the 63-character RFC 1123 label limit #2374

Closed

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

  1. 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
  2. The second Tenant CR passes ValidateApplicationName (DNS-1035, k8s.io/apimachinery/pkg/util/validation) and validateNameLength (25 < 46) in pkg/registry/apps/application/rest.go. The aggregated API Create call returns 201.

  3. cozystack-api publishes status.namespace as tenant-alphaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-betabetabetabetabetabetab — 68 characters, five over the 63-character RFC 1123 label limit.

  4. The tenant Helm chart then tries to create that Namespace and Kubernetes rejects it. The containing HelmRelease surfaces the rejection as a reconcile failure, but the Tenant CR is already in the cluster with a status.namespace that 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::validateNameLength at rest.go:1054 — validates the single application name against maxHelmReleaseName - len(prefix), currently 53 - 7 = 46 for tenants.
  • pkg/registry/apps/application/rest.go::computeTenantNamespace at rest.go:1298 — computes the namespace but is not called from any validation path at Create time.
  • packages/apps/tenant/templates/namespace.yaml — creates the Namespace resource, 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

kind/bugCategorizes issue or PR as related to a bug

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions