This is the "latest" release of Envoy Gateway, which contains the most recent commits from the main branch.
This release might not be stable.
Please refer to the /docs documentation for the most current information.
Backend Dependencies for Extensions
7 minute read
An extension may need to call another service before allowing a request to
continue, for example to check authorization or look up a key. Declare that
service in EnvoyExtensionPolicy.spec.backends so Envoy Gateway can configure
the connection for Lua, Wasm or dynamic module HTTP callouts.
Envoy sends these calls through a cluster, which holds the backend’s endpoints
and connection settings. Your extension uses an alias you choose, such as
resolver, to find the cluster name during a request. This keeps generated
cluster names out of your extension configuration and lets you change the
backend reference while keeping the same alias in your code. Envoy Gateway
resolves endpoints, authorizes references and configures backend TLS.
Enable extension backends
Extension backend bindings are disabled by default. To enable them, set
extensionApis.enableExtensionBackends: true in the EnvoyGateway configuration:
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyGateway
provider:
type: Kubernetes
extensionApis:
enableExtensionBackends: true
This setting applies to Service, ServiceImport and Backend references for all
supported extension runtimes. References to Backend resources additionally require
extensionApis.enableBackend: true. For the Lua example, also
enable Lua support.
While extension backends are disabled, policies that declare or inherit bindings
are rejected with Accepted=False, and affected routes return HTTP 500. Policies
without bindings continue to work.
Declare a backend
In spec.backends, name defines the alias your extension uses. The
backendRef.name identifies a Kubernetes resource by its metadata.name. That
resource can be a Service, ServiceImport or Envoy Gateway Backend. A
Backend resource defines destinations such as IP
addresses, DNS names and Unix socket paths. The alias and resource name can differ.
This policy binds the alias resolver to the Kubernetes Service lookup-service.
The Lua script looks up that alias and calls the resulting cluster. The Service does not need
to appear in the HTTPRoute’s backendRefs. Your route can send client requests
to the application while the extension uses a separate service for its lookup.
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyExtensionPolicy
metadata:
name: resolverpolicy
namespace: default
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: app
backends:
- name: resolver
backendRef:
name: lookup-service
port: 8080
lua:
- type: Inline
inline: |
function envoy_on_request(handle)
local cluster = handle:route():metadata():get("resolver")
if cluster == nil then
handle:respond({[":status"] = "502"}, "missing resolver binding")
return
end
local headers = handle:httpCall(cluster, {
[":method"] = "GET",
[":path"] = "/lookup",
[":authority"] = "lookup-service"
}, nil, 2000)
if headers[":status"] ~= "200" then
handle:respond({[":status"] = "502"}, "resolver callout failed")
end
end
If the lookup returns HTTP 200, the original request continues to the route’s backend. Otherwise, the script responds with HTTP 502.
Aliases must be lowercase DNS labels and unique within the policy, with at most
16 bindings. resolver is an example alias chosen by the operator. Separate
policies can use the same alias for different Services on the same proxy. Lua,
Wasm and dynamic modules using the same effective policy share its backend bindings.
The HTTP authority, path, timeout and response handling remain part of extension
configuration or code. The alias supplies only the Envoy cluster name.
Configure backend connections
Use backendSettings to control how Envoy connects to the callout service.
For example, this binding allows three seconds to establish a connection and
limits the cluster to 100 connections:
backends:
- name: resolver
backendRef:
name: lookup-service
port: 8080
backendSettings:
timeout:
tcp:
connectTimeout: 3s
circuitBreaker:
maxConnections: 100
These settings configure the callout cluster. Omitted settings use existing
defaults. Inherited bindings keep their settings. BackendTrafficPolicy settings
for route traffic do not apply to these callouts. A connection timeout limits
how long Envoy waits to connect. The extension controls how long it waits for
an individual callout and whether to retry. For that reason, requestTimeout
and streamIdleTimeout are rejected here. DynamicModule load balancing and
HTTP/3 are not supported for extension backends.
Backend references support the same resources as external processing services:
Service, ServiceImport and Envoy Gateway Backend. Enable the Backend API before
referencing Backend resources. References to another namespace require a ReferenceGrant
allowing gateway.envoyproxy.io/EnvoyExtensionPolicy from the declaring policy’s
namespace. BackendTLSPolicy and applicable backend TLS settings are resolved by
Envoy Gateway.
DynamicResolver backends and mixed DNS/static endpoint sets are not supported.
Use a Unix socket backend
Reference a Backend resource to reach a sidecar over a Unix socket:
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: Backend
metadata:
name: sidecar
namespace: default
spec:
endpoints:
- unix:
path: /run/resolver/socket
Bind it in the policy:
backends:
- name: resolver
backendRef:
group: gateway.envoyproxy.io
kind: Backend
name: sidecar
Enable the Backend API and mount the socket directory in both Envoy and the sidecar. Operators manage the sidecar and socket lifecycle. Extensions use the same alias lookup and HTTP callout APIs as for a Service.
Read a binding
The alias map is stored in Envoy’s route metadata with the route configuration. Reading it is a local lookup, with no request to Envoy Gateway or Kubernetes. It is not an HTTP header and is not sent to the backend service.
Envoy Gateway publishes a flat map of aliases to cluster names in
the selected route’s metadata.filter_metadata under
gateway.envoyproxy.io/extension-backends. It also publishes the map under each
Lua filter’s exact name, because Lua’s metadata accessor selects that namespace.
Lua uses handle:route():metadata():get("resolver"), as above.
A Wasm extension uses its SDK’s property getter with this path:
["xds", "route_metadata", "filter_metadata",
"gateway.envoyproxy.io/extension-backends", "resolver"]
Decode the returned bytes as a UTF-8 cluster name and pass it to
dispatch_http_call (or the equivalent call in the SDK).
The dynamic module Go SDK provides this lookup:
cluster, found := handle.GetMetadataString(
shared.MetadataSourceTypeRoute,
"gateway.envoyproxy.io/extension-backends",
"resolver",
)
if !found {
// Return a local error for the missing binding.
return
}
// Use cluster.ToString() as the cluster argument to handle.HttpCallout.
The Rust SDK provides get_metadata_string with metadata source Route and the
same namespace and key. SDK buffers can borrow Envoy memory. Copy the string if
it must outlive the callback. See the dynamic module test example for a complete
HTTP callout using the Go SDK.
Security considerations
Backend bindings allow policy authors to configure outbound traffic from Envoy.
ReferenceGrant checks authorize references to Kubernetes resources, but they
do not prevent SSRF or restrict the network destinations behind those resources.
Aliases identify clusters, however they do not restrict which clusters a dynamic module can call. Use trusted modules and restrict who can configure policies and referenced backends.
A child policy can use inherited bindings or replace them while retaining the parent’s extension. Replacing an authorization backend could therefore change authorization decisions.
Active health checks can also generate traffic without extension calls. Use admission controls and network restrictions when delegating policy management.
Scope and lifecycle
Bindings belong to the selected route, so a shared extension can call different services for different applications. Always resolve the alias for the current request rather than keeping one cluster name for the lifetime of the extension.
Look up bindings while processing request headers or later in the request’s context. Initialization and independent background tasks have no request route. Asynchronous callout completion remains supported. Resolve the binding in request context and use it only for that request, copying borrowed strings if needed. Do not cache a binding across requests: another route can map the same alias to a different backend, including when the same filter instance serves both routes. Handle missing bindings explicitly and treat generated cluster names as opaque. Route changes can change which bindings are available.
Normal EnvoyExtensionPolicy attachment precedence applies. When merging a route
policy with a parent policy, an omitted or empty backends list inherits the parent’s
bindings and reference namespace. A supplied nonempty list replaces the whole
list, so repeat every binding needed by inherited extensions. This also lets a
route intentionally rebind an alias used by an inherited extension. Without
merging, only the selected policy’s bindings apply.
For example, a Gateway policy can provide a common lookup service. A route
policy that merges with it can replace the resolver binding to use a service
for that application while retaining the parent’s extension code.
Invalid references set the policy’s Accepted condition to False and affected
routes return HTTP 500 until the dependency is corrected. This applies even when
a Wasm extension has failOpen enabled: the policy’s declared dependency is
invalid. Unrelated policies continue to operate.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.