Skip to content

ADR-0026: Policies for Gateway HTTP and gRPC rules

Status

Accepted.

Context

The native Ingress annotations configure authentication, fleet-wide rate limits, WAF inspection, and shared caching. Gateway routes need the same enforcement without copying Ingress annotations or adding resource-specific Go code.

Route names alone cannot identify enforcement state. An HTTPRoute and GRPCRoute can share a namespace and name, and rules within either route can require different policies. Backend names must also distinguish the target namespace.

Decision

The chart defines a namespaced HAProxyRoutePolicy resource. An HTTPRoute or GRPCRoute rule attaches one policy through an ExtensionRef filter with group haproxy-haptic.org. The policy must be in the route's namespace. Credential Secrets must be in the policy's namespace. Backend-level attachments and multiple policy filters on one rule are rejected; there is no implicit merge or precedence.

Credential Secrets must be immutable. Route and policy admission validate their contents before attachment, and Kubernetes rejects subsequent data updates. Rotation creates a new immutable Secret and changes the policy reference. Every reconciliation rechecks immutability and content, including after deletion and recreation under the same name.

This replaces the unreleased broad Secret webhook: protecting credentials no longer depends on a controller being available when a Secret changes. The policy change still validates the complete affected route set before acceptance. Missing, mutable, or malformed credentials fail closed during reconciliation. Mutable frontend and operational TLS Secrets retain their existing validation and renewal paths. Helm can write its release metadata while repairing a stopped controller.

Gateway WAF catalogs follow the same reference contract. waf.catalogRef selects an immutable ConfigMap in the policy's namespace, with a key defaulting to policies.yaml. Rotation creates a new immutable catalog and updates the policy reference. Each catalog version has a distinct enforcement identity, so old and new versions can coexist. Namespace and cluster policy limits count distinct catalog entries, including the existing Ingress catalogs; sharing a reference doesn't consume the budget twice. Inline administrator policies remain available; external trusted catalogs must also be immutable when used by Gateway policies.

Kubernetes rejects catalog data edits even while the controller is stopped. Route and policy admission validate new selections, and reconciliation rejects missing, mutable, or invalid replacements under an existing name. This replaces the unreleased ConfigMap webhook without exempting a mutable policy dependency. Admission stays in the controller pods. Helm can write its operational ConfigMaps and Secrets before running repair hooks; policy reference changes still fail closed while admission is unavailable. Missing or invalid trusted catalogs also fail closed for their Ingress consumers instead of aborting the entire render. Admission continues to reject invalid catalogs and unresolved default policies.

A policy can combine JWT and API-key authentication, a shared rate limit, a WAF catalog selection, and HTTP caching. Both authentication checks apply when both are configured. WAF selection keeps the catalog's enforcement and tenant limits. GRPCRoute rejects caching and WAF policies that buffer request bodies.

Rate limits apply across the HAProxy fleet. Reusing a policy shares its budget across attached rules; source IP or authenticated consumer partitions the budget. Cache entries remain separated by the selected origin and route rule. Each key component is encoded before joining it to avoid delimiter collisions. Authenticated cached responses also require a verified consumer identity in the cache key.

The existing engines accept normalized settings and route records through a shared chart library. Ingress annotation parsing remains in its annotation library; Gateway attachment and status handling live in the gateway-policies library, loaded with Gateway support. Neither adapter constructs fake resources for the other. Go continues to discover schemas and watch resources through the existing generic interfaces.

Runtime policy lookups use an identity containing route kind, namespace, name, and rule index. Human-readable access-log resource fields retain namespace/name. Policy identity and route identity are separate: a shared rate budget belongs to the policy, while filter selection and cache origin belong to the route rule.

Missing or invalid attached policies fail closed for the affected rule. Admission rejects changes that introduce invalid attachments or dependencies. Reconciliation must also handle invalid objects that predate admission and dependencies removed after attachment; these cannot block unrelated routes or retain stale permission. Route status reports unresolved references and unsupported configurations. A policy update revalidates every attached rule, including gRPC restrictions.

Verification

Native chart tests cover schema absence, namespace ownership, same-name route kinds, multiple rules, missing dependencies, policy updates and deletion, and disabled feature dependencies. Live tests verify credential rejection, budget sharing across replicas, WAF rejection, cache isolation, and recovery. Existing Ingress behavior and Gateway conformance remain required gates.

Found a problem on this page? Report it or edit the page with the pencil icon above the title.