fix: produce valid listener names for wildcard and unusual DNS names
A DNS name like *.home.hrajfrisbee.cz put "*" into the listener name; the API server rejected the patch, which blocked every certificate on that Gateway. - lowercase the DNS part, spell "*" as "wildcard", map other characters outside [a-z0-9] to "-" - names over 253 chars are cut and get a hash suffix instead of a blind cut - names that were valid before are unchanged Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+11
-8
@@ -325,19 +325,22 @@ The comparison is done via a name-keyed map (`listenerMap`, `patch.go:118`), so
|
||||
|
||||
## 7. Listener Naming and Sanitization
|
||||
|
||||
`listenerNameFor` (`patch.go:143`):
|
||||
`listenerNameFor` and `sanitizeDNSName` (`patch.go`):
|
||||
|
||||
```
|
||||
input: prefix="auto-" certName="my-app-cert" dnsName="app.example.com"
|
||||
|
||||
step 1: replace every "." with "-"
|
||||
step 1: sanitize the DNS name
|
||||
lowercase; "*" ──► "wildcard"; every other character outside
|
||||
[a-z0-9] ──► "-"; trim leading and trailing "-"
|
||||
"app.example.com" ──► "app-example-com"
|
||||
|
||||
step 2: concatenate
|
||||
"auto-" + "my-app-cert" + "-" + "app-example-com"
|
||||
──► "auto-my-app-cert-app-example-com"
|
||||
|
||||
step 3: if len > 253 bytes: name = name[:253] (hard byte slice)
|
||||
step 3: if len > 253: cut to 244, trim trailing "-" and ".", append
|
||||
"-" + first 8 hex chars of sha256(full name)
|
||||
```
|
||||
|
||||
Example outputs for a cert with two DNS names:
|
||||
@@ -345,9 +348,9 @@ Example outputs for a cert with two DNS names:
|
||||
| `dnsName` | listener `Name` |
|
||||
|-----------|-----------------|
|
||||
| `app.example.com` | `auto-my-app-cert-app-example-com` |
|
||||
| `*.example.com` | `auto-my-app-cert-*-example-com` |
|
||||
| `*.example.com` | `auto-my-app-cert-wildcard-example-com` |
|
||||
|
||||
The second entry is a risk: `*` is not valid in a Gateway API `SectionName` (which must match RFC 1123). The API server will reject the patch and the reconciler will requeue forever (see §10, item 7).
|
||||
The listener `Hostname` keeps the original DNS name, wildcard included.
|
||||
|
||||
The sanitization is also lossy: `a.b.com` and `a-b-com` produce the same result. If a certificate lists both, two listeners with the same name are appended; the API server rejects the patch (listener names must be unique), so no listener on that Gateway is updated.
|
||||
|
||||
@@ -490,9 +493,9 @@ Branch (F) of Reconcile treats all Gateway `Get` errors the same (including `Not
|
||||
`ctrl.NewManager` at `main.go:50` does not set `LeaderElection: true`. The `deploy/deployment_gateway-cert-operator.yaml` runs `replicas: 1` which makes this safe today. Scaling to multiple replicas without enabling leader election would result in concurrent Gateway patches.
|
||||
|
||||
**7. `listenerNameFor` edge cases**
|
||||
- **Wildcard hostnames**: `*.example.com` produces `SectionName` `auto-…-*-example-com`. The `*` character is not valid in an RFC 1123 label; the Gateway API server will reject the Patch, causing an error-retry loop.
|
||||
- **Collisions after sanitization**: `a.b.com` and `a-b-com` produce identical sanitized names. The same happens across certificates (cert `a` with `b-c.com` and cert `a-b` with `c.com`) and across namespaces, since the name omits the cert namespace. Both listeners are appended and the API server rejects the patch for the whole Gateway.
|
||||
- **Truncation at 253 bytes**: `patch.go:147` slices on bytes, not runes (safe for ASCII DNS names). Two DNS names sharing the first 253 characters after sanitization will map to the same listener name.
|
||||
- **Wildcard hostnames** (fixed): `*` used to end up in the listener name and the API server rejected the patch. It is now spelled out as `wildcard`.
|
||||
- **Collisions after sanitization**: `a.b.com` and `a-b-com` produce identical sanitized names, as do `*.x.com` and `wildcard.x.com`. The same happens across certificates (cert `a` with `b-c.com` and cert `a-b` with `c.com`) and across namespaces, since the name omits the cert namespace. Both listeners are appended and the API server rejects the patch for the whole Gateway.
|
||||
- **Truncation at 253 bytes** (fixed): long names are cut and get a hash of the full name appended, so they stay valid and distinct.
|
||||
|
||||
**Dead symbols in `patch.go`**
|
||||
`gatewayGroup` and `gatewayKind` are declared at `patch.go:16-17` but never referenced. They can be removed without effect.
|
||||
|
||||
@@ -3,7 +3,32 @@
|
||||
Plan: `docs/plans/2026-10-08-2046-listener-name-sanitize.md`
|
||||
|
||||
- [x] Step 0 — Save and commit the plan
|
||||
- [ ] Step 1 — Regression tests first
|
||||
- [ ] Step 2 — Fix
|
||||
- [ ] Step 3 — Docs
|
||||
- [x] Step 1 — Regression tests first
|
||||
- [x] Step 2 — Fix
|
||||
- [x] Step 3 — Docs
|
||||
- [ ] Step 4 — Ship
|
||||
|
||||
## Steps 1–3 — Tests, fix, docs
|
||||
|
||||
Tests were written first and failed on the old code (wildcard, uppercase, `_`,
|
||||
trailing dot, long names), then passed after the change to `listenerNameFor`.
|
||||
|
||||
```
|
||||
$ go test -race -run 'TestListenerNameFor|TestBuildListeners' -v ./internal/gateway/
|
||||
--- PASS: TestListenerNameFor_validSectionName/wildcard_label_is_spelled_out
|
||||
--- PASS: TestListenerNameFor_longNames
|
||||
--- PASS: TestBuildListenersFromCertificates_wildcard
|
||||
$ make check
|
||||
ok .../internal/gateway coverage: 98.6% of statements
|
||||
```
|
||||
|
||||
Deviations from the plan:
|
||||
|
||||
- A trailing `-` or `.` is trimmed from the whole name, not only from the DNS part,
|
||||
so a DNS name that sanitizes to nothing cannot leave the name ending in `-`.
|
||||
- One extra case: a trailing dot (`app.example.com.`) is dropped.
|
||||
- Steps 1–3 are one commit, since the tests do not pass without the fix.
|
||||
|
||||
Worth noting: names that were valid before are unchanged, so existing listeners are
|
||||
not renamed. Not verified against a cluster; the production case is covered by the
|
||||
test using the exact cert and DNS name from the error log.
|
||||
|
||||
Reference in New Issue
Block a user