Skip to content
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

envoy: use upstream JWT filter instead of the custom Istio extension #47407

Merged
merged 24 commits into from
Nov 20, 2023

Conversation

kyessenov
Copy link
Contributor

@kyessenov kyessenov commented Oct 17, 2023

Change-Id: I1a34a470f9770e1c7990e12a8370ee93fb297279

Modifies xDS to use the upstream JWT filter from Envoy instead of the extra Istio istio_authn HTTP filter for JWT matching and JWT policy evaluation. It is functionally equivalent with the main difference being that the space-delimited claims must be explicitly opted-in. The default space-delimited claims are scope and permission.

Issue: envoyproxy/envoy#29681

Change-Id: I1a34a470f9770e1c7990e12a8370ee93fb297279
Signed-off-by: Kuat Yessenov <kuat@google.com>
@kyessenov kyessenov requested a review from a team as a code owner October 17, 2023 19:30
@istio-testing istio-testing added the size/S Denotes a PR that changes 10-29 lines, ignoring generated files. label Oct 17, 2023
Change-Id: I99c29a3b28ffad9ae4125b2ea9da1d00b0430044
Signed-off-by: Kuat Yessenov <kuat@google.com>
@istio-testing istio-testing added size/M Denotes a PR that changes 30-99 lines, ignoring generated files. and removed size/S Denotes a PR that changes 10-29 lines, ignoring generated files. labels Oct 17, 2023
Change-Id: I2943e8a1fe415c7fa3558a903d374413a7e3a6da
Signed-off-by: Kuat Yessenov <kuat@google.com>
@istio-testing istio-testing added size/S Denotes a PR that changes 10-29 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Oct 17, 2023
Change-Id: I87ef250fe59af07aa43ae0684fe944dc446f5713
Signed-off-by: Kuat Yessenov <kuat@google.com>
@kyessenov kyessenov requested a review from a team as a code owner October 17, 2023 21:02
Change-Id: Id46d841ab58abfcb619d4c15df23604e2aafd26e
Change-Id: I243387881193c0676796472c30d0bcf07ca8c047
Signed-off-by: Kuat Yessenov <kuat@google.com>
@istio-testing istio-testing added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/S Denotes a PR that changes 10-29 lines, ignoring generated files. labels Oct 23, 2023
Change-Id: Ie69f3d0728e9b4c48c9d6807e67075a904066d66
Change-Id: I0e684914d85f6fb5edebd92127e0426f5b05b4c3
Signed-off-by: Kuat Yessenov <kuat@google.com>
Change-Id: Ic5dabdc5cd6681899e251eaa89f1c221a9a285a7
Signed-off-by: Kuat Yessenov <kuat@google.com>
Change-Id: Ia1bba5731a98d8841adbe99ae1d1518cbafa852a
Signed-off-by: Kuat Yessenov <kuat@google.com>
Change-Id: I0695b5c1aefec0198886d0068cc3667c8c3225e1
Signed-off-by: Kuat Yessenov <kuat@google.com>
Change-Id: I31b1278dbeba17203766cade3f94c3003d366a9f
Signed-off-by: Kuat Yessenov <kuat@google.com>
Change-Id: Ic1445984457022cf6099a3df43ad79c9f95b1e4d
Signed-off-by: Kuat Yessenov <kuat@google.com>
@kyessenov kyessenov requested review from a team as code owners November 8, 2023 18:19
Change-Id: Iaac6c9912d4d20851e524075f899e90827bea118
Signed-off-by: Kuat Yessenov <kuat@google.com>
@kyessenov kyessenov changed the title test: remove istio_authn HTTP filter envoy: use upstream JWT filter instead of the custom Istio extension Nov 8, 2023
@@ -270,7 +270,7 @@ func buildRBAC(node *model.Proxy, push *model.PushContext, suffix string, contex
for _, policy := range policies {
for i, rule := range policy.Spec.Rules {
name := fmt.Sprintf("%s-%s-%d", policy.Namespace, policy.Name, i)
m, err := authzmodel.New(rule)
m, err := authzmodel.New(rule, true)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does grpc support this filter before? I am concerned about the compatibility here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gRPC doesn't support it either way. Any value here would not work for JWT.

@@ -32,7 +32,7 @@ type PolicyApplier interface {

// JwtFilter returns the JWT HTTP filter to enforce the underlying authentication policy.
// It may return nil, if no JWT validation is needed.
JwtFilter() *hcm.HttpFilter
JwtFilter(useExtendedJwt, clearRouteCache bool) *hcm.HttpFilter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clearRouteCache looks weired

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestions? That's what xDS field is called.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually i am not familiar with jwt clear cache, will take a look though

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is to clear the cache used for making route decision, right? @kyessenov

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's to re-calculate the route selection

@@ -99,8 +99,15 @@ func (b *Builder) BuildHTTP(class networking.ListenerClass) []*hcm.HttpFilter {
// Only applies to inbound and gateways
return nil
}
if b.proxy.SupportsEnvoyExtendedJwt() {
filter := b.applier.JwtFilter(true, b.proxy.Type != model.SidecarProxy)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we clear cache only for no sidecar? can we always set it true

And actually envoy only clear when ClearRouteCache = true and claim_to_headers added

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It only makes sense on gateways because there are routes there. Inbound listener has no routes, see https://istio.io/latest/docs/tasks/security/authentication/jwt-route/ disclaimer at the top.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IC,i mean once we add another type, this could be broken. Is there any side-effect if we set it true for sidecar

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it causes overhead to reset a cache. It is also backwards incompatible since other filters might affect the route as well.

Change-Id: I67743d13a0670fca111cfe8dd923bc3f6906625a
Change-Id: I31a7035c45b2647379e365b72f4d5ef4cf3db6c3
Signed-off-by: Kuat Yessenov <kuat@google.com>
@kyessenov
Copy link
Contributor Author

Gentle ping.

Copy link
Member

@zirain zirain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overall lgtm

if forTCP {
return nil, fmt.Errorf("%q is HTTP only", key)
}

if rpg.useExtendedJwt {
iss, sub, _ := strings.Cut(value, "/")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should probably reject if there is no / in it here, and add a warning into the webhook if a user does this?

Copy link
Contributor Author

@kyessenov kyessenov Nov 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it was valid to have something like a* which matches iss with prefix a and any sub. Let me update it.

}
im := MetadataMatcherForJWTClaims([]string{"iss"}, matcher.StringMatcher(iss), true)
sm := MetadataMatcherForJWTClaims([]string{"sub"}, matcher.StringMatcher(sub), true)
return principalAnd([]*rbacpb.Principal{principalMetadata(im), principalMetadata(sm)}), nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we would now allow matching a*/b where before we would not since we treated the whole thing as one unit.

Which is probably reasonable. Not sure why we didn't just make 2 fields to begin with

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeap, added a fix for this.

Change-Id: Iaf2d407dd1e95312d1aee5bf72a7da22f2b1c211
Signed-off-by: Kuat Yessenov <kuat@google.com>
Change-Id: I8f0a68fa400f83e120a99ad634cac54cb5901758
Signed-off-by: Kuat Yessenov <kuat@google.com>
@istio-testing istio-testing added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Nov 15, 2023
@kyessenov
Copy link
Contributor Author

Any more comments?

@@ -1338,6 +1338,13 @@ func (node *Proxy) WorkloadEntry() (string, bool) {
return node.workloadEntryName, node.workloadEntryAutoCreated
}

// SupportsEnvoyExtendedJwt indicates that the proxy JWT extension is capable of
// replacing istio_authn filter.
func (node *Proxy) SupportsEnvoyExtendedJwt() bool {
Copy link
Contributor

@aryan16 aryan16 Nov 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wouldn't be a breaking change for any user right and users don't need to configure this? Why can't we just cherrypick for all the supported versions instead of maintaining different configs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it should be fully compatible - it's never 100% but close to it. We can't backport Envoy features, that's against the project policy to backport features and not fixes.

@@ -226,6 +226,13 @@ func convertToEnvoyJwtConfig(jwtRules []*v1beta1.JWTRule, push *model.PushContex
ForwardPayloadHeader: jwtRule.OutputPayloadToHeader,
PayloadInMetadata: jwtRule.Issuer,
}
if useExtendedJwt {
provider.PayloadInMetadata = filters.EnvoyJwtFilterPayload
Copy link
Contributor

@aryan16 aryan16 Nov 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this would break a user who is already assuming Issuer in the payload and using that for some reason? We should atlease document this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean the internal dynamic metadata from istio_authn? That was not a contract we generally follow - most of Envoy internal interfaces are private for a good reason.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean any user can have a custom envoy filter that is reading the dynamic metadata from jwt_authn filter. It could break them?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

previously we were just adding issuer now payload. Wouldn't it be a problem?

Copy link
Contributor Author

@kyessenov kyessenov Nov 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if they have a custom Envoy/Wasm filter, they need to update. That's always the expectation that the low-level Envoy interfaces are not stable; otherwise, we could never make Envoy changes. I'll update the change note to warn against this. It's a fairly simple change to rename the metadata key.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good!!

res := []*hcm.HttpFilter{}
if filter := b.applier.JwtFilter(); filter != nil {
if filter := b.applier.JwtFilter(false, false); filter != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where's the code to ignore the istio_authn filter. I see we are still building isito_authn filter for all the cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above if block exits early.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah I missed that. Thanks

Change-Id: I668691cc926be197a253827ff02e85bf2bebd6fc
Change-Id: Iaea2503e3f0403766b96442ae3b3ce490e8bac08
Signed-off-by: Kuat Yessenov <kuat@google.com>
res := []*hcm.HttpFilter{}
if filter := b.applier.JwtFilter(); filter != nil {
if filter := b.applier.JwtFilter(false, false); filter != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah I missed that. Thanks

@@ -226,6 +226,13 @@ func convertToEnvoyJwtConfig(jwtRules []*v1beta1.JWTRule, push *model.PushContex
ForwardPayloadHeader: jwtRule.OutputPayloadToHeader,
PayloadInMetadata: jwtRule.Issuer,
}
if useExtendedJwt {
provider.PayloadInMetadata = filters.EnvoyJwtFilterPayload
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good!!

@kyessenov
Copy link
Contributor Author

@ericvn can I get a test&release WG review?

@kyessenov
Copy link
Contributor Author

@ericvn is OoO, @howardjohn can you give this another look, please?

@istio-testing istio-testing merged commit d4d1475 into istio:master Nov 20, 2023
27 checks passed
@ericvn
Copy link
Contributor

ericvn commented Nov 27, 2023

This PR looks to have caused a failure in step 3 of the jwt-route test: https://istio.io/latest/docs/tasks/security/authentication/jwt-route/#validating-ingress-routing-based-on-jwt-claims. Docs say we should get a 200, but we are getting back a 404.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants