Skip to content

Namespace validating webhook panics ("assignment to entry in nil map") when adopting an annotation-less namespace into a tenant #1981

Description

@sarg3nt

Bug description

When a Capsule administrator (or any admin-classified identity) adds the
capsule.clastix.io/tenant label to an existing namespace that has no
annotations
, the namespaces.validating.projectcapsule.dev webhook panics
and the request is denied:

admission webhook "namespaces.validating.projectcapsule.dev" denied the request:
panic: assignment to entry in nil map [recovered]

Root cause

internal/webhook/namespace/validation/user_metadata.go, userMetadataHandler.OnUpdate:

labels      := maps.Clone(oldNs.GetLabels())
annotations := maps.Clone(oldNs.GetAnnotations())   // nil when the ns has no annotations

for key, value := range newNs.GetAnnotations() {
    if _, ok := annotations[key]; !ok {
        annotations[key] = value   // user_metadata.go:107 — write to a nil map → panic
    }
}

maps.Clone(nil) returns nil. The mutating webhook injects the tenant's
managed annotations (allowed storage classes, registries, etc.) into newNs,
so newNs.GetAnnotations() is non-empty while the clone of the (absent)
oldNs annotations is nilannotations[key] = value panics.

This inline diff block also appears to be dead codelabels/annotations
are immediately reassigned by userMetadataForValidation(...) a few lines later
when tnt.Spec.NamespaceOptions != nil. The block looks already removed on
main, but it is still present in the latest release.

How to reproduce

  1. Install Capsule (chart v0.13.5 or v0.13.6) with a tenant that has
    NamespaceOptions (e.g. allowed storage classes), and an administrator in
    CapsuleConfiguration.spec.administrators.
  2. Create a namespace with no annotations:
    kubectl create ns demo
  3. As the administrator, adopt it:
    kubectl label ns demo capsule.clastix.io/tenant=<tenant>
  4. Observe the panic above.

Workaround: seed any annotation first
(kubectl annotate ns demo x=y) so the annotations map is non-nil, then label.

Logs

controller-manager panic trace
{"level":"error","logger":"admission","msg":"Observed a panic","object":{"name":"sarge-test-1"},"user":"system:admin","panic":"assignment to entry in nil map",
"stacktrace":"... github.com/projectcapsule/capsule/internal/webhook/namespace/validation.(*userMetadataHandler).OnUpdate.func1 ... user_metadata.go:107 ...
github.com/projectcapsule/capsule/internal/webhook/namespace/validation.(*handler).OnUpdate.func1 ... handler.go:200 ..."}

Expected behavior

Adopting an annotation-less namespace into a tenant should succeed (or be
cleanly denied) without panicking the webhook.

Versions

  • Capsule chart: v0.13.5 (also reproduces on v0.13.6)
  • Capsule image: ghcr.io/projectcapsule/capsule:0.13.5
  • Kubernetes: RKE2 (server v1.x)

Metadata

Metadata

Assignees

No one assigned

    Labels

    blocked-needs-validationIssue need triage and validationbugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions