Breadcrumb

  1. Red Hat Interactive Learning Portal
  2. Unlock self-service API credentials on Connectivity Link
  3. Use the Swagger Try It Out feature for Connectivity Link with EnvoyFilter

Unlock self-service API credentials on Connectivity Link

Understand how to set up a hub-only Connectivity Link stack, then let developers mint API keys and OIDC client credentials from Red Hat Developer Hub, without ticket back and forth.

A multi-cloud GitOps hub-only lab validates these patterns and deploys the AI Computer Vision pattern repository. By completing the Unlock self-service API credentials on Connectivity Link learning path first, that allows you to configure the Kuadrant AuthPolicyHTTPRoute ResponseHeaderModifier Cross-Origin Resource Sharing (CORS) headers, and Developer Hub Swagger integration. This article shows how a simple EnvoyFilter layer completes the browser experience on Connectivity Link after those controls are already in place.

Machine-to-machine clients, such as curl, and backend services, send authorization headers directly. Browser-based Swagger Try it out in Developer Hub is different; when a non-simple header such as Authorization: Bearer or Authorization: APIKEY is present, the browser sends a CORS preflight (OPTIONS) before the real request.

Red Hat Connectivity Link already does the heavy lifting by the Kuadrant AuthPolicy authenticating requests, and HTTPRoute ResponseHeaderModifier supplying  Access-Control-Allow-* headers. For browser Swagger clients, Connectivity Link also skips JSON Web Token (JWT) or API key checks on OPTIONS through an AuthPolicy spec.when definition.

Some gateway API HTTPRoute filter sets do not include a direct respond-with-204 action. When the backend API or the gateway listener still returns a non-2xx status for OPTIONS, browsers block Try it out even though Connectivity Link policies are correct. A small EnvoyFilter custom resource on the Connectivity Link Gateway workload closes that gap. EnvoyFilter comes from OpenShift Service Mesh (Istio): it is a Kubernetes resource that patches the Envoy proxy in the gateway pods, not a command-line tool. Lua returns HTTP 204 for OPTIONS before traffic reaches backends, and an optional access-log patch surfaces AuthPolicy identity (auth_client_id) for operators.

Prerequisites:

In this lesson, you will:

  • Enable Swagger UI and Developer Hub browser clients to call Connectivity Link Gateways without "Failed to fetch" errors or blocked CORS preflight requests.
  • Learn and follow the Connectivity Link-first order: HTTPRoute CORS headers, then AuthPolicy OPTIONS exemption, then a small EnvoyFilter Lua 204 only if browsers still need a preflight short-circuit.
  • See who called the API (auth_client_id) in gateway access logs after AuthPolicy succeeds.
  • Keep reusable EnvoyFilter patterns separate from workshop-specific or sandbox-only exceptions.

Stay on Connectivity Link first

Prefer Connectivity Link and Gateway API controls first. Add an EnvoyFilter only if the browser still blocks Swagger Try it out after those controls. A preflight short-circuit means Envoy answers OPTIONS with HTTP 204 immediately, so the request never reaches the back end. It is not a quiz. Work through these checks in order:

  • Can HTTPRoute ResponseHeaderModifier set CORS headers? If yes, start there.
  • Can AuthPolicy exempt OPTIONS with a spec.when definition? If yes, add a JWT or API key for authentication.
  • Does preflight still return a non-2xx OPTIONS? If yes, add EnvoyFilter Lua 204.
  • Does the backend API break the HTTP contract? For example, does it redirect POST, or does it not implement OPTIONS? If yes, fix that backend API or use a request rewrite.
  • Is the Envoy or WebAssembly (WASM) version incompatible? If yes, use the sandbox exception only. See the warning regarding the pattern for removing WASM and MaaS headers in the Developer Sandbox environment.

Figure 1 illustrates the decision flow. The workflow starts with HTTPRoute CORS headers and AuthPolicy OPTIONS exemption before adding EnvoyFilter Lua patches or backend-specific rewrites.

Flow chart from HTTPRoute CORS through AuthPolicy OPTIONS exemption to EnvoyFilter Lua and a backend API fix.
Figure 1: Decision tree.

A reusable pattern for CORS preflight short-circuit

Swagger UI sends a non-simple authorization header. Browsers issue a CORS preflight (OPTIONS) before the real request. If the backend API or gateway returns anything other than 2xx for OPTIONS, the browser blocks the call, even when CORS response headers are present on other status codes.

As a solution, you can insert a Lua HTTP filter on the gateway workload that responds to OPTIONS with HTTP 204 before traffic reaches backends. The following example is from the charts/all/neuroface-gateway/templates/envoyfilter-cors-preflight.yaml file:

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: neuroface-gateway-cors-preflight
  namespace: neuroface-gateway-system
spec:
  workloadSelector:
    labels:
      gateway.networking.k8s.io/gateway-name: neuroface-gateway
  configPatches:
    - applyTo: HTTP_FILTER
      match:
        context: GATEWAY
        listener:
          filterChain:
            filter:
              name: envoy.filters.network.http_connection_manager
              subFilter:
                name: envoy.filters.http.router
      patch:
        operation: INSERT_BEFORE
        value:
          name: envoy.filters.http.lua.cors_preflight
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
            inlineCode: |
              function envoy_on_request(request_handle)
                local headers = request_handle:headers()
                if headers:get(":method") == "OPTIONS" then
                  request_handle:respond({[":status"] = "204"}, "")
                end
              end

Important

The Lua filter sets status only. Access-Control-Allow-* headers still come from each HTTPRoute's ResponseHeaderModifier. Duplicating CORS headers in Lua causes browser errors.

Where this pattern is applied

The YAML above is the neuroface-gateway copy of that Lua 204 pattern. Apply the same EnvoyFilter once per Connectivity Link Gateway, because the workloadSelector matches a single gateway name. This table lists each gateway in the lab, its namespace, and the EnvoyFilter name: 

Gateway

Namespace

EnvoyFilter name

neuroface-gateway

neuroface-gateway-system

neuroface-gateway-cors-preflight

workshop-apis-gateway

workshop-kuadrant-apis

workshop-apis-gateway-cors-preflight

ai-gateway

ai-gateway-system

ai-gateway-cors-preflight


It is important to note that you should copy one EnvoyFilter per gateway because the workloadSelector matches a single gateway name. For example, the neuroface-gateway uses the charts/all/neuroface-gateway/templates/envoyfilter-cors-preflight.yaml file. 

The workshop chart ships workshop-apis-gateway-cors-preflight and ai-gateway-cors-preflight as separate EnvoyFilter documents in one multi-document Helm template, charts/all/workshop-kuadrant-apis/templates/envoyfilter-cors-preflight.yaml. This same file also includes the workshop-specific filters for the cities rewrite as described below. 

The ai-gateway is an optional third gateway in the workshop chart for AI catalog demos. This is outside the Unlock self-service API credentials on Connectivity Link learning path, which only covers the workshop-apis-gateway and neuroface-gateway.

Figure 2 shows how the three CORS layers work together. The ResponseHeaderModifier supplies the Access-Control-* headers; AuthPolicy skips JWT validation on OPTIONS; EnvoyFilter Lua returns HTTP 204 so browsers accept preflight.

Layered diagram of HTTPRoute CORS headers, AuthPolicy OPTIONS skip, and EnvoyFilter 204 response.
Figure 2: Three-layer CORS stack.

Verify preflight with curl

curl -sk -X OPTIONS \
  -H "Origin: https://developer-hub.<apps-domain>" \
  -H "Access-Control-Request-Method: GET" \
  -H "Access-Control-Request-Headers: authorization,content-type" \
  -w "\nHTTP %{http_code}\n" \
  "https://neuroface-cv.<apps-domain>/health"

The expected output is an HTTP 204 with Access-Control-Allow-Origin from the HTTPRoute filter. Then confirm Swagger Try it out succeeds on an authenticated endpoint in Developer Hub.

Important

This pattern exception is only for the workshop countries’ API. It is not a reusable Connectivity Link CORS pattern.

Problem

The external countriesnow.space API returns HTTP 301 on POST /countries/cities, redirecting to a GET URL API call. Envoy forwards a relative location header. The browser follows it to a path that does not match any HTTPRoute, returning 404 without CORS headers. Swagger shows "Failed to fetch". Rewriting the response location header caused redirect loops.

Solution

Rewrite the request before it leaves the gateway by intercepting POST /countries/cities, read the JSON body, and convert to GET /countries/cities/q?country=<value>, the country name from the JSON country field, for example, Argentina. The Multicloud GitOps charts ship this as a third EnvoyFilter resource, workshop-apis-gateway-restcountries-cities-rewrite, in the same multi-document Helm template as the CORS preflight filters, see the charts/all/workshop-kuadrant-apis/templates/envoyfilter-cors-preflight.yaml file for details. The Lua filter is inserted before the router so it sees the external path /countries/cities, then the existing HTTPRoute URLRewrite adds the backend path /api/v0.1 prefix.

Do not treat this rewrite as a Connectivity Link pattern. Prefer fixing the backend API so it accepts POST, or adjusting the OpenAPI specification to use GET directly.

Important

A shortcoming with Lua to consider, is after calling request_handle:body(), this re-fetches the headers with request_handle:headers(). Reusing a handle captured before body(), triggers Envoy Lua scope errors.

A pattern for WASM removal and MaaS header in the Developer Sandbox environment

Important

This exercise is for the Red Hat Developer Sandbox only, and not for production environments.

Problem

On Red Hat OpenShift AI disposable sandboxes, gateways using data-science-gateway-class might run an Envoy version incompatible with the Kuadrant WebAssembly (WASM) module shipped in Red Hat Connectivity Link 1.4.1. That module runs inside Envoy and enforces AuthPolicy and rate limits. Gateway pods crash-loop or return HTTP 500.

Solution

The following sandbox-only workaround removes Kuadrant WASM filters from gateways in openshift-ingress. It disables AuthPolicy and rate limit enforcement on affected listeners, so it is not a Connectivity Link production configuration. Use only on disposable demo clusters, for example, Red Hat Product Demo System sandboxes. The Helm chart charts/all/models-as-a-service/templates/envoyfilter-wasm-fix.yaml applies a REMOVE patch such as:

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: maas-gateway-wasm-removal
  namespace: openshift-ingress
spec:
  priority: 10
  configPatches:
    - applyTo: HTTP_FILTER
      match:
        context: GATEWAY
        listener:
          filterChain:
            filter:
              name: envoy.filters.network.http_connection_manager
              subFilter:
                name: envoy.filters.http.wasm
      patch:
        operation: REMOVE

A companion Lua filter injects X-MaaS-Username and X-MaaS-Group headers required by the Models as a Service (MaaS) API backend when Kuadrant authentication is bypassed.

Disable the sandbox workaround with the following Helm values:

gateway:
  wasmFix:
    enabled: false
    injectMaasHeaders: false

A pattern for identifying clients making API calls in the access log

Problem

The Kuadrant WASM module does not always expose ext_authz metadata in Envoy access logs. Operators cannot see which OpenID Connect (OIDC) client made an API call from the default istio-proxy logs.

Solution

When AuthPolicy injects response headers such as x-auth-client-id, an EnvoyFilter can merge a JSON access log format that reads those headers. The following example is from the Helm chart, charts/all/neuroface-gateway/templates/envoyfilter-access-log.yaml:

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: neuroface-gateway-custom-access-log
  namespace: neuroface-gateway-system
spec:
  workloadSelector:
    labels:
      gateway.networking.k8s.io/gateway-name: neuroface-gateway
  configPatches:
    - applyTo: NETWORK_FILTER
      match:
        context: GATEWAY
        listener:
          filterChain:
            filter:
              name: envoy.filters.network.http_connection_manager
      patch:
        operation: MERGE
        value:
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
            access_log:
              - name: envoy.access_loggers.file
                typed_config:
                  "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
                  path: /dev/stdout
                  log_format:
                    json_format:
                      timestamp: "%START_TIME%"
                      method: "%REQ(:METHOD)%"
                      path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
                      response_code: "%RESPONSE_CODE%"
                      auth_client_id: "%REQ(x-auth-client-id)%"
                      auth_metadata: "%REQ(x-auth-metadata)%"

This technique is portable to any gateway where AuthPolicy sets identifiable response headers on success. After applying Pattern C, authenticated requests show auth_client_id in the gateway logs; OPTIONS preflight, and unauthenticated API calls show null.

Observe EnvoyFilters in OpenShift Service Mesh

In the OpenShift web console, click Workloads > Pods > neuroface-gateway-istio-* > Service Mesh > Overview.

Figure 3 shows EnvoyFilters on the Connectivity Link Gateway.

Service mesh Overview with Istio Config EnvoyFilters and Gateway topology to CV and hub back ends.
Figure 3: Istio Config on neuroface-gateway-istio listing the Kuadrant WASM module, cors-preflight, and custom-access-log EnvoyFilters.

Kiali exposes the same gateway workload. Under Logs, you should see the container istio-proxy. The custom access log format shows JWT client identity after applying the pattern on step 4. Figure 4 shows an authenticated request.

Kiali istio-proxy JSON logs showing auth_client_id on authenticated requests.
Figure 4: Kiali Logs with custom JSON access log format and auth_client_id on authenticated inference requests.

Operational checklist for each gateway

Use this checklist once per Connectivity Link Gateway when you enable browser Swagger, and again if Try it out starts failing. These are not ongoing health probes. Every item must be true on that gateway, or the browser will still block preflight.

  • The HTTPRoute ResponseHeaderModifier sets Access-Control-Allow-Origin to the Developer Hub URL.
  • AuthPolicy includes when: request.method != "OPTIONS".
  • EnvoyFilter Lua returns 204 for OPTIONS on this Gateway workload.
  • The OPTIONS test returns 204 and CORS headers.
  • Swagger Try it out succeeds on an authenticated endpoint.
  • Document any backend-specific rewrite separately from reusable patterns.

Learning path summary

You assembled this solution with the multi-cloud GitOps validated pattern on a single hub cluster, using one Pattern CR and the values-hub-only.yaml overlay against the AI Computer Vision repository. That deployment gave you Red Hat Connectivity Link Gateways, Red Hat Developer Hub, Red Hat build of Keycloak, and the NeuroFace demo workloads from GitOps.

You then configured two self-service authentication flows:

  • API keys for an external REST API on workshop-apis, with AuthPolicy, PlanPolicy tiers, and Developer Hub key provisioning.
  • OIDC client credentials for the Computer Vision inference API on neuroface-cv, with AuthPolicy JWT validation and plan-based rate limits.

You then configured the Red Hat Connectivity Link Gateway API, and Kuadrant AuthPolicy to configure rate limits for authentication and covers most CORS needs. You used the reusable EnvoyFilter Lua 204 pattern for each gateway only to finish browser preflight when listeners still need an explicit 204. Then you strengthen the Connectivity Link operations by creating a pattern to surface auth_client_id in gateway access logs. This keeps backend API rewrites and WASM removal as sandbox or workshop exceptions, not the default path.

Developers now request credentials from Developer Hub, and Kuadrant enforces authentication at the Gateway edge.

Next

If Swagger Try it out still fails after AuthPolicy and HTTPRoute CORS headers, continue with these resources:

Ready to learn more?

Previous resource
Protect AI inference APIs with OpenID Connect AuthPolicy