feat(gateway): advertise HTTP/2 via ALPN on Gateway API listeners#3398
Conversation
Browsers negotiate HTTP/2 exclusively through TLS ALPN "h2". Cilium's Gateway API implementation ships with enable-gateway-api-alpn=false, so every Gateway listener advertised no ALPN protocols and every browser silently fell back to HTTP/1.1 — a regression against the ingress-nginx path, which advertised h2 out of the box. Enable ALPN (h2, then http/1.1) by default on the client-facing hop: - packages/system/cilium: default gatewayAPI.enableAlpn=true. - platform chart: new gateway.http2 value (default true), threaded as inline component values into the cozystack.networking Package for the isp-full and isp-full-generic bundles, so an explicit false overrides the system/cilium valuesFiles default either way. - apps/kubernetes: tenant-cluster cilium gets enableAlpn=true whenever the gatewayAPI addon is enabled; opt out per cluster via addons.cilium.valuesOverride. The backend hop is unchanged: gateway-to-upstream stays HTTP/1.1 unless a Service opts in per GEP-1911 via appProtocol kubernetes.io/h2c (that support is switched on together with ALPN; no cozystack-shipped Service declares such a value today). Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
📝 WalkthroughWalkthroughThe change adds a ChangesGateway HTTP/2 ALPN
Estimated code review effort: 3 (Moderate) | ~20 minutes Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request addresses a regression where the migration to Gateway API caused browser traffic to silently downgrade to HTTP/1.1 due to the lack of ALPN advertisement. By enabling ALPN by default, the platform restores native HTTP/2 support for the client-to-gateway hop, improving performance by eliminating head-of-line blocking. The change is designed to be safe for non-HTTP/2 clients and provides clear opt-out mechanisms for operators. Highlights
New Features🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request enables HTTP/2 (ALPN "h2") by default on TLS-terminating Gateway API listeners served by the Cilium dataplane, preventing silent downgrades to HTTP/1.1. The changes span the platform, kubernetes, and cilium packages, along with corresponding unit tests. Feedback suggests using the Helm "dig" helper when referencing ".Values.gateway.http2" to prevent potential nil pointer evaluation errors if the gateway configuration is omitted. Additionally, the PR scope should be updated to a valid scope (such as "platform" or "cilium") to comply with the repository's Conventional Commits style guide.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| {{- $ciliumValues := dict "cilium" (dict | ||
| "k8sServiceHost" "localhost" | ||
| "k8sServicePort" "7445" | ||
| "cgroup" (dict "autoMount" (dict "enabled" false))) -}} | ||
| "cgroup" (dict "autoMount" (dict "enabled" false)) | ||
| "gatewayAPI" (dict "enableAlpn" .Values.gateway.http2)) -}} |
There was a problem hiding this comment.
To prevent potential nil pointer evaluation errors if .Values.gateway is completely omitted or set to null in custom values, we should use the dig helper to safely retrieve http2 with a default value of true.
{{- $ciliumValues := dict "cilium" (dict\n "k8sServiceHost" "localhost"\n "k8sServicePort" "7445"\n "cgroup" (dict "autoMount" (dict "enabled" false))\n "gatewayAPI" (dict "enableAlpn" (dig "gateway" "http2" true .Values))) -}}| {{- $ciliumValues := dict "cilium" (dict | ||
| "k8sServiceHost" $apiHost | ||
| "k8sServicePort" $apiPort | ||
| "cgroup" (dict "autoMount" (dict "enabled" true))) -}} | ||
| "cgroup" (dict "autoMount" (dict "enabled" true)) | ||
| "gatewayAPI" (dict "enableAlpn" .Values.gateway.http2)) -}} |
There was a problem hiding this comment.
Similarly, use the dig helper here to safely retrieve gateway.http2 with a default of true to avoid nil pointer dereferences if gateway is not defined in custom values.
{{- $ciliumValues := dict "cilium" (dict\n "k8sServiceHost" $apiHost\n "k8sServicePort" $apiPort\n "cgroup" (dict "autoMount" (dict "enabled" true))\n "gatewayAPI" (dict "enableAlpn" (dig "gateway" "http2" true .Values))) -}}| # isp-hosted the hosting-side CNI owns this setting. Flipping it re-rolls | ||
| # the cilium DaemonSet on the next platform upgrade (rollOutCiliumPods), | ||
| # the same disruption profile as any cilium config change. | ||
| http2: true |
There was a problem hiding this comment.
The PR title and commit message use the scope gateway (e.g., feat(gateway): ...). However, according to the repository style guide, gateway is not listed as a valid System, Apps, or Meta scope, nor does it match a package directory under packages/ (the package is named gateway-application). Since the changes in this PR span platform, cilium, and kubernetes, please consider using one of the valid scopes such as platform or cilium to adhere to the Conventional Commits requirements.
References
- Each commit must follow Conventional Commits format with valid scopes (System, Apps, Meta, or Package-specific matching a directory under packages/). (link)
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
LGTM — restores the browser HTTP/2 support that was lost when the platform moved from ingress-nginx to Gateway API, fixed at the correct layer (Cilium agent config) with a working opt-out at every management layer and full contract test coverage.
Business context: Cilium ships enable-gateway-api-alpn=false, so every TLS-terminating Gateway listener advertised no ALPN and browsers silently downgraded to HTTP/1.1; this turns ALPN (h2, then http/1.1) on by default and adds a gateway.http2 platform knob plus a per-tenant-cluster opt-out.
I verified the load-bearing claims against the vendored chart, the merge semantics, and the actual rendered output.
Consumption is real end-to-end: cilium.gatewayAPI.enableAlpn maps to enable-gateway-api-alpn in cilium-configmap.yaml (and flips enable-gateway-api-app-protocol on via or enableAppProtocol enableAlpn); rendering packages/system/cilium produces "true" by default and "false" when the flag is threaded false. The shipped Cilium is 1.19.5, which honors this agent flag, so the option is not a dead feature.
The opt-out works on both paths. On the platform path gateway.http2 is threaded as inline Package component values in both true and false states, so an operator's http2: false overrides the chart default. On the tenant path deepCopy .Values.addons.cilium.valuesOverride | mergeOverwrite (fromYaml defaults) resolves (by pipe order) to mergeOverwrite(defaults, valuesOverride), so the operator override wins. Both are confirmed by passing tests that go red if the override were shadowed.
No regression from turning ALPN on: it advertises h2 then http/1.1, so HTTP/1.1-only clients keep negotiating unchanged. The one real side effect — enabling enable-gateway-api-app-protocol — is inert here: no cozystack-shipped Gateway/HTTPRoute backend Service declares a protocol-changing appProtocol (h2c/grpc), and the only http/https values in-tree are on an ingress-nginx Ingress backend that Cilium's Gateway path never touches. isp-hosted uses noop networking and is correctly left untouched.
Tests define the full contract and pass: tenant suite covers addon-off, default-on, and explicit opt-out; platform suite covers default-on and http2: false for both isp-full and isp-full-generic plus a guard that the Talos endpoint values survive alongside the new flag. Full suites are green (platform 93/93, kubernetes 184/184), no existing test regressed.
Non-blocking follow-ups
- The user-facing
gateway.http2knob should get a row in the website's platform values table; the PR already acknowledges this and leaves it for a docs sweep. Non-blocking. - CI shows E2E red, but the failure is
flux-plungerfailing its own liveness/readiness probes (connection refused on its health port) during install — an application-startup issue independent of this change: every other release installed, and a fresh install does not re-roll Cilium. Recommend re-running E2E for a green signal before merge. Non-blocking. - Optional: the
system/cilium/values.yamldefault (enableAlpn: true) is currently asserted only indirectly through the platform/tenant suites; a one-line assertion in the cilium chart's own tests would pin it directly. Non-blocking.
Adds a `gateway.http2` row to the `next/` platform-package values table, between `gateway.enabled` and `gateway.attachedNamespaces`, matching the value's position in the platform chart's `values.yaml`. The value ships with cozystack/cozystack#3398, which enables HTTP/2 via TLS ALPN (`h2`, then `http/1.1`) by default on Gateway API listeners served by the bundled Cilium dataplane. The row documents the default (`true`), the frontend-only scope (backends stay HTTP/1.1 unless a Service declares `appProtocol: kubernetes.io/h2c` per GEP-1911), the cluster-wide granularity, the bundles it applies to (`isp-full`, `isp-full-generic`), and the cilium DaemonSet re-roll on flipping it. `next/` only: the feature is unreleased, so no released version directory is touched. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Documented the new `gateway.http2` setting for controlling HTTP/2 advertisement on Gateway API listeners. * Clarified backend-to-Gateway protocol behavior, rollout considerations, and supported Cilium bundle limitations. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Add the gateway.http2 row to the next/ platform-package values table. The value ships with cozystack/cozystack#3398, which turns on HTTP/2 via TLS ALPN (h2, then http/1.1) for Gateway API listeners served by the bundled Cilium dataplane, restoring the browser HTTP/2 support the ingress-nginx path had before the Gateway API migration. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
What this PR does
Browsers negotiate HTTP/2 exclusively through TLS ALPN: if the terminating listener does not advertise
h2, every client silently falls back to HTTP/1.1, with no error surfaced anywhere. Cozystack's Gateway API path is served by Cilium's embedded Envoy, and Cilium ships withenable-gateway-api-alpn=false— so every Gateway listener advertised no ALPN protocols and all browser traffic through a Gateway ran over HTTP/1.1 (observable in browser dev tools ashttp/1.1on every request). The ingress-nginx path this replaces advertisedh2out of the box, so the migration to Gateway API silently downgraded clients: head-of-line blocking and the ~6-connections-per-origin limit are back for anything served through a Gateway.This PR enables ALPN (
h2, thenhttp/1.1) on Gateway API listeners, default-on, with an opt-out at each management layer:packages/system/cilium/values.yamlnow defaultsgatewayAPI.enableAlpn: true, which rendersenable-gateway-api-alpn: "true"into thecilium-configConfigMap.gateway.http2(defaulttrue) next to the existinggateway.enabled/gateway.attachedNamespaces. Theisp-fullandisp-full-genericbundles thread it as inline component values into thecozystack.networkingPackage CR (components.cilium.values.cilium.gatewayAPI.enableAlpn); inline HelmRelease values override the packagevaluesFiles, so an explicitgateway.http2: falseactually reaches the dataplane rather than being shadowed by the new chart default. Onisp-hostedthe hosting-side CNI owns this setting and the flag is a no-op.packages/apps/kubernetes) get the same fix: when thegatewayAPIaddon is enabled, the tenant cilium HelmRelease now setsenableAlpn: true; individual clusters opt out viaaddons.cilium.valuesOverride.cilium.gatewayAPI.enableAlpn=false.Why default-on rather than opt-in
HTTP/2 for browsers was the platform's behavior before the Gateway API migration, so keeping ALPN off would preserve a regression, not a status quo. Defaulting on is safe because ALPN is a negotiation: clients that cannot speak HTTP/2 simply keep negotiating
http/1.1, exactly as before. Operators who need the old wire behavior can setgateway.http2: falseplatform-wide.Frontend vs backend hop
This change affects the client↔gateway hop only. Gateway↔backend connections stay HTTP/1.1 unless a Service explicitly opts in per GEP-1911 by declaring
appProtocol: kubernetes.io/h2con its port. Cilium couples that backend-protocol support to the ALPN flag (enable-gateway-api-app-protocolflips to true together with it), but it only acts on Services that declare a recognizedappProtocolvalue — and no cozystack-shipped Service does today (only plainhttp/httpsvalues exist in-tree, which Cilium ignores for protocol selection). So upstreams see no change from this PR alone; h2c/gRPC backends become possible to configure, not configured.The setting is cluster-wide Cilium agent config, so it covers the root Gateway and all tenant Gateways at once — Cilium offers no per-Gateway granularity here. Flipping it re-rolls the cilium DaemonSet on upgrade (
rollOutCiliumPods: true), the same disruption profile as any other cilium config change.How it was verified
packages/core/platform/tests/bundles_gateway_http2_wiring_test.yamlasserts the Package CR carriesenableAlpntrue by default and false withgateway.http2: falsefor both cilium bundles;packages/apps/kubernetes/tests/cilium_gateway_alpn_test.yamlasserts the tenant HelmRelease default, the addon-off case, and thevaluesOverrideopt-out. Full suites for both charts pass (93 and 184 tests).helm templateofpackages/system/ciliumconfirms the value is consumed, not just rendered: default producesenable-gateway-api-alpn: "true"(andenable-gateway-api-app-protocol: "true") incilium-config; the threadedfalseproduces"false"for both.Screenshots
Not a UI change.
Downstream repositories
The website's hand-written platform values table (
content/en/docs/next/operations/configuration/platform-package.md) needs a row forgateway.http2— filed as cozystack/website#625. No other downstream repository restates anything in this diff (no appvalues.schema.json, ansible-consumed platform key, or hack/ tooling changed).Release note