Skip to content

Commit dd2ace6

Browse files
committed
Update Auth GEP with Implementable details
This commit adds the design rationale and API design for phase 1 of the Auth GEP, adding a Filter to HTTPRoute. Signed-off-by: Nick Young <[email protected]>
1 parent b4794ff commit dd2ace6

File tree

2 files changed

+334
-9
lines changed

2 files changed

+334
-9
lines changed

geps/gep-1494/index.md

Lines changed: 330 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# GEP-1494: HTTP Auth in Gateway API
22

33
* Issue: [#1494](https://github.com/kubernetes-sigs/gateway-api/issues/1494)
4-
* Status: Provisional
4+
* Status: Implementable
55

66
(See [status definitions](../overview.md#gep-states).)
77

@@ -130,20 +130,343 @@ From @ongy, some additional goals to keep in mind:
130130

131131
## API
132132

133-
(... details, can point to PR with changes)
133+
This GEP proposes a two-part solution to this problem:
134+
135+
* We introduce a new HTTPRoute Filter, `ExternalAuth`, that allows the
136+
specification of an external source to connect to using Envoy's `ext_auth` protocol.
137+
* We introduce a new Policy object that can be targeted at either the
138+
Gateway or HTTPRoute levels. In either of these cases, it _defaults_ the settings
139+
for the HTTPRoute Filter across all HTTPRoute matches that roll up to the object.
140+
141+
These two parts will be done in two separate changes - Filter first, then
142+
Policy after.
143+
144+
Both of these additions use the same underlying struct for the config, so that
145+
changes or additions in one place add them in the other as well.
146+
147+
This plan has some big things that need explaining before we get to the API details:
148+
149+
* Why a Filter plus Policy approach?
150+
* Why two changes?
151+
* Why Envoy's `ext_auth`?
152+
153+
### Why a Filter plus Policy approach?
154+
155+
We have two main requirements: Ana needs to be able to configure auth at least at
156+
the smallest possible scope, and Ana, Ian and Chihiro need to be able to configure
157+
defaults at larger scopes.
158+
159+
The smallest possible scope for this config is the HTTPRoute Rule level, where
160+
you can match a single set of matchers - like a path, or a path and header
161+
combination.
162+
163+
At this level, the inline tool we have available to perform changes is the HTTPRoute
164+
Filter, which also has the property that it's designed to _filter_ requests. This
165+
matches the overall pattern here, which is to _filter_ some requests, allowing
166+
or denying them based on the presence of Authentication and the passing of
167+
Authorization checks.
168+
169+
A Policy _can_ be targeted at this level, using the Route rule as a `sectionName`,
170+
but that leaves aside that Filters are exactly designed to handle this use case.
171+
172+
Policy attachment includes defaulting fields like Filters in its scope already,
173+
so we are allowed to use a combination in this way.
174+
175+
Using a Filter also has the advantage that, at the tightest possible scope (the
176+
object being defaulted) you can _explicitly_ override any configured defaults.
177+
178+
Using a Filter also includes ordering (because Filters are an ordered list),
179+
although this exact behavior is currently underspecified. This change will also
180+
need to clarify. Ordering is particularly important for Auth use cases, because
181+
sometimes Auth will expect certain properties that may need to be introduced
182+
by things like header modification.
183+
184+
Lastly, using a Filter means that, for the simplest possible case, where Ana
185+
wants to enable Auth* for a single path, then there is only a single object to
186+
edit, and a single place to configure.
187+
188+
Using a Policy for the simplest case immediately brings in all the discovery
189+
problems that Policy entails.
190+
191+
There are two important caveats here that must be addressed, however:
192+
* Firstly, whatever is in the filter either must be accepted, or the rule
193+
is not accepted. Overrides can't make explicit config not work - that would
194+
violate one of the key declarative principles, that what is requested in the
195+
spec is either what ends up in the state, or that config is rejected.
196+
* Secondly, filter ordering is particularly important for Auth use cases, so we
197+
must ensure that when we add Policy defaulting we have a way to indicate at
198+
what position in a filter list the Auth policy should fit.
199+
200+
### Why two phases?
201+
202+
In short: In the interest of getting something, even if incomplete, into our
203+
user's hands as quickly as possible.
204+
205+
Policy design is complex, and needs to be done carefully. Doing a first
206+
pass using only a Filter to get the basic config correct while we discuss
207+
how to make the Policy handling work means that we can get some work out to the
208+
community without needing to complete the whole design.
209+
210+
In particular, the design for the Filter plus Policy will need to answer at
211+
least the following questions:
212+
213+
* How to set where in a list of Filters a defaulted Auth filter sits;
214+
and what happens if no Filters are specified in a HTTPRoute? Does it go first,
215+
last, or should there be a way to specify the order?
216+
* What Policy types are possible? Defaults? (Definitely useful for setting a
217+
baseline expectation for a cluster, which is desirable for security constructs
218+
like Auth) Overrides? (Also very useful for ensuring that exceptions meet
219+
certain requirements - like only allowing the disabling of Auth on `/healthz`
220+
endpoints or similar use cases.)
221+
* Should Policy have a way to describe rules around when it should take effect?
222+
That's in addition to the usual hierarchical rules, should the Policy have ways
223+
to include or exclude specific matches? This would require agreement in the
224+
Policy Attachment spec as well.
225+
226+
All of these changes have costs in complexity and troubleshooting difficulty, so
227+
it's important to ensure that the design consciously makes these tradeoffs.
228+
229+
In particular, the last two items in the above list seem likely to require a fair
230+
amount of discussion, and including a Policy in the initial release of this
231+
seems likely to make this change miss its current release window.
232+
233+
234+
### Why Envoy's ext_auth?
235+
236+
#### What is ext_auth?
237+
238+
Envoy's External Authorization filter is a filter that calls out to an authorization
239+
service to check if the incoming request is authorized or not. Note that, in
240+
order to check _authorization_, it must also be able to determine _authentication_ -
241+
this is one of the reasons why we've chosen this approach.
242+
243+
Envoy's implementation of this filter allows both a
244+
[gRPC, protobuf API](https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/http/ext_authz/v3/ext_authz.proto)
245+
and configuration of a HTTP based API (which, as it's not defined using a
246+
specification like protobuf, requires more configuration).
247+
248+
The important thing to remember here is that the actual authentication and
249+
authorization processed are delegated to the authorization service - which is
250+
why the ext_auth approach allows handling many Auth methods - most of the
251+
work is performed by external services which implement various methods (like
252+
Basic Auth, OAuth, JWT validation, etc)
253+
254+
#### Why use it over other options?
255+
256+
The community discussed Auth extensively in-person at Kubecon London in early 2025,
257+
and got broad agreement from multiple dataplanes that:
258+
259+
* something like ext_auth was a good idea, because it's flexible and allows the
260+
implementation of many types of Auth without specific protocol implementation
261+
in upstream
262+
* Envoy's ext_auth protocol has no major problems that would stop us using it
263+
* Envoy-based implementations mostly already have support for it
264+
265+
At that meeting, those present agreed that ext_auth was a good place to start.
266+
267+
Most non-Envoy dataplanes also already have similar methods, so the maintainers
268+
of projects using other dataplanes were okay with this idea.
269+
270+
The alternative here would be to add a Filter type _per auth method_, which, given
271+
the large number of options, could quickly become very complex.
272+
273+
This GEP is, however, explicitly _not_ ruling out the possibility of adding
274+
specific Filters for specific Auth methods in the future, if users of this API
275+
find the overhead of running a compatible implementation to be too much.
276+
277+
### API Design
278+
279+
#### Phase 1: Adding a Filter
280+
281+
This config mainly takes inspiration from Envoy's ext_auth filter config, while
282+
also trying to maintain compatibility with other HTTP methods.
283+
284+
This design is also trying to start with a minimum feature set, and add things
285+
as required, rather than adding everything configurable in all implementations
286+
immediately.
287+
288+
There is some difference between data planes, based on the links above, but
289+
these fields should be broadly supportable across all the listed implementations.
290+
291+
Some design comments are included inline.
292+
293+
##### Go Structs
294+
295+
```go
296+
// HTTPExtAuthFilter defines a filter that modifies requests by sending
297+
// request details to an external authorization server.
298+
//
299+
// Support: Extended
300+
// Feature Name: HTTPRouteExtAuth
301+
type HTTPExtAuthFilter struct {
302+
303+
// ExtAuthProtocol describes which protocol to use when communicating with an
304+
// ext_auth authorization server.
305+
//
306+
// When this is set to GRPC, each backend must use the Envoy ext_auth protocol
307+
// on the port specified in `backendRefs`. Requests and responses are defined
308+
// in the protobufs: <link>
309+
//
310+
// When this is set to HTTP, each backend must respond with a status code in
311+
// the `2xx` range on a successful authorization. Any other code is considered
312+
// an authorization failure.
313+
//
314+
// +unionDiscriminator
315+
// +kubebuilder:validation:Enum=HTTP;GRPC
316+
ExtAuthProtocol string `json:"protocol"`
317+
318+
// BackendRefs is a list of references to backends to send authorization
319+
// requests to.
320+
//
321+
// If this list contains more than one entry, authorization requests must
322+
// be load-balanced across all the backends equally.
323+
//
324+
// The backends must speak the selected protocol (GRPC or HTTP) on the
325+
// referenced port.
326+
//
327+
// If the backend service requires TLS, use BackendTLSPolicy to tell the
328+
// implementation to supply the TLS details to be used to connect to that
329+
// backend.
330+
//
331+
// +kubebuilder:validation:MinLength=1
332+
// +kubebuilder:validation:MaxLength=8
333+
BackendRefs []BackendObjectReference `json:"backendRefs"`
334+
335+
// GRPCAuthConfig contains configuration for communication with ext_auth
336+
// protocol-speaking backends.
337+
//
338+
// If unset, implementations must assume the default behavior for each
339+
// included field is intended.
340+
//
341+
// +optional
342+
GRPCAuthConfig *GRPCAuthConfig `json:"grpc,omitempty"`
343+
344+
// HTTPAuthConfig contains configuration for communication with HTTP-speaking
345+
// backends.
346+
//
347+
// If unset, implementations must assume the default behavior for each
348+
// included field is intended.
349+
//
350+
// +optional
351+
HTTPAuthConfig *HTTPAuthConfig `json:"http,omitempty"`
352+
353+
// ForwardBody controls if requests to the authorization server should include
354+
// the body of the client request; and if so, how big that body is allowed
355+
// to be.
356+
//
357+
// Feature Name: HTTPRouteExtAuthForwardBody
358+
//
359+
// GEP Review Notes:
360+
// Both Envoy and Traefik show support for this feature, but HAProxy and
361+
// ingress-nginx do not. So this has a separate feature flag for it.
362+
//
363+
// +optional
364+
ForwardBody *ForwardBodyConfig `json:"body,omitempty"`
365+
}
366+
367+
// GRPCAuthConfig contains configuration for communication with ext_auth
368+
// protocol-speaking backends.
369+
type GRPCAuthConfig struct {
370+
371+
// AllowedRequestHeaders specifies what headers from the client request
372+
// will be sent to the authorization server.
373+
//
374+
// If this list is empty, then all headers must be sent.
375+
//
376+
// +optional
377+
// +kubebuilder:validation:MaxLength=64
378+
AllowedRequestHeaders []string `json:"allowedHeaders,omitempty"`
379+
}
380+
381+
// HTTPAuthConfig contains configuration for communication with HTTP-speaking
382+
// backends.
383+
type HTTPAuthConfig struct {
384+
// Path sets the prefix that paths from the client request will have added
385+
// when forwarrded to the authorization server.
386+
//
387+
// When empty or unspecified, no prefix is added.
388+
// +optional
389+
Path string `json:"path,omitempty"`
390+
391+
// AllowedRequestHeaders specifies what additional headers from the client request
392+
// will be sent to the authorization server.
393+
//
394+
// The following headers must always be sent to the authorization server,
395+
// regardless of this setting:
396+
//
397+
// * `Host`
398+
// * `Method`
399+
// * `Path`
400+
// * `Content-Length`
401+
// * `Authorization`
402+
//
403+
// If this list is empty, then only those headers must be sent.
404+
//
405+
// Nick's GEP Review Notes:
406+
// Envoy actually does not allow sending all headers to HTTP authorization
407+
// servers. If the equivalent Envoy field is unspecified, then only
408+
// `Host`, `Method`, `Path`, `Content-Length` and `Authorization` are included.
409+
// This is kept for backwards compatibility in Envoy.
410+
//
411+
// Traefik includes _all_ headers when the equivalent setting is empty.
412+
//
413+
// This definition is the only compromise I can see between the two; Envoy
414+
// can set this to empty, and Traefik can always explicitly specify those headers
415+
// at a minimum.
416+
//
417+
// +optional
418+
// +kubebuilder:validation:MaxLength=64
419+
AllowedRequestHeaders []string `json:"allowedHeaders,omitempty"`
420+
421+
// AllowedResponseHeaders specifies what headers from the authorization response
422+
// will be copied into the request to the backend.
423+
//
424+
// If this list is empty, then all headers from the authorization server
425+
// except Authority or Host must be copied.
426+
//
427+
// +optional
428+
// +kubebuilder:validation:MaxLength=64
429+
AllowedResponseHeaders []string `json:"allowedResponseHeaders,omitempty"`
430+
431+
}
432+
433+
// ForwardBody configures if requests to the authorization server should include
434+
// the body of the client request; and if so, how big that body is allowed
435+
// to be.
436+
type ForwardBodyConfig struct {
437+
438+
// ForwardBody specifies if the body should be forwarded to the authorization
439+
// server. If not specified, the body will not be forwarded.
440+
ForwardBody bool `json:"forward,omitempty"`
441+
// MaxSize specifies how large the largest body that will be buffered and
442+
// sent to the authorization
443+
MaxSize uint16 `json:"maxSize,omitempty"`
444+
}
445+
446+
```
447+
#### YAML Examples
448+
449+
Coming soon.
450+
451+
#### Phase 2: Adding more complex configuration with Policy
452+
453+
This phase is currently undefined until we reach agreement on the Filter + Policy
454+
approach.
134455

135456
## Conformance Details
136457

137458
(from https://github.com/kubernetes-sigs/gateway-api/blob/main/geps/gep-2162/index.md#standardize-features-and-conformance-tests-names)
138459

139460
#### Feature Names
140461

141-
Every feature should:
462+
For this feature as a base:
463+
464+
`HTTPRouteExtAuth`
465+
466+
For forwarding the body of the client request to the authorization server
467+
468+
`HTTPRouteExtAuthForwardBody`
142469

143-
1. Start with the resource name. i.e HTTPRouteXXX
144-
2. Follow the PascalCase convention. Note that the resource name in the string should come as is and not be converted to PascalCase, i.e HTTPRoutePortRedirect and not HttpRoutePortRedirect.
145-
3. Not exceed 128 characters.
146-
4. Contain only letters and numbers
147470

148471
### Conformance tests
149472

geps/gep-1494/metadata.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ apiVersion: internal.gateway.networking.k8s.io/v1alpha1
22
kind: GEPDetails
33
number: 1494
44
name: HTTP Auth in Gateway API
5-
status: Provisional
5+
status: Implementable
66
# Any authors who contribute to the GEP in any way should be listed here using
77
# their GitHub handle.
88
authors:
@@ -16,4 +16,6 @@ references: {}
1616
featureNames: {}
1717
# changelog is a list of hyperlinks to PRs that make changes to the GEP, in
1818
# ascending date order.
19-
changelog: {}
19+
changelog:
20+
- https://github.com/kubernetes-sigs/gateway-api/pull/3500
21+
- https://github.com/kubernetes-sigs/gateway-api/pull/3884

0 commit comments

Comments
 (0)