Skip to content

kubernetesIngressNGINX: location ^~ modifier silently fails to match paths in server-snippet #13900

Description

@asouchang

Welcome!

  • Yes, I've searched similar issues on GitHub and didn't find any.
  • Yes, I've searched similar issues on the Traefik community forum and didn't find any.

What did you do?

Used kubernetesIngressNGINX provider with an Ingress containing an NGINX server-snippet using the standard ^~ (best prefix match) location modifier to block internal paths:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: test-ingress
  annotations:
    nginx.ingress.kubernetes.io/server-snippet: |
      location ^~ /internal {
        return 404;
      }
spec:
  ingressClassName: nginx
  rules:
    - host: example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: test-service
                port:
                  number: 80

Sent an HTTP request to GET /internal.

What did you see instead?

The request to /internal was not blocked and fell through to the catch-all path: / backend. No error or warning was produced during Ingress parsing or at runtime.

Root Cause in Source Code:

In pkg/middlewares/ingressnginx/snippet/collector.go:

  1. createLocationCollectable() handles 2 parameters when a modifier is present:

    pathPattern := params[0].String()
    if len(params) == 2 {
        pathPattern += params[1].String()
    }

    For location ^~ /internal, params[0] is "^~" and params[1] is "/internal", making pathPattern = "^~/internal".

  2. buildLocationMatcher(pathPattern) checks for ~*, ~, and =, but omits ^~:

    func buildLocationMatcher(pathPattern string) (locationMatcher, error) {
        if pattern, ok := strings.CutPrefix(pathPattern, "~*"); ok { ... }
        if pattern, ok := strings.CutPrefix(pathPattern, "~"); ok { ... }
        if exact, ok := strings.CutPrefix(pathPattern, "="); ok { ... }
    
        // Fallback: Prefix match
        return func(req *http.Request) bool {
            return strings.HasPrefix(req.URL.Path, pathPattern)
        }, nil
    }
  3. Because "^~/internal" does not start with ~ or =, it falls into the prefix match fallback:
    strings.HasPrefix(req.URL.Path, "^~/internal").

  4. Since request paths never start with "^~", matcher(req) silently evaluates to false for every request, causing the block to never execute.

What version of Traefik are you using?

v3.7.5

What is your environment & configuration?

  • Kubernetes with kubernetesIngressNGINX provider enabled (--providers.kubernetesingressnginx=true, --providers.kubernetesingressnginx.allowSnippetAnnotations=true).
  • Standard networking.k8s.io/v1 Ingress with nginx.ingress.kubernetes.io/server-snippet.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions