kubepath
The request path hop 4 of 10 ordinary Kubernetes

Matching the request

Traefik v3 routers, both providers on, and one setting that is off by default for good reason.

TLS is terminated; Traefik now has a decrypted request and needs to decide where it goes. Two providers are enabled: kubernetesCRD for the platform’s own IngressRoute objects, and kubernetesIngress for plain Ingress, which is what cert-manager’s solver creates and what tenants are given.

Traefik v3 changed the matcher language, and v2 rules do not quietly keep working. Matchers compose with && and ||, and Path is exact while PathPrefix is not — a v2 rule written as Path(`/api`) that used to prefix-match now matches one URL and 404s the rest of the tree. Priority is explicit and higher wins; when two routers could match, do not rely on rule length.

allowExternalNameServices: true. This is set on both providers, and it is off by default in v3 deliberately. An ExternalName Service is a DNS alias, not an endpoint: it tells Traefik to send the request to an arbitrary hostname. So allowing it converts "can create a Service in a watched namespace" into "can make the ingress controller issue requests to a host of my choosing" — a server-side request forgery primitive with the cluster’s network position behind it.

It is on here because the edge/workload split needs it: the backend for some routes is a different cluster, referenced by name. That is a real requirement, so the honest framing is not "this setting is unsafe" but "this setting moves a trust boundary onto Service creation." Which means Service creation in the watched namespaces has to be treated as a privileged operation from here on — by RBAC, by namespace scoping on the providers, or both. Turning the setting on without moving that boundary is the actual mistake.

a route, in v3 matcher syntax
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: tenant-acme
spec:
  entryPoints: [websecure]
  routes:
    - match: Host(`acme.example.com`) && PathPrefix(`/`)
      kind: Rule
      priority: 10                  # explicit; higher wins
      services:
        - name: tenant-acme
          port: 8080
  tls:
    secretName: tenant-acme-tls     # the per-host Secret from hop 3

Check yourself

1 question. One attempt each is recorded; the explanation is the point, not the score.

Why is allowExternalNameServices disabled by default in Traefik v3?