Trying to issue redirect with EnvoyFilter

Not sure where to start to debug this. I want to issue a redirect for all traffic arriving with “x-forwarded-proto” == “http”. I was thinking something like this would do the trick:

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: mhite-elbgateway-http-redir
  namespace: istio-system
spec:
  workloadLabels:
    app: mhite-elbgateway
  filters:
  - listenerMatch:
      listenerType: GATEWAY
      portNumber: 443
      listenerProtocol: HTTP
    filterName: envoy.lua
    filterType: HTTP
    insertPosition:
      index: FIRST
    filterConfig:
      inlineCode: |
        function envoy_on_request(request_handle)
          -- Response directly with a redirect when X-Forwarded-Proto == "http"
          local headers = request_handle:headers()
          local xfp = headers:get('x-forwarded-proto')
          if xfp == "http" then
            local path = headers:get(':path')
            local authority = headers:get(':authority')
            local location = "https://" .. authority .. path
            request_handle:respond({[":status"] = "308", ["Location"] = location}, "308 Permanent Redirect")
            end
        end

I’m running Istio 1.4.6. For what it is worth, when I use istioctl proxy-config listener, I can see it attached as a filter:

Snippet:

        "filters": [
            {
                "name": "envoy.http_connection_manager",
                "typedConfig": {
                    "@type": "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager",
                    "statPrefix": "outbound_0.0.0.0_443",
                    "rds": {
                        "configSource": {
                            "ads": {}
                        },
                        "routeConfigName": "https.443.https.mhite-elbgateway.istio-system"
                    },
                    "httpFilters": [
                        {
                            "name": "envoy.lua",
                            "config": {
                                "inlineCode": "function envoy_on_request(request_handle)\n    -- Response directly with a redirect when X-Forwarded-Proto == \"http\"\n    local headers = request_handle:headers()\n    local xfp = headers:get('x-forwarded-proto')\n    if xfp == \"http\" then\n    local path = headers:get(':path')\n    local authority = headers:get(':authority')\n    local location = \"https://\" .. authority .. path\n    request_handle:respond({[\":status\"] = \"308\", [\"Location\"] = location}, \"308 Permanent Redirect\")\n    end\nend\n"
                            }
                        },
                        {
                            "name": "envoy.filters.http.jwt_authn",
                            "typedConfig": {
                                "@type": "type.googleapis.com/envoy.config.filter.http.jwt_authn.v2alpha.JwtAuthentication",
                                "providers": {
                                    "origins-0": {

Any ideas or pointers from folks who have gone down this path would be much appreciated!

I had based the approach on something mentioned here:

I realize that using the “filters” part in the spec is deprecated, but I am seeing a lot of chatter that the new applyTo patch method doesn’t work right yet. I also don’t know how to translate this old method into the new method.

@xring - Any idea on this? I see in a different thread you’ve been down this path.

I’ve figured out the mystery!

Envoy/Istio strips out the X-Forwarded-For and X-Forwarded-Proto from the header context in the http filter by default.

Here is what I managed to get working. In addition to refactoring this to use the patch method vs. deprecated filterConfig method, adding the use_remote_address: true patch was also key! And xff_num_trusted_hops: 2 allows me to get the real client addresses in the X-Envoy-External-Address request header before it is sent to the service.

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: mhite-elbgateway-ef
  namespace: istio-system
spec:
  configPatches:
  - applyTo: NETWORK_FILTER
    match:
      context: GATEWAY
      listener:
        filterChain:
          filter:
            name: envoy.http_connection_manager
        portNumber: 443
    patch:
      operation: MERGE
      value:
        typed_config:
          '@type': type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
          use_remote_address: true
          xff_num_trusted_hops: 2
  - applyTo: HTTP_FILTER
    match:
      context: GATEWAY
      listener:
        filterChain:
          filter:
            name: envoy.http_connection_manager
        portNumber: 443
    patch:
      operation: INSERT_BEFORE
      value:
        config:
          inlineCode: |
            function envoy_on_request(request_handle)
              -- Response directly with a redirect when X-Forwarded-Proto == "http"
              local headers = request_handle:headers()
              local xfp = headers:get('x-forwarded-proto')
              if xfp == "http" then
                local path = headers:get(':path')
                local authority = headers:get(':authority')
                local location = "https://" .. authority .. path
                request_handle:respond({[":status"] = "308", ["Location"] = location}, "308 Permanent Redirect")
              end
            end
        name: envoy.lua
  workloadSelector:
    labels:
      istio: mhite-elbgateway

Hopefully this helps other trying to perform a redirect using X-Forwarded-Proto signaling!

-M

4 Likes

@mhite I created an account here, just to thumbs up your solution <3

We use the Envoy filter above to redirect requests to https when using Istio with GCP NEGs in front of it.

Thanks for sharing.

Glad to have helped!

This is a very helpful discussion. But, any suggestion on how to handle redirect on the response, if I get the error code 503 I want to redirect to some http://xyz.co.au URL.

@Mkb2020 I know it’s a few years late, but if anyone sees this there is the envoy_on_response function in lua that should run on the response back to the client → Lua — envoy 1.27.0-dev-7000e2 documentation