Problem Statement
When a CRD is registered under extensionManager.policyResources, Envoy Gateway treats it as a "directly attached" Gateway API policy and re-translates xDS on changes. However, an extension-server policy can only attach to a Gateway that lives in the same namespace as the policy. Targeting a Gateway in a different namespace silently fails to attach (and, for our extension, means the policy never drives re-translation).
This is currently documented as a one-line limitation on the API field but not explained, and there is no supported mechanism (e.g. ReferenceGrant) to opt into cross-namespace attachment.
From api/v1alpha1/envoygateway_types.go (ExtensionManager.PolicyResources):
// PolicyResources defines the set of K8S resources the extension server will handle
// as directly attached Gateway API policies. Only policies in the same namespace as
// the target Gateway resources are supported. Cross-namespace attachments are not supported.
PolicyResources []GroupVersionKind `json:"policyResources,omitempty"`
Environment
- Envoy Gateway:
v1.8.1
- Provider: Kubernetes
- Using
extensionManager.hooks.xdsTranslator + extensionManager.policyResources with a custom CRD.
Root cause (code references)
-
The target reference used for extension-server policies is LocalPolicyTargetReferenceWithSectionName, which has no namespace field — so it can only ever refer to an object in the policy's own namespace.
-
internal/gatewayapi/extensionserverpolicy.go — resolveExtServerPolicyGatewayTargetRef() hard-codes the Gateway lookup namespace to the policy's namespace:
func resolveExtServerPolicyGatewayTargetRef(policy *unstructured.Unstructured, target gwapiv1.LocalPolicyTargetReferenceWithSectionName, gateways map[types.NamespacedName]*policyGatewayTargetContext) *GatewayContext {
key := types.NamespacedName{
Name: string(target.Name),
Namespace: policy.GetNamespace(), // <-- always the policy's namespace
}
gateway, ok := gateways[key]
if !ok {
return nil // cross-namespace target => "no targets found for the policy"
}
return gateway.GatewayContext
}
internal/provider/kubernetes/controller.go — processExtensionServerPolicies() additionally requires a targetRef/targetRefs to be present at all, otherwise the policy is dropped:
_, foundTargetRef := policySpec["targetRef"]
_, foundTargetRefs := policySpec["targetRefs"]
if !foundTargetRef && !foundTargetRefs {
return fmt.Errorf("not a policy object - no targetRef or targetRefs found in %s.%s %s", ...)
}
Net effect: a policyResources policy must (a) carry a targetRef/targetRefs, and (b) live in the same namespace as its target Gateway. There is no way to reference a Gateway in another namespace.
Expected behavior
Allow an extension-server policy to attach to a Gateway in another namespace, gated by a ReferenceGrant (consistent with how Gateway API handles cross-namespace references elsewhere), e.g.:
- Support a namespaced target reference (e.g.
LocalPolicyTargetReferenceWithSectionName extended with an optional namespace, or accept NamespacedPolicyTargetReference) for extension-server policies, and
- Require a
ReferenceGrant in the Gateway's namespace granting from: <policy GVK/namespace> to: Gateway before honoring the cross-namespace attachment.
Proposed change
- In
resolveExtServerPolicyGatewayTargetRef, use the target's namespace when provided (falling back to policy.GetNamespace()), and enforce a ReferenceGrant check for cross-namespace targets.
- In
processExtensionServerPolicies, keep the targetRef presence check, but ensure cross-namespace targets are retained through translation so status can be computed.
- Update the
PolicyResources field docs and the extension-server policy documentation to describe the cross-namespace rules and required ReferenceGrant.
Steps to reproduce
- Configure
extensionManager.policyResources for a custom policy CRD and an xdsTranslator post hook.
- Create a Gateway
gw in namespace infra.
- Create the custom policy in namespace
app with a targetRefs entry {group: gateway.networking.k8s.io, kind: Gateway, name: gw}.
- Observe: the policy is treated as having no matching target ("no targets found for the policy"); it does not attach, and changes to it don't trigger re-translation.
- Move the policy to namespace
infra (same as the Gateway) → it attaches and re-translation works.
Would love to understand if there is a reason we dont have a cross-namespace support for extension-server Policy Resources.
Problem Statement
When a CRD is registered under
extensionManager.policyResources, Envoy Gateway treats it as a "directly attached" Gateway API policy and re-translates xDS on changes. However, an extension-server policy can only attach to a Gateway that lives in the same namespace as the policy. Targeting a Gateway in a different namespace silently fails to attach (and, for our extension, means the policy never drives re-translation).This is currently documented as a one-line limitation on the API field but not explained, and there is no supported mechanism (e.g.
ReferenceGrant) to opt into cross-namespace attachment.From
api/v1alpha1/envoygateway_types.go(ExtensionManager.PolicyResources):Environment
v1.8.1extensionManager.hooks.xdsTranslator+extensionManager.policyResourceswith a custom CRD.Root cause (code references)
The target reference used for extension-server policies is
LocalPolicyTargetReferenceWithSectionName, which has no namespace field — so it can only ever refer to an object in the policy's own namespace.internal/gatewayapi/extensionserverpolicy.go—resolveExtServerPolicyGatewayTargetRef()hard-codes the Gateway lookup namespace to the policy's namespace:internal/provider/kubernetes/controller.go—processExtensionServerPolicies()additionally requires atargetRef/targetRefsto be present at all, otherwise the policy is dropped:Net effect: a
policyResourcespolicy must (a) carry atargetRef/targetRefs, and (b) live in the same namespace as its target Gateway. There is no way to reference a Gateway in another namespace.Expected behavior
Allow an extension-server policy to attach to a Gateway in another namespace, gated by a
ReferenceGrant(consistent with how Gateway API handles cross-namespace references elsewhere), e.g.:LocalPolicyTargetReferenceWithSectionNameextended with an optionalnamespace, or acceptNamespacedPolicyTargetReference) for extension-server policies, andReferenceGrantin the Gateway's namespace grantingfrom: <policy GVK/namespace>to: Gatewaybefore honoring the cross-namespace attachment.Proposed change
resolveExtServerPolicyGatewayTargetRef, use the target's namespace when provided (falling back topolicy.GetNamespace()), and enforce aReferenceGrantcheck for cross-namespace targets.processExtensionServerPolicies, keep thetargetRefpresence check, but ensure cross-namespace targets are retained through translation so status can be computed.PolicyResourcesfield docs and the extension-server policy documentation to describe the cross-namespace rules and requiredReferenceGrant.Steps to reproduce
extensionManager.policyResourcesfor a custom policy CRD and anxdsTranslatorpost hook.gwin namespaceinfra.appwith atargetRefsentry{group: gateway.networking.k8s.io, kind: Gateway, name: gw}.infra(same as the Gateway) → it attaches and re-translation works.Would love to understand if there is a reason we dont have a cross-namespace support for extension-server Policy Resources.