Skip to content

HTTPRoute set to Accepted: False when listener has missing TLS certificate #9128

Description

@apkatsikas

Description

When an HTTPRoute is attached to a listener (via Gateway or ListenerSet) that has a missing or unresolvable TLS certificate reference, Envoy Gateway sets the route's Accepted condition to False with reason NoReadyListeners. This affects all route types processed by processAllowedListenersForParentRefs.

In this scenario the ListenerSet itself is Accepted: True, and the listener entry is also Accepted: True, only Programmed: False because the TLS secret is missing. The route binding is valid. Setting Accepted: False on the route conflates listener programmed state with route acceptance, which the spec treats as separate concerns.


Spec

The spec defines the valid reasons for the Accepted condition on a route:

apis/v1/shared_types.go L342–397

RouteReasonAccepted                   RouteConditionReason = "Accepted"
RouteReasonNotAllowedByListeners      RouteConditionReason = "NotAllowedByListeners"
RouteReasonNoMatchingListenerHostname RouteConditionReason = "NoMatchingListenerHostname"
RouteReasonNoMatchingParent           RouteConditionReason = "NoMatchingParent"
RouteReasonUnsupportedValue           RouteConditionReason = "UnsupportedValue"
RouteReasonPending                    RouteConditionReason = "Pending"
RouteReasonIncompatibleFilters        RouteConditionReason = "IncompatibleFilters"

All reasons for Accepted: False concern binding validity: hostname match, allowedRoutes, sectionName. None reference listener programmed state.


Expected behavior

The HTTPRoute binding is valid: the route matches the listener's allowedRoutes, the hostname matches, and the sectionName resolves. The listener not being programmed is a listener-level concern already tracked by the listener's Programmed: False condition. The HTTPRoute should be Accepted: True.

The spec does not define a Programmed condition for routes, and ResolvedRefs is scoped to the route's own object references (backends, etc.); neither fits this scenario. There is no spec-defined condition on a route that represents listener readiness. The listener's own Programmed: False is sufficient to surface the unready state.

Actual behavior

The HTTPRoute is set to Accepted: False / NoReadyListeners even though the ListenerSet is Accepted: True and the listener entry is Accepted: True.


Relation to previous fix

This is the same class of bug as #8870 (ListenerSet Accepted: False for InvalidCertificateRef), fixed in #8871. That fix moved the concern to ResolvedRefs: False and Programmed: False at the listener level. The same pattern is occurring one level up: the listener's unprogrammed state is being promoted into the route's Accepted condition.


Repro steps

Apply the following to a cluster running Envoy Gateway with a Gateway already deployed. The secret bug-test-tls does not exist.

apiVersion: v1
kind: Namespace
metadata:
  name: eg-bug-test
---
apiVersion: gateway.networking.k8s.io/v1
kind: ListenerSet
metadata:
  name: bug-test
  namespace: eg-bug-test
spec:
  parentRef:
    name: gateway
    namespace: envoy-gateway-system
  listeners:
    - name: https
      hostname: bug-test.localhost
      port: 443
      protocol: HTTPS
      tls:
        mode: Terminate
        certificateRefs:
          - name: bug-test-tls
      allowedRoutes:
        namespaces:
          from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: bug-test
  namespace: eg-bug-test
spec:
  parentRefs:
    - kind: ListenerSet
      name: bug-test
      namespace: eg-bug-test
      sectionName: https
  hostnames:
    - bug-test.localhost
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: fake
          port: 80

Observe:

kubectl get listenerset bug-test -n eg-bug-test -o yaml
kubectl get httproute bug-test -n eg-bug-test -o yaml

ListenerSet is Accepted: True, listener entry is Accepted: True and Programmed: False:

status:
  conditions:
  - message: All listeners are accepted
    reason: Accepted
    status: "True"
    type: Accepted
  - message: No listeners are programmed
    reason: ListenersNotValid
    status: "False"
    type: Programmed
  listeners:
  - conditions:
    - message: 'No valid secrets exist: certificate refs 0: Secret eg-bug-test/bug-test-tls
        does not exist.'
      reason: InvalidCertificateRef
      status: "False"
      type: ResolvedRefs
    - message: Listener has been successfully translated
      reason: Accepted
      status: "True"
      type: Accepted
    - message: Listener is invalid, see other Conditions for details.
      reason: ListenersNotValid
      status: "False"
      type: Programmed
    name: https

HTTPRoute is Accepted: False / NoReadyListeners despite the valid binding:

status:
  parents:
  - conditions:
    - message: There are no ready listeners for this parent ref
      reason: NoReadyListeners
      status: "False"
      type: Accepted
    - message: 'Failed to process route rule 0 backendRef 0: service eg-bug-test/fake
        not found.'
      reason: BackendNotFound
      status: "False"
      type: ResolvedRefs
    parentRef:
      kind: ListenerSet
      name: bug-test
      namespace: eg-bug-test
      sectionName: https

Real-world impact

This creates a bootstrapping deadlock for users of cert-manager (HTTP-01) + external-dns (gateway-httproute source):

  1. ListenerSet deployed with cert-manager.io/cluster-issuer annotation. Port 443 listener references a TLS secret that does not yet exist.
  2. HTTPRoute attached to port 443 listener.
  3. EG sets Accepted: False / NoReadyListeners on the HTTPRoute.
  4. external-dns requires Accepted: True to create DNS records → no A record created.
  5. cert-manager cannot complete HTTP-01 challenge (no DNS) → cert not issued.
  6. TLS secret never created → listener never programmed → HTTPRoute never accepted → back to step 4.

Environment

  • Envoy Gateway version: v1.8.0
  • Kubernetes version: v1.35.3-gke.1389002
  • Gateway API version: v1.5.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions