Search nomadLab

Ingress-NGINX Is Archived: Reading the CVE Log Before You Migrate

The repository was archived on 23 March 2026 with a CVE published the same day as its final release. Seven landed in the ten weeks before that, all the same shape, and that shape is exactly what makes the migration hard.

Updated

The kubernetes/ingress-nginx repository is archived. Not deprecated, not in maintenance mode: archived, read-only, on 23 March 2026. The final release, controller-v1.15.1, went out four days earlier. Its own README now describes the project in the past tense and tells you to go pick a Gateway API implementation instead.

The usual framing for this is that you are now exposed to some hypothetical future CVE. That framing understates it, and the fix is to read the actual advisory record rather than reason about risk in the abstract.

Seven CVEs in ten weeks, and the last one shipped on closing day

Searching NVD for ingress-nginx on 22 August 2026 returns twenty advisories. Seven of them were published in 2026, all in a ten-week stretch ending the day the project shut down.

PublishedCVECVSSTrigger
3 Feb 2026CVE-2026-15808.8auth-method annotation
3 Feb 2026CVE-2026-245128.8rules.http.paths.path field
3 Feb 2026CVE-2026-245133.1auth-* annotation protection bypass
3 Feb 2026CVE-2026-245146.5validating admission controller
6 Feb 2026CVE-2025-155668.8auth-proxy-set-headers annotation
9 Mar 2026CVE-2026-32888.8rewrite-target annotation
19 Mar 2026CVE-2026-43428.8a combination of annotations

Five of the seven share a description almost word for word: an Ingress annotation can be used to inject configuration into nginx, leading to arbitrary code execution in the controller and disclosure of Secrets, with the parenthetical that “in the default installation, the controller can access all Secrets cluster-wide.”

That is not seven unrelated bugs. That is one design property, found seven times. The controller turns user-supplied annotations into nginx configuration, and every place that translation is not perfectly escaped is a path from “can create an Ingress” to “can read every Secret in the cluster.” Which is why the README also carries a line that predates the retirement: “Do not use in multi-tenant Kubernetes production installations. This project assumes that users that can create Ingress objects are administrators of the cluster.”

CVE-2026-4342 was published on 19 March 2026, the same day as the last release. It is described as “a combination of Ingress annotations,” meaning they were still finding new arrangements of the same flaw on the last day anyone was looking.

CVEs published against ingress-nginx by month in 2026. Five in February, two in March, none after the repository was archived on 23 March 2026, because no maintainer is triaging reports. ingress-nginx CVEs published per month, 2026 5 archived 23 Mar 2026 2 nothing published since, and nobody triaging reports Jan Feb Mar Apr May Jun Jul Aug
Counts from an NVD keyword search on 22 August 2026. An empty column after March is an absence of reporting, not an absence of bugs.

Check which nginx controller you are actually running

Two projects carry “NGINX Ingress” in the name and only one of them is dead.

The archived one is kubernetes/ingress-nginx, the SIG Network community controller, the thing helm install ingress-nginx ingress-nginx/ingress-nginx gives you. Its annotations start with nginx.ingress.kubernetes.io/.

The live one is F5’s, now at nginx/kubernetes-ingress after the org rename from nginxinc, currently at v5.5.4 released 16 July 2026. Its annotations start with nginx.org/.

kubectl get ingress -A -o yaml | grep -oE 'nginx\.(ingress\.kubernetes\.io|org)/[a-z-]+' | sort | uniq -c | sort -rn

Run it with the counts. You want to know which controller you are on and, more importantly, which annotations you actually use, because that list is your migration scope.

The replacements, with their current state

Checked against the GitHub API on 22 August 2026, because “actively maintained” is a marketing phrase and a release date is a fact.

ProjectLatestReleasedLicenseConfig model
Traefikv3.7.1119 Aug 2026MITIngress + Gateway API
Envoy Gatewayv1.9.015 Aug 2026Apache-2.0Gateway API only
Kong Ingress Controllerv3.5.137 Aug 2026Apache-2.0Ingress + Gateway API
HAProxy Ingressv0.16.14 May 2026Apache-2.0Ingress
F5 NGINX Ingress Controllerv5.5.416 Jul 2026Apache-2.0Ingress + Gateway API

Two entries in that table need a footnote.

Envoy Gateway is Gateway API only. Its README says Gateway API resources are what provision and configure the proxies, and there is no Ingress path. Comparison posts that list it as supporting both are wrong, and the difference is not academic: picking it means doing the controller migration and the resource-model migration in the same change window.

Kong’s controller is healthy; Kong’s free data plane is not. KIC ships regularly, but the open source Kong/kong gateway underneath is frozen at 3.9.3 while Enterprise is on 3.15, and from 3.10 the Enterprise image no longer has a free mode. I went through that in more detail in the API gateway comparison. Choosing Kong here is choosing either a version ceiling or a vendor relationship, and it is better to know that on day one.

HAProxy Ingress is the slowest-moving of the five at roughly three and a half months since its last tag, though the repository is still receiving commits. Traefik is the closest thing to a drop-in for a team that wants to keep writing Ingress resources.

Gateway API: the project says yes, the calendar says one thing at a time

The archived README’s own advice is to pick a Gateway API implementation. Gateway API is at v1.6.1 as of 16 July 2026 and it is unambiguously where this is going. The role separation between Gateway, HTTPRoute, and ReferenceGrant is the fix for the annotation soup that produced the CVE table above.

The tension is that a forced migration is a bad time to learn a new resource model. My read: if you have an existing cluster with meaningful Ingress usage, move to a controller that speaks Ingress first, get the traffic moved, then convert resources on your own schedule. If the cluster is new, or your Ingress usage is thin enough to rewrite in an afternoon, go straight to Gateway API and use Envoy Gateway.

The annotations that do not port

The migration is not the controller swap. It is the annotation translation, and the CVE table tells you which annotations to look at hardest, because the ones with injection bugs are the ones doing string substitution into config.

rewrite-target is the worst offender for a reason: capture-group semantics differ between controllers. Traefik expresses it as a Middleware, Envoy Gateway as a URLRewrite filter, Kong as a request-transformer plugin. Trailing-slash behavior differs among all three. Test each rewrite path with real URLs rather than trusting a translation script.

configuration-snippet and server-snippet are raw nginx config embedded in an annotation, and nothing ports them automatically. If any of yours contain Lua, there is no equivalent on the other side and you are rewriting the logic.

The auth-url family, which accounts for three of the seven 2026 CVEs, maps to external authorization in Envoy Gateway and ForwardAuth middleware in Traefik. The semantics of which headers are forwarded back to the upstream differ, and that difference is a silent auth bypass if you get it wrong.

Sticky sessions, backend mTLS, and the validating admission webhook all behave differently on every controller. Budget for the Prometheus metric names too: your existing Grafana dashboards key off ingress-nginx labels and none of them survive the swap.

A rollout that does not require a maintenance window

Install the new controller with its own IngressClass and leave the old one running. Nothing routes to it yet.

Convert one low-risk service. Same Ingress, new class, new annotations. Send synthetic traffic and diff response headers, status codes, and p99 against the existing path. Header differences are where the surprises live.

Shift traffic at the load balancer or DNS layer rather than by reassigning IngressClass, so rollback is a weight change instead of a redeploy. Move in steps over days and watch error budgets rather than dashboards you built that morning.

Wait 48 hours at full traffic before deleting anything. The usual way this goes wrong is uninstalling the old controller and finding an internal service that was still pointed at the old class.

Then audit the snippets and rewrites by hand. Automated translators handle the common case and fail quietly on the rest, which is the same failure mode that put seven CVEs in the table above.

If you want one thing to do this week, run the annotation count command. Whatever number comes back is the real size of this project, and it is usually either much smaller than the platform team feared or concentrated in three services nobody wants to touch.

Keep reading