Skip to content

Bug: Admission validation skips checking subsequent rules/paths if the first one is valid in checkOverlap #14240

@yttdj

Description

@yttdj

What happened:

The Admission Validation logic (checkOverlap) incorrectly skips checking subsequent Rules and Paths if the first Rule/Path in the Ingress definition is valid (no conflict).

This allows an Ingress with conflicting rules to bypass the admission controller and be successfully applied to the cluster, as long as the conflicting rule is placed after a non-conflicting rule in the YAML definition.

Specifically, in internal/ingress/controller/controller.go, inside the checkOverlap function, there is a return nil statement inside the inner Path loop. This causes the function to exit immediately after verifying the first path of the first rule, ignoring all remaining paths and rules.

What you expected to happen:

The Admission Controller should check ALL rules and paths defined in an Ingress object for conflicts. If any rule/path conflicts with an existing Ingress, the request should be rejected, regardless of the order of rules in the YAML.

NGINX Ingress controller version:
Latest (and affects previous versions as well). Code logic issue exists in main branch.

Kubernetes version:
All versions.

Environment:

  • Cloud provider: Any (Logic issue)
  • OS: Any
  • Install tools: Any

How to reproduce this issue:

  1. Create a Victim Ingress (A) that occupies a specific host/path.
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    name: victim
    namespace: default
    spec:
    ingressClassName: nginx
    rules:
    • host: conflict.example.com
      http:
      paths:
      • path: /
        pathType: Prefix
        backend:
        service:
        name: http-svc
        port:
        number: 80
    1. Create an Intruder Ingress (B).
      Crucial Step: Define a non-conflicting rule FIRST, and the conflicting rule SECOND.
      apiVersion: networking.k8s.io/v1
      kind: Ingress
      metadata:
      name: intruder
      namespace: default
      spec:
      ingressClassName: nginx
      rules:

    Rule 1: No conflict (Safe)

    • host: safe.example.com
      http:
      paths:
      • path: /
        pathType: Prefix
        backend:
        service:
        name: http-svc
        port:
        number: 80

    Rule 2: Conflict! (Should be rejected, but is accepted due to bug)

    • host: conflict.example.com
      http:
      paths:
      • path: /
        pathType: Prefix
        backend:
        service:
        name: http-svc
        port:
        number: 80
    1. Apply Ingress B.
    • Actual Result: ingress.networking.k8s.io/intruder created. (The conflict in Rule 2 is ignored).
    • Expected Result: Admission webhook should deny the request due to conflict in Rule 2.

Anything else we need to know:

Root Cause Analysis:

The issue is located in internal/ingress/controller/controller.go.

func (n *NGINXController) checkOverlap(ing *networking.Ingress, servers []*ingress.Server) error {
for _, rule := range ing.Spec.Rules {
for _, path := range rule.HTTP.Paths {
// ... conflict checking logic ...

        existingIngresses := ingressForHostPath(rule.Host, path.Path, servers)
        if len(existingIngresses) == 0 {
            continue
        }

        // ... checks ...

        // BUG HERE:
        // This 'return nil' is inside the loop.
        // It causes the function to exit successfully after the FIRST valid path check.
        // Subsequent paths and rules are never checked.
        return nil 
    }
}
return nil

}Additionally, there is a secondary minor issue where it returns nil immediately if it encounters itself in the existingIngresses list, instead of continue, which might also skip checks in other edge cases.

Suggested Fix:
Remove the premature return nil inside the loop.

        // ... inside loop ...
        // return nil  <-- REMOVE THIS
    }
}
return nil

}

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/bugCategorizes issue or PR as related to a bug.needs-priorityneeds-triageIndicates an issue or PR lacks a `triage/foo` label and requires one.

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions