-
Notifications
You must be signed in to change notification settings - Fork 584
Description
Bug: HTTPRoute without hostname generates dummy 500 directResponse routes instead of proper backend routes
Description
When a Listener in a Gateway does NOT have a hostname specified, HTTPRoutes that would be attached to it are incorrectly translated to xDS config with directResponse: 500 instead of proper route: { cluster: ... } entries.
What is being seen:
- All traffic to routes attached to hostname-less listeners returns 500 errors
- xDS config inspection shows routes have
"directResponse": { "status": 500 }instead of proper"route": { "cluster": "..." }
What should be happening:
- When a Listener has NO hostname, it should act as a catch-all listener
- HTTPRoutes matching that listener should be properly translated to xDS routes with valid cluster references
- Requests should be routed to backend services, not rejected with 500 errors
Repro Steps
- Create a Gateway with HTTP/HTTPS listeners WITHOUT hostname field:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: test-gateway
spec:
listeners:
- name: http
port: 80
protocol: HTTP
# NO hostname field
- name: https
port: 443
protocol: HTTPS
# NO hostname field
tls:
certificateRefs:
- name: test-cert
kind: Secret- Create HTTPRoute(s) attached to this Gateway:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: test-route
spec:
parentRefs:
- name: test-gateway
sectionName: http
hostnames:
- example.com
rules:
- backendRefs:
- name: my-backend
port: 8080- Check the xDS route config:
kubectl exec -it <envoy-pod> -c envoy -- \
curl -s localhost:15000/config_dump | jq '.configs[] | select(.type=="route")'- Observe: The route has
"directResponse": { "status": 500 }instead of proper cluster route
Root Cause Analysis
In internal/gatewayapi/route.go processHTTPRouteParentRefListener() (lines 609-680):
func (t *Translator) processHTTPRouteParentRefListener(route RouteContext, routeRoutes []*ir.HTTPRoute, parentRef *RouteParentContext, xdsIR XdsIRMap) bool {
var hasHostnameIntersection bool
for _, listener := range parentRef.listeners {
hosts := computeHosts(GetHostnames(route), listener.Hostname)
if len(hosts) == 0 {
continue // ← Routes are skipped
}
hasHostnameIntersection = true
// ... routes are properly added to irListener.Routes ...
}
return hasHostnameIntersection
}When len(hosts) == 0, the route is skipped (continue). However, somewhere in the translation pipeline, when a route is skipped but already has some state generated, the translator generates a dummy route with directResponse: 500 instead of either:
- Properly attaching the route with
hosts = []string{"*"}if listener has no hostname - Or completely skipping the route generation
The issue is in how computeHosts() in helpers.go handles the case when BOTH route and listener lack hostnames.
Environment
- Envoy Gateway version: v1.5.4
- Envoy Gateway Image: docker.io/envoyproxy/gateway:v1.5.4
- Kubernetes version: [Please specify - e.g., 1.28+]
- Gateway API version: v1
- Envoy Proxy version: [Included in v1.5.4]
Logs
xDS Route Configuration Dumps
WITH hostname on Listener (WORKING):
See attached: route_with_hostname.log
Sample working route:
{
"name": "httproute/newbalance-dev/admin/rule/0/match/5/newbalance-gw_prod_merce_com",
"match": {
"pathSeparatedPrefix": "/user/password_reset_email_sent"
},
"route": {
"cluster": "httproute/newbalance-dev/admin/rule/0"
}
}WITHOUT hostname on Listener (BROKEN):
See attached: route_without_hostname.log
Sample broken route:
{
"name": "httproute/newbalance-dev/admin/rule/0/match/5/newbalance-gw_prod_merce_com",
"match": {
"pathSeparatedPrefix": "/user/password_reset_email_sent"
},
"directResponse": {
"status": 500
}
}Listener Configuration Dumps
listeners-with-hostnames.log- Shows proper filterChainMatch generationlisteners-no-hostnames.log- Shows missing filterChainMatch
listeners-no-hostnames.log
listeners-with-hostnames.log
route_with_hostname.log
route_without_hostname.log
Proposed Fix
The logic in computeHosts() should return []string{"*"} when BOTH conditions are true:
- Route has no hostnames specified
- Listener has no hostname specified
This would allow routes to properly attach to catch-all listeners.
Alternatively, the route translator should properly handle the case where len(hosts) == 0 by generating a catch-all route instead of skipping and leaving a dangling route that gets converted to a 500 error.
Additional Context
Per Gateway API specification, hostname field on Listener is OPTIONAL. When omitted, the listener should match requests for any hostname. The current implementation does not properly handle this case for HTTPRoute -> xDS translation, generating error routes instead of proper routing rules.
The Gateway API docs show examples with commented-out hostname fields (https://gateway.envoyproxy.io/docs/tasks/traffic/http-redirect/), suggesting that listeners without hostnames should be a valid and supported configuration.