Repository navigation
gateway: a wildcard hostname claim builds a listener name the CRD rejects, failing the whole reconcile #3787
Description
A tenant route may claim a wildcard hostname, and the listener name the controller builds from it is rejected by the Gateway CRD schema. The reconcile then fails for the whole TenantGateway, not just for that route.
The chain, each link checked against main:
The route policy admits a wildcard from a tenant namespace. "*.foo.example.com".endsWith("." + "foo.example.com") is true, so a route declaring *.<apex> passes admission.
Nothing filters wildcards out of the claim path. Grepping internal/controller/tenantgateway/reconciler.go for wildcard handling returns only the lines that build one for the certificate; none that drop one coming in.
hostnameFirstLabel takes everything before the first dot:
func hostnameFirstLabel(hostname string) string {
hostname = strings.ToLower(hostname)
if i := strings.Index(hostname, "."); i >= 0 {
return hostname[:i]
}
return hostname
}For *.apex that returns *, and perListenerName composes https- + * + - + hex, giving https-*-<hex>.
The listener name pattern in the CRD that actually ships (packages/system/gateway-api-crds/templates/crds-experimental.yaml, same for Gateway v1 and v1beta1) is:
^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
(taken from spec.listeners.name by walking the schema path, not by grepping the file. A grep lands on .group first, and that one carries an extra ^$| alternative because an empty group is legal while an empty listener name is not.)
Checked against it directly:
https-*-06a3e25d REJECT
https-harbor-06a3e25d ACCEPT
So the Gateway write is refused, and because the controller renders all listeners in one object, the failure is not scoped to the offending route: certificate issuance for every other hostname on that gateway stops with it.
Two things are inferred rather than observed, and worth stating as such. The apiserver's refusal follows from the pattern rather than from a live cluster run. And unit tests will not catch it: the fake client used there does not validate SectionName against the schema.
This is not introduced by any open PR. Neither the missing wildcard filter nor hostnameFirstLabel differs between main and the branches currently in flight.
The fix is a filter on the claim path. Whether a wildcard claim should be dropped silently, rejected at admission with a message, or mapped to a stable label is a design call, not an obvious one.
Activity
dosubot commentedon Aug 12, 2026
Good write-up. I can confirm the analysis against the code and fill in one detail you flagged as inferred.
The VAP does admit the wildcard
The
cozystack-route-hostname-policyCEL expression only checks suffix containment: for*.foo.example.comwith an apex offoo.example.com,"*.foo.example.com".endsWith(".foo.example.com")→ true. It never inspects whether the first label is*.collectHostnameClaimspasses wildcards straight throughEvery hostname from
route.Spec.Hostnamesis added to the output map with no filter on the leading label [1]. That map feedsdynHostnames, which is passed to bothreconcileGatewayandreconcilePerListenerCertificates[2]. There is no guard between admission and listener construction.perListenerNameproduces the invalid namehostnameFirstLabel("*.foo.example.com")returns"*"[3], soperListenerNamecomposes"https-*-<hex>"[4]. That string fails the Gateway CRD's listener name regex, and because all listeners are written in a singleGatewayobject, the apiserver rejects the entire write — not just the offending entry.The bug surface is HTTP-01 only
Both DNS-01 and existingSecret modes return
nilfromcollectHostnameClaimsimmediately [5], so wildcards from tenant routes never reachperListenerName.On the design question
Your three options map to concrete insertion points:
hwherestrings.HasPrefix(string(h), "*.")in thecollectHostnameClaimsloop. Simplest patch; the route stays admitted but gets no listener or cert, so it sitsAccepted=Falsewith no message explaining why.h.startsWith("*.")in tenant namespaces. Makes the failure visible to the route author at creation time and preserves the invariant that anything the VAP admits is safe to reconcile.*with a fixed string (e.g."wildcard") insidehostnameFirstLabel. Lowest disruption, but silently swallows the wildcard semantics and could issue an incorrect per-listener certificate.Option 2 looks cleanest: the reconciler's contract becomes "anything in
dynHostnameshas a safe first label", and the test gap you identified (fake client not validatingSectionName) can be closed with a unit test asserting no*appears in any listener name produced byperListenerName.To reply, just mention @dosu.
Share context across your team and agents. Try Dosu.
fix(gateway)!: require tenant routes to declare their hostnames (#3475)
lexfrei commentedon Sep 17, 2026
Fixed on main by #3342 (2bf9ae4), in both places.
judgeHTTPRouteClaimnow refuses an HTTPRoute wildcard that wants the terminate listener under HTTP-01, and the route gets a status message.hostnameFirstLabelrewrites a leading*towildcard, so the listener name ishttps-wildcard-06a3e25dand the CRD pattern accepts it.The test gap is closed as well.
TestRenderGateway_ListenerNamesAreAdmissibleForEveryRouteHostnamechecks rendered names against the pattern of the shipped Gateway CRD, so the fake client is out of the picture. It passes on main at 8a6fe02 together withTestReconcile_WildcardHTTPRouteHostnameIsRefusedUnderHTTP01.Same caveat as in the report: the apiserver refusing
https-*-<hex>was inferred from the pattern, I never saw it on a live cluster.