gateway-api icon indicating copy to clipboard operation
gateway-api copied to clipboard

Support DirectResponse HTTPRouteFilter

Open arkodg opened this issue 11 months ago • 17 comments

What would you like to be added:

A new HTTPRouteFilter that allows users to directly respond to requests with a specific body and status w/o sending it to a backend.

Existing implementations that have this feature Istio - https://istio.io/latest/docs/reference/config/networking/virtual-service/#HTTPDirectResponse Contour https://projectcontour.io/docs/1.28/config/api/#projectcontour.io/v1.HTTPDirectResponsePolicy

Why this is needed:

  • Adding a catch-all / fallback route to HTTPRoute

arkodg avatar Feb 28 '24 15:02 arkodg

Obviously Istio has this as you noted, so I am in favor of this, but just want to note its a tricky tradeoff of how big we allow. Good for simple "return 404" but anything more complex becomes a bit dicey

howardjohn avatar Feb 28 '24 16:02 howardjohn

Yes, I think that anything we add will need to have a strict limit about size. And an explanation that no, we really won't increase the limit.

youngnick avatar Feb 29 '24 05:02 youngnick

Why this is needed:

For example, some hosts want to disable crawl any of the website's pages. If the request path is "/robots.txt", the gateway will return 200 OK with body "User-agent: * Disallow: /".

Based on the following HTTPRoute, the host "http.route.robots.com" with path "/robots.txt" will be disallowed always.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: http-route-cookie
spec:
  hostnames:
  - http.route.robots.com
  parentRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: http-gateway
  rules:
  - backendRefs:
    - kind: Service 
       name: http-route-production
       port: 7001
    matches:
    - path:
         type: PathPrefix
         value: /
  - directResponse:
    - status: 200
       body: 
         string: "User-agent: *\nDisallow: /"
    matches:
    - path:
         type: Exact
         value: /robots.txt

lianglli avatar Mar 25 '24 13:03 lianglli

Why this is needed:

Moreover, if there is a L7 attack, the directResponse will return 404 based on a new HTTPRoute asap.

Then, the upstream service will be able to process the normal requests without any changes.

E.g., upstream service "http-route-production" try to add a HTTPRoute for protecting it against L7 attack ("http.route.com/acs") specifically:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: http-route-cookie
spec:
  hostnames:
  - http.route.com
  parentRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: http-gateway
  rules:
  - backendRefs:
    - kind: Service 
       name: http-route-production
       port: 7001
    matches:
    - path:
         type: PathPrefix
         value: /
  - directResponse:
    - status: 404
    matches:
    - path:
         type: Exact
         value: /acs

lianglli avatar Mar 25 '24 14:03 lianglli

Yes, I think that anything we add will need to have a strict limit about size. And an explanation that no, we really won't increase the limit.

Yes, it is. The directResponse should be a response with small body or no body.

E.g.,

// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=128
Value string `json:"value"`

lianglli avatar Mar 25 '24 14:03 lianglli

I also have a use-case for this. I have an application with x number of paths, and I want all of them to be exposed to the internet except 1.

Today I can write a httpRoute with rules.matches.pathto whitelist all paths I want to be available, but this becomes very boring if I have an app with a long list of paths. So instead of doing whitelist/allowlist, I would like to be able to do blacklist/blocklist.

For example:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: echo
spec:
  parentRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: http-gateway
  rules:
    - directResponse:
      - status: 404
      matches:
      - path:
           type: Exact
           value: /secretPath
    - backendRefs:
        - name: echo
          kind: Service
          port: 1027

NissesSenap avatar Apr 09 '24 17:04 NissesSenap

I also have a use-case for this. I have an application with x number of paths, and I want all of them to be exposed to the internet except 1.

Today I can write a httpRoute with rules.matches.pathto whitelist all paths I want to be available, but this becomes very boring if I have an app with a long list of paths. So instead of doing whitelist/allowlist, I would like to be able to do blacklist/blocklist.

For example:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: echo
spec:
  parentRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: http-gateway
  rules:
    - directResponse:
      - status: 404
      matches:
      - path:
           type: Exact
           value: /secretPath
    - backendRefs:
        - name: echo
          kind: Service
          port: 1027

The directResponse of HTTPRouteFilter is able to fit your requirement.

I'm working on a GEP for directResponse that focuses on background and past implementations first.

lianglli avatar Apr 10 '24 03:04 lianglli

/assign @lianglli

lianglli avatar Apr 10 '24 03:04 lianglli

The Kubernetes project currently lacks enough contributors to adequately respond to all issues.

This bot triages un-triaged issues according to the following rules:

  • After 90d of inactivity, lifecycle/stale is applied
  • After 30d of inactivity since lifecycle/stale was applied, lifecycle/rotten is applied
  • After 30d of inactivity since lifecycle/rotten was applied, the issue is closed

You can:

  • Mark this issue as fresh with /remove-lifecycle stale
  • Close this issue with /close
  • Offer to help out with Issue Triage

Please send feedback to sig-contributor-experience at kubernetes/community.

/lifecycle stale

k8s-triage-robot avatar Jul 09 '24 03:07 k8s-triage-robot

/remove-lifecycle stale

jbohanon avatar Jul 16 '24 22:07 jbohanon

@lianglli were you able to make any progress on a GEP for this feature?

jbohanon avatar Jul 16 '24 22:07 jbohanon

@lianglli were you able to make any progress on a GEP for this feature?

Yes, I will create a GEP firstly.

lianglli avatar Aug 20 '24 13:08 lianglli

As for other Provisional GEPs, the critical parts are What this is, Why we need it, and Who it's for, along with what is currently supported in various data planes and/or implementations. Would love to see a Provisional GEP PR like that.

youngnick avatar Aug 21 '24 04:08 youngnick

Yes, get it.

lianglli avatar Aug 23 '24 12:08 lianglli

Per suggestion by Mike Morris in the Istio slack I'd like to add my use case

Right now we're moving from VMs fronted by HAProxy to K8s using K8s G8way + Istio (plus other stuff). One of the hang ups I've encountered in trying to recreate our existing behavior is the lack errorfile directive in haproxy or return in nginx. This allows an inline of an error code and a body. To recreate this in the existing k8s gateway api I need to create a null-backend service whose only job is to act as the backend to return 403 "<html><body><h1>\n403 Forbidden</h1>Request forbidden by administrative rules.\n</body></html>\n"

frontend fe_https
    default_backend null
    # lots of acls either http-request deny or allowing to specific backends
    acl deny path_beg /edge/api/internal
    acl api_backend path_beg /edge/api
    http-request deny if deny
    use_backend api if api_backend

backend api
    server edge-api ...

backend null
    errorfile 403 /usr/local/etc/haproxy/403.http
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: translation
spec:
  parentRefs:
  - name: external
  rules:
  - matches: # deny
    - path:
        type: PathPrefix
        value: /edge/api/internal
    filters:
    - type: DirectResponse
      directResponse:
        statusCode: 403
        body:
          string: "Finger wag"
  - matches: # api
    - path:
        type: PathPrefix
        value: /edge/api
    backendRefs:
    - name: edge-api
      port: 8080
  - filters: # default_backend
    - type: DirectResponse
      directResponse:
        statusCode: 403
        body:
          string: "Finger wag"
  

gganley avatar Sep 16 '24 16:09 gganley

@gganley we have a very similar use case and I would also appreciate the direct response filter within HTTPRoute a lot. Instead of using a custom service that returns arbitrary responses, we're applying an Envoy filter custom resource to the gateway, where you can simply patch the route configuration to use the direct response, works like a charm, but it would be nice to accomplish this without additional overhead and just configure it in the HTTPRoute.

bravenut avatar Sep 16 '24 18:09 bravenut