Skip to content

fix: make RBAC and supporting templates nil-safe for optional provider and hub blocks - #1906

Open
victorlane wants to merge 10 commits into
traefik:masterfrom
victorlane:fix-rbac-nil-providers
Open

fix: make RBAC and supporting templates nil-safe for optional provider and hub blocks#1906
victorlane wants to merge 10 commits into
traefik:masterfrom
victorlane:fix-rbac-nil-providers

Conversation

@victorlane

@victorlane victorlane commented Jun 25, 2026

Copy link
Copy Markdown

What does this PR do?

Several templates dereference .Values.providers.* and .Values.hub.* without guarding the parent map, so rendering panics with nil pointer evaluating interface {}.X whenever a user-supplied values file omits or nulls an optional block. The pre-existing ((.x).y) "nil-safe" form in rolebinding.yaml was also broken in a different way: it returned nil (not list) and concat panicked on the nil argument.

Motivation

Running into this issue while trying to deploy the helm chart

More

  • Yes, I updated the tests accordingly
  • Yes, I updated the schema accordingly
  • Yes, I ran make test and all the tests passed

`concat` panics on nil arguments, so the previous `((.x).y)` form
crashed when a user-supplied values file omitted or nulled a provider
block. Defaulting to an empty list keeps the "empty = watch all"
semantics intact and renders cleanly when providers.kubernetesIngress,
providers.kubernetesCRD, providers.kubernetesGateway, providers.knative,
or hub.namespaces are absent.
Wrap provider field reads in `(map).field` so the requirements
preflight does not panic when a values file omits or nulls
providers.kubernetesGateway, providers.kubernetesIngress,
providers.kubernetesCRD, or providers.kubernetesIngressNGINX.
Wrap the kubernetesGateway lookup so NOTES.txt does not panic
when the kubernetesGateway block is absent from values.
ClusterRole rules and the shared image/version helpers crashed when
providers.kubernetesIngress, providers.kubernetesCRD, or the hub block
were absent. Wrap parent maps in `(map).field` so reads short-circuit
to nil instead of dereferencing nil.
…lates

The hub block is optional but several templates (clusterrole.yaml,
role.yaml, requirements.yaml, hub-license.yaml, hub-apiportal.yaml,
hub-admission-controller.yaml) dereferenced .Values.hub.* directly,
panicking when a user supplied values that omitted or nulled the
whole hub block. Wrap the entry-point hub reads in `(.Values.hub).field`
so they short-circuit to nil and the gated blocks render as no-ops.
…ates

The shared pod template, deployment, daemonset, and file-provider
ConfigMap each crashed when providers.file, providers.kubernetesIngress,
providers.kubernetesCRD, or the hub block were nulled. Wrap each
top-of-block guard so the gated regions render as no-ops instead of
dereferencing nil.
…ranches

NOTES.txt label-selector and hub warnings, plus the per-provider
rule branches in Role, dereferenced provider/hub fields without
guarding the parent map. Apply the same `(map).field` pattern so
omitted blocks render cleanly.
Extend the existing nil-namespaces test with a case where the
kubernetesGateway, knative, file, and kubernetesIngressNGINX
provider blocks are absent entirely, locking in the nil-safe
template behavior.
@victorlane
victorlane force-pushed the fix-rbac-nil-providers branch from ff3cc28 to 4092a56 Compare June 25, 2026 08:26
@darkweaver87

darkweaver87 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Hello @victorlane 👋,

Thanks for this contribution 🏅
I'll have a deep look into it 👀
In the meantime, would you mind sharing the values.yaml which led you to open this PR? I'd like to understand the exact case — these blocks have full defaults in the chart, so the panic only shows up when hub: or providers: (or a child like providers.file:) is explicitly set to null rather than merged into.

Thanks :-)

@victorlane

victorlane commented Jun 25, 2026

Copy link
Copy Markdown
Author

Hello @victorlane 👋,

Thanks for this contribution 🏅 I'll have a deep look into it 👀 In the meantime, would you mind sharing the values.yaml which led you to open this PR? I'd like to understand the exact case — these blocks have full defaults in the chart, so the panic only shows up when hub: or providers: (or a child like providers.file:) is explicitly set to null rather than merged into.

Thanks :-)

Hi Remi,

Below is a minimal values yaml that reproduces the issues. It only happens when an optional block is explicitly nulled rather than left out fully.

rbac:
  namespaced: true

providers:
  kubernetesIngress:
    enabled: true
  kubernetesCRD:
    enabled: true
  kubernetesGateway:
    enabled: false
  knative: ~
  kubernetesIngressNGINX: ~

@victorlane

Copy link
Copy Markdown
Author

@darkweaver87 had time to look after this yet? We are experiencing issues with this bug in (prod) installations.

@darkweaver87

Copy link
Copy Markdown
Contributor

Hi @victorlane, and sorry for the late follow-up — I know you're hitting this in prod, thanks for your patience and for the minimal reproducer, it's very helpful. 🙏

Looking at it, everything in your values comes down to these two lines:

  knative: ~
  kubernetesIngressNGINX: ~

As you noted yourself, the panic only happens when an optional block is explicitly nulled rather than left out. Both of these providers are opt-in and already enabled: false by default, so if you simply remove those two lines, the chart's defaults apply, nothing is nil, and it renders fine — no chart change needed on your side. Out of curiosity: is there a reason you need to null them explicitly instead of just not specifying them? That would help me understand whether there's a real use case to support here.

On the PR itself, my hesitation is that guarding parents path-by-path makes the chart only look nil-safe while leaving gaps:

  • rbac: ~ still panics — every template uses .Values.rbac.enabled / .Values.rbac.namespaced unguarded.
  • Even within the PR's scope it's incomplete: in requirements.yaml, ((.Values.providers.kubernetesIngressNGINX).modsec).enabled is now guarded, but the sibling (not $.Values.hub.token) on the same line isn't — and since Go template and evaluates all its arguments, nulling hub still panics there.
  • The guarded blocks here (knative, kubernetesIngressNGINX) are opt-in and disabled by default, so their map always exists from the defaults; nulling a provider you aren't using is what triggers this, which makes the guards protect a scenario that doesn't really need to occur.

So there's a design question underneath: do we want to officially support "nulling any optional block"? If yes, I'd rather we do it once and consistently (e.g. normalizing optional blocks in a helper, or leaning on schema defaults) than scatter parentheses that every future template author has to remember. If not, dropping the explicit ~ on your side resolves it cleanly.

The default list ... fixes in the RBAC templates and the added tests are genuinely nice, though — that part addresses a real concat-on-nil case.

Let me know your thoughts, and thanks again for pushing on this! 🙌

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants