From 6698eab1d8ea7e7bf89050127f965e44026c0636 Mon Sep 17 00:00:00 2001 From: Jan Novak Date: Thu, 8 Oct 2026 20:46:47 +0200 Subject: [PATCH] docs: add plan for sanitizing listener names Co-Authored-By: Claude Opus 5.5 --- .../2026-10-08-2046-listener-name-sanitize.md | 9 ++ .../2026-10-08-2046-listener-name-sanitize.md | 111 ++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 docs/plans-executions/2026-10-08-2046-listener-name-sanitize.md create mode 100644 docs/plans/2026-10-08-2046-listener-name-sanitize.md diff --git a/docs/plans-executions/2026-10-08-2046-listener-name-sanitize.md b/docs/plans-executions/2026-10-08-2046-listener-name-sanitize.md new file mode 100644 index 0000000..d0107a8 --- /dev/null +++ b/docs/plans-executions/2026-10-08-2046-listener-name-sanitize.md @@ -0,0 +1,9 @@ +# Execution: Sanitize listener names derived from certificate DNS names + +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 +- [ ] Step 4 — Ship diff --git a/docs/plans/2026-10-08-2046-listener-name-sanitize.md b/docs/plans/2026-10-08-2046-listener-name-sanitize.md new file mode 100644 index 0000000..7c2ae75 --- /dev/null +++ b/docs/plans/2026-10-08-2046-listener-name-sanitize.md @@ -0,0 +1,111 @@ +# Plan: Sanitize listener names derived from certificate DNS names + +**Created:** 2026-10-08 20:46 CEST + +## Context + +The operator in the home-lab cluster fails every reconcile of +`kube-system/wildcard-home-hrajfrisbee`: + +``` +spec.listeners[4].name: Invalid value: "auto-wildcard-home-hrajfrisbee-*-home-hrajfrisbee-cz": +should match '^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$' +``` + +`listenerNameFor` (`internal/gateway/patch.go:137`) only replaces dots in the DNS +name, so `*.home.hrajfrisbee.cz` puts a `*` into the listener name. Gateway API +listener names are `SectionName`: lowercase alphanumerics, `-` and `.`, each +dot-separated label starting and ending alphanumeric, max 253 chars. Because the +patch replaces the whole listeners array, this one name blocks every other +certificate on `cilium-gateway` too. + +Outcome: any DNS name cert-manager accepts yields a valid listener name, and names +that are valid today do not change (no listener churn on existing Gateways). + +Lands on `feat/house-conventions` (decided), in the same MR as the conventions port. + +## Design + +Only the DNS-name part is rewritten; the listener `Hostname` keeps the original +value (`*.home.hrajfrisbee.cz` is a valid Gateway hostname). + +`listenerNameFor(prefix, certName, dnsName)` in `internal/gateway/patch.go`: + +1. Sanitize the DNS name with a new unexported `sanitizeDNSName`: + - lowercase; + - a leading `*` label becomes `wildcard` (`*.example.com` → `wildcard-example-com`); + - `.` becomes `-` (as today); + - any other character outside `[a-z0-9-]` becomes `-`; + - trim leading and trailing `-`. +2. Name is `prefix + certName + "-" + sanitized`, as today. +3. If longer than 253: cut to 244, trim trailing `-` and `.`, append `-` plus the + first 8 hex chars of the SHA-256 of the untruncated name. This replaces the + current blind `name[:253]`, which can end in `-` (invalid) and makes long names + collide. + +Result for the failing cert: +`auto-wildcard-home-hrajfrisbee-wildcard-home-hrajfrisbee-cz`. + +Not changed here (stay on the backlog): collisions between distinct inputs that +sanitize to the same name (e.g. `*.x.com` and `wildcard.x.com` in one cert, or the +same cert name in two namespaces), uppercase in the listener `Hostname`, and the +other items in `docs/reviews/`. + +## Steps + +### Step 0 — Save and commit the plan + +Copy to `docs/plans/2026-10-08-2046-listener-name-sanitize.md`, create the paired +execution log, commit alone. + +### Step 1 — Regression tests first + +In `internal/gateway/patch_test.go`, extend `TestListenerNameFor_sanitizesDots` +(rename to `TestListenerNameFor_validSectionName`) with cases, each asserting the +exact name and that it matches the `SectionName` pattern and length: + +- `*.home.hrajfrisbee.cz` with cert `wildcard-home-hrajfrisbee` (the production case); +- uppercase DNS name; +- a character outside the allowed set; +- a name over 253 chars: result is ≤ 253, valid, ends with the hash suffix, and two + long inputs sharing a 253-char prefix produce different names; +- existing plain names unchanged (already in the table). + +Add a wildcard case to `TestBuildListenersFromCertificates_names` and assert in +`_listenerFields` style that the hostname stays `*.example.com`. Run and confirm the +new cases fail before the fix. + +### Step 2 — Fix + +Implement the design in `internal/gateway/patch.go` (stdlib only: `strings`, +`crypto/sha256`, `encoding/hex`). Update the function comment. + +### Step 3 — Docs + +- `CLAUDE.md`: Gotchas — drop the wildcard half of "a single bad listener blocks the + whole Gateway" (collisions remain); Architecture — describe the name as sanitized. +- `docs/flows.md` §7 and §10 item 7: wildcard and truncation rows updated. +- `docs/reviews/…-house-conventions.md` is not touched (it predates this). + +### Step 4 — Ship + +`make check` and `go test -race ./...`, then `/ship` (pushes +`feat/house-conventions`) and open the MR with `tea pr create`. Tick Step 7 of the +conventions execution log in the same commit. + +## Verification + +```bash +go test -race -run 'TestListenerNameFor|TestBuildListeners' -v ./internal/gateway/ +make check +``` + +Live, after you merge the MR: + +1. `/release` → tag `v0.1.0`; CI builds and moves `:latest`. +2. `kubectl -n gateway-cert-operator-system rollout restart deploy/gateway-cert-operator` + (the Deployment pins `:latest`, so it does not roll on its own). +3. The "Reconciler error" for `wildcard-home-hrajfrisbee` stops, and + `kubectl -n kube-system get gateway cilium-gateway -o jsonpath='{.spec.listeners[*].name}'` + lists `auto-wildcard-home-hrajfrisbee-wildcard-home-hrajfrisbee-cz`. +4. Then the CHANGELOG entries for both the conventions port and this fix.