-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
*No* merging of policies #10
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,267 @@ | ||
# RFC A single Policy scoped to a HTTPRouteRule | ||
|
||
- Feature Name: Policy targeting HTTPRoute and HTTPRouteRules | ||
- Start Date: 2022-11-09 | ||
- RFC PR: [Kuadrant/architecture#0000](https://github.com/Kuadrant/architecture/pull/0000) | ||
- Issue tracking: [Kuadrant/architecture#0000](https://github.com/Kuadrant/architecture/issues/0000) | ||
|
||
# Summary | ||
[summary]: #summary | ||
|
||
Currently both `RateLimitPolicy` and `AuthPolicy` allow for a policy to only affect a _subset_ of the | ||
traffic on a targeted `HTTPRoute`. This proposal aims at simplifying the scope of a policy to target | ||
_all_ the traffic of a route or the traffic specified by a specific `HTTPRouteRule`'s `HTTPRouteMatch`. | ||
|
||
# Motivation | ||
[motivation]: #motivation | ||
|
||
As per the specification, a given resource will always need to support multiple policies targeting it. | ||
Even if we only support a single e.g. `RateLimitPolicy` per resource, there will alway be a possibility | ||
for another policy being defined else where in that resource's hierarchy: e.g. _policy1_ targeting the | ||
`Gateway`, while _policy2_ targeting the `HTTPRoute` being attached to the `Gateway`. Until there is an | ||
`HTTPRoute` tho, _policy1_ will have no semantic, as there is no traffic routed, so no policy for | ||
Kuadrant to enforce, as it operates on the data plane. When an `HTTPRoute` is attached to the `Gateway` | ||
is the point when that _policy1_ becomes applicable. If that `HTTPRoute` also defines a `RateLimitPolicy`, | ||
only one will be enforced, according to the `default` & `override` rules of the specification. | ||
|
||
The idea is to keep things simple (if only for now), so that when traffic is routed through an | ||
`HTTPRoute` only one policy of a `kind` becomes applicable and applies to the whole traffic of that route | ||
(or a subset thereof, see below). | ||
|
||
The user might want to qualify traffic to only a specific `HTTPRouteRule` of a given `HTTPRoute`. That would be | ||
permitted for as long as the `Policy` reflects the exact same set of `HTTPRouteMatch` as the `HTTPRouteRule` defines. | ||
If two policies target the same `HTTPRouteRule`, one would take precedence over the other according to | ||
the Gateway API's merging rules. | ||
|
||
With these rules, Kuadrant can "easily" identify what policies apply to an `HTTPRoute`, possibly | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. one thing that occurred to me might be to actually reflect this in the status of the HTTPRoute in a condition |
||
reflecting the `status` of the policy on a per `HTTPRouteRule` level, for when one get "shadowed". | ||
|
||
# Guide-level explanation | ||
[guide-level-explanation]: #guide-level-explanation | ||
|
||
Traffic to your service is ultimately routed when an `HTTPRoute` binds at least one `Gateway`, thru | ||
the | ||
[`parentRefs`](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.ParentReference), | ||
and wires traffic to the | ||
[`backendRef`](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPBackendRef), | ||
i.e. your service, according to the | ||
[`HTTPRouteMatch`](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteMatch) | ||
of a | ||
[`HTTPRouteRule`](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteRule) | ||
define on that `HTTPRoute`. All the traffic matched by _any_ `HTTPRouteRule` of a given `HTTPRoute` | ||
will be affected by which ever `RateLimitPolicy` or `AuthPolicy` you attach to it, unless the _Policy_ references a | ||
specific `HTTPRouteRule` by duplicating all of its `HTTPRouteMatch`es. | ||
|
||
Any Kuadrant _Policy_ will apply to the traffic that is ultimately routed through because of a | ||
`HTTPRouteRule`, which binds incoming traffic to a backend service and as such defines a route. Each | ||
_Policy_ has to be a complete specification to be accepted by Kuadrant. For any given route, Kuadrant | ||
resolves the _Policy_ to enforce based on the `default` & `override` specification of the individual | ||
_Policies_. | ||
|
||
```yaml | ||
kind: PaintPolicy | ||
spec: | ||
override: | ||
color: blue | ||
targetRef: | ||
kind: Gateway | ||
name: my-gw | ||
``` | ||
|
||
This `PaintPolicy` defines `color` as `blue`. Since there is no other `PaintPolicy` attached to the | ||
`my-gw` `Gateway`, its namespace and so forth, see [the specification for the | ||
hierarchy](https://gateway-api.sigs.k8s.io/references/policy-attachment/#hierarchy), the `override` value | ||
for `color` precedes and as such `blue` will be enforced for _any_ `HTTPRoute` that ends up being wired | ||
to this `Gateway`. | ||
|
||
That policy tho has no traffic to police yet. Unless an `HTTPRoute` binds that `Gateway` to a | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 |
||
`Backend`, no traffic can exist and as such can't be applied to by the policy. As such Kuadrant will | ||
_not_ create any further of its custom resources (`CR`) until that happens. | ||
|
||
Let's change our `PaintPolicy` to use a `default` instead and show how this can then now the behavior can | ||
be changed by other another `PaintPolicy` attached further down the [hierarchy of network | ||
resources](https://gateway-api.sigs.k8s.io/references/policy-attachment/#hierarchy): | ||
|
||
```yaml | ||
kind: PaintPolicy | ||
spec: | ||
default: | ||
color: blue | ||
targetRef: | ||
kind: Gateway | ||
name: my-gw | ||
``` | ||
|
||
Say, we now have a `Backend` (e.g. `foo`) wired to the `my-gw` using some `HTTPRoute` (e.g. | ||
`foo-route`), none of which have yet any `PaintPolicy` attached. Yet, since the `Gateway` used as a | ||
policy defined, Kuadrant will create a `PaintLimit` `CR` and eventually configure its service to apply | ||
the policy for traffic routed to that `foo` `Backend`. The `PaintPolicy` | ||
to eventually apply will be resolved using the network resource hierarchy. The | ||
traffic "painted" will be the one matching the union of all the `HTTPRouteMatch` of each | ||
`HTTPRouteRule` as defined in that `HTTPRoute`. | ||
|
||
```yaml | ||
apiVersion: gateway.networking.k8s.io/v1beta1 | ||
kind: HTTPRoute | ||
metadata: | ||
name: foo-route | ||
spec: | ||
parentRefs: | ||
- name: my-gw | ||
hostnames: | ||
- "example.com" | ||
rules: | ||
- matches: | ||
- path: | ||
type: PathPrefix | ||
value: /foo | ||
backendRefs: | ||
- name: foo | ||
port: 8080 | ||
``` | ||
|
||
With `foo-route` declaring a single hostname (`example.com`) and a single `HTTPRouteMatch` (anything | ||
starting with `/foo`), the policy will apply to the same traffic: `example.com/foo*`, so that `foo` 's | ||
traffic to port `8080` will be painted blue, as specified by the `default` section of our `PaintPolicy`. | ||
|
||
In order to change that color, we'd need to define another `PaintPolicy` that defines a `default` and | ||
targets our `foo-route`: | ||
|
||
```yaml | ||
kind: PaintPolicy | ||
spec: | ||
default: | ||
color: green | ||
targetRef: | ||
kind: HTTPRoute | ||
name: foo-route | ||
``` | ||
|
||
Attaching the `PaintPolicy` above to our `HTTPRoute` will _shadow_ the `PaintPolicy` attached to the | ||
`Gateway`. It is important to note that there is no way for the policy to discriminate the traffic it | ||
applies to at this stage. All traffic to `foo-route` is now painted `green`, while any further routes | ||
attached to `my-gw` would still be painted `blue`. | ||
|
||
>**Note:** The status block of a policy should reflect whether it is shadowing or being shadowed by other | ||
>policies at that level of the hierarchy. | ||
|
||
Which would effectively disable all rate limiting traffic to the `foo-route` and ultimately the `foo` | ||
`Backend`. | ||
|
||
Using another example, let's consider a single `HTTPRoute` with two `HTTPRouteRule`s: | ||
|
||
```yaml | ||
apiVersion: gateway.networking.k8s.io/v1beta1 | ||
kind: HTTPRoute | ||
metadata: | ||
name: bar-route | ||
spec: | ||
parentRefs: | ||
- name: my-gw | ||
hostnames: | ||
- "bar.example.com" | ||
rules: | ||
- matches: | ||
- path: | ||
type: PathPrefix | ||
value: /login | ||
backendRefs: | ||
- name: bar-svc | ||
port: 8081 | ||
- matches: | ||
- headers: | ||
- type: Exact | ||
name: env | ||
value: production | ||
backendRefs: | ||
- name: bar-svc | ||
port: 8080 | ||
``` | ||
|
||
It is important to consider that only _one_ rule will ever be match for any given requests. It makes for | ||
a simpler model to have only one _Policy_ to also only ever being matched, aligning the both behaviors. | ||
To apply a policy on the first rule only, the user needs to duplicate that match on the policy targeting | ||
`bar-route`: | ||
|
||
```yaml | ||
kind: PaintPolicy | ||
spec: | ||
default: | ||
color: red | ||
targetRef: | ||
kind: HTTPRoute | ||
name: foo-route | ||
rules: | ||
- matches: | ||
- path: | ||
type: PathPrefix | ||
value: /login | ||
``` | ||
|
||
Hereby we are coloring the traffic to our `/login` on port `8081` to be `red`. That coloring _will not_ | ||
apply to other matches. So that traffic to `bar-svc` on port `8080` would be colored `green` as per the | ||
`PaintPolicy` attached to `my-gw`. | ||
|
||
The resolving of _Policies_ to enforce occurs at the `HTTPRoute` or within individual `HTTPRouteRule` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like that we can describe this relatively simply. That said I thnk we would want a good range of "example" policies to cover common use cases similar to how it is done for https://github.com/kubernetes-sigs/gateway-api/tree/main/examples/standard |
||
within a route. The merging strategy will resolve the individual `HTTPRouteRule` all the policies boil | ||
down to, stack them according to the `default` and `override` and appy the most specific one according to | ||
the Gateway API Policy Attachment hierarchy. Targeting a complete `HTTPRoute` is the equivalent of | ||
targeting each indidividual `HTTPRouteRule`s that composes the route. | ||
|
||
## What actual use-cases does this address? | ||
|
||
- An admin restricts all traffic using a `default` at the gateway to force application developers to | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It also ensure no unlimited endpoints are exposed |
||
"think" about how to authorize access to their services and have them come up with sensible `default` | ||
on their `HTTPRoute`s | ||
- An application developer protects its service by some rate limit, while an cluster admin could come | ||
and use an `override` to mitigate a DDoS. | ||
|
||
# Reference-level explanation | ||
[reference-level-explanation]: #reference-level-explanation | ||
|
||
So there are a couple of things to consider here: | ||
|
||
- status reporting on "not yet" existing `HTTPRouteRule`, e.g. a Policy declares a set of `matches` that | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I think reporting status in the RLP and in the HTTPRoute / Gateway is worth consideration |
||
don't (yet?) exist. The policy must _not_ be enforced... yet. But could eventually. This needs proper | ||
reporting. | ||
|
||
# Drawbacks | ||
[drawbacks]: #drawbacks | ||
|
||
- The use cases this approach doesn't address… | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think linking to kubernetes-sigs/gateway-api#1489 would make sense here also |
||
- Not very DRY... the need to keep `HTTPRouteRule`s in sync between the Policy and the `HTTPRoute` is | ||
suboptimal. | ||
|
||
# Rationale and alternatives | ||
[rationale-and-alternatives]: #rationale-and-alternatives | ||
|
||
- [`HTTPRouteFilter`](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) | ||
would be an alternative and provide finer grained control over how a policy applies, i.e. at the | ||
`HTTPRouteRule` level, or even on a per `HTTPBackendRef` within an `HTTPRouteRule`. But today there is | ||
no way (that I know of) for us to add a `HTTPRouteFilterType` to the list of supported | ||
`HTTPRouteFilter` by a Gateway API implementor. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if there was a way to do this (for example if we proposed a GEP around third party filters), would this compliment the policy side? Does a filter at the gateway level make sense? I don't see how filters would solve the use case of wanting to set a default limit for all new endpoints |
||
|
||
# Prior art | ||
[prior-art]: #prior-art | ||
|
||
I don't know of prior art per se, _but_ the envoy gateway project, if only for rate limiting, seems to | ||
[avoid policy attachment](https://github.com/envoyproxy/gateway/issues/670#issuecomment-1299114982), but | ||
they are an implementor and as such can extend the | ||
[`HTTPRouteFilter`](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.HTTPRouteFilter) | ||
types. This proposed approach if actually trying to enable achieving the same control `HTTPRouteFilter` | ||
gives, while using _Policy Attachement_ and targeting a specific `HTTPRouteRule` within a `HTTPRoute`. | ||
|
||
# Unresolved questions | ||
[unresolved-questions]: #unresolved-questions | ||
|
||
- What use cases are we making "overly" complex, because of the duplication of `HTTPRoute`'s | ||
`HTTPRouteMatch` ? | ||
- Is it fine for the `matches` section to be at the same level as `default`, `override` and `targetRef`? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the fact it is an optional complexity is the perfect balance at least until we hear otherwise via new use cases. You have the "target all of a resource by default" and given a way to cover more advanced use cases and targeting. I also like the fact that the default section stays focused on the actual domain (RL) and leaves the targeting as a separate piece |
||
That bit is unclear to me [from the | ||
specification](https://gateway-api.sigs.k8s.io/references/policy-attachment/#policy-boilerplate). | ||
|
||
# Future possibilities | ||
[future-possibilities]: #future-possibilities | ||
|
||
- Selectors for `HTTPRoute` on which a policy should be "pushed down" | ||
- Introduce partial _policies_ and merging of them, rather than having them being complete specs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 to this description, clear and concise