From 968a6f97ec19ed6e55d8e75b21172c19b2f8c38f Mon Sep 17 00:00:00 2001 From: zirain Date: Mon, 30 Oct 2023 21:03:28 -0500 Subject: [PATCH] Improve CreateFakeJwks (#47621) * fix CreateFakeJwks * lint * lint: lll * gofumpt * reuse rsa key * lint * cache * nit --- go.mod | 5 +++- go.sum | 10 +++++++- istio.deps | 2 +- .../github.com/lestrrat-go/httprc/LICENSE | 21 ++++++++++++++++ .../github.com/lestrrat-go/jwx/v2/LICENSE | 22 ++++++++++++++++ pilot/pkg/model/jwks_resolver.go | 25 ++++++++++++------- .../authn/v1beta1/policy_applier_test.go | 7 +++--- 7 files changed, 77 insertions(+), 15 deletions(-) create mode 100644 licenses/github.com/lestrrat-go/httprc/LICENSE create mode 100644 licenses/github.com/lestrrat-go/jwx/v2/LICENSE diff --git a/go.mod b/go.mod index f2e838b7c8e1..b042b20edb75 100644 --- a/go.mod +++ b/go.mod @@ -56,6 +56,7 @@ require ( github.com/kr/pretty v0.3.1 github.com/kylelemons/godebug v1.1.0 github.com/lestrrat-go/jwx v1.2.26 + github.com/lestrrat-go/jwx/v2 v2.0.15 github.com/mattn/go-isatty v0.0.20 github.com/miekg/dns v1.1.56 github.com/mitchellh/copystructure v1.2.0 @@ -184,8 +185,9 @@ require ( github.com/klauspost/compress v1.17.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect - github.com/lestrrat-go/blackmagic v1.0.1 // indirect + github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/httprc v1.0.4 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/option v1.0.1 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect @@ -217,6 +219,7 @@ require ( github.com/rivo/uniseg v0.4.3 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/segmentio/asm v1.2.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/afero v1.10.0 // indirect diff --git a/go.sum b/go.sum index b068d5b31bcf..5a64a0175414 100644 --- a/go.sum +++ b/go.sum @@ -544,14 +544,19 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A= github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= -github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80= github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= +github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= +github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= +github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= +github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= github.com/lestrrat-go/jwx v1.2.26 h1:4iFo8FPRZGDYe1t19mQP0zTRqA7n8HnJ5lkIiDvJcB0= github.com/lestrrat-go/jwx v1.2.26/go.mod h1:MaiCdGbn3/cckbOFSCluJlJMmp9dmZm5hDuIkx8ftpQ= +github.com/lestrrat-go/jwx/v2 v2.0.15 h1:XvR2lQdX+mZechmqWxqQb2foU3hgAn5+Rj0ICa0I6sU= +github.com/lestrrat-go/jwx/v2 v2.0.15/go.mod h1:jBHyESp4e7QxfERM0UKkQ80/94paqNIEcdEfiUYz5zE= github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= @@ -733,6 +738,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= +github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -1101,6 +1108,7 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/istio.deps b/istio.deps index b535b30cb300..996d53b4ffe8 100644 --- a/istio.deps +++ b/istio.deps @@ -4,7 +4,7 @@ "name": "PROXY_REPO_SHA", "repoName": "proxy", "file": "", - "lastStableSHA": "25b4a3908a0366e6779d7b1cd36279b70cc8e6aa" + "lastStableSHA": "7770bba582a02c45885ee8697c7b4453c3fd0f51" }, { "_comment": "", diff --git a/licenses/github.com/lestrrat-go/httprc/LICENSE b/licenses/github.com/lestrrat-go/httprc/LICENSE new file mode 100644 index 000000000000..3e196892cab8 --- /dev/null +++ b/licenses/github.com/lestrrat-go/httprc/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 lestrrat + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/github.com/lestrrat-go/jwx/v2/LICENSE b/licenses/github.com/lestrrat-go/jwx/v2/LICENSE new file mode 100644 index 000000000000..205e33a7f148 --- /dev/null +++ b/licenses/github.com/lestrrat-go/jwx/v2/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 lestrrat + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/pilot/pkg/model/jwks_resolver.go b/pilot/pkg/model/jwks_resolver.go index c135c7437774..a1df5fe0d6a9 100644 --- a/pilot/pkg/model/jwks_resolver.go +++ b/pilot/pkg/model/jwks_resolver.go @@ -15,9 +15,10 @@ package model import ( + "crypto/rand" + "crypto/rsa" "crypto/tls" "crypto/x509" - "encoding/base64" "encoding/json" "errors" "fmt" @@ -35,6 +36,7 @@ import ( core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoy_jwt "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/jwt_authn/v3" + "github.com/lestrrat-go/jwx/v2/jwk" "istio.io/istio/pilot/pkg/features" "istio.io/istio/pkg/monitoring" @@ -152,7 +154,6 @@ type JwksResolver struct { jwksUribackgroundChannel bool } -// NewJwksResolver creates new instance of JwksResolver. func NewJwksResolver(evictionDuration, refreshDefaultInterval, refreshIntervalOnFailure, retryInterval time.Duration) *JwksResolver { return newJwksResolverWithCABundlePaths( evictionDuration, @@ -283,7 +284,7 @@ func (r *JwksResolver) BuildLocalJwks(jwksURI, jwtIssuer, jwtPubKey string) *env log.Infof("The JWKS key is not yet fetched for issuer %s (%s), using a fake JWKS for now", jwtIssuer, jwksURI) // This is a temporary workaround to reject a request with JWT token by using a fake jwks when istiod failed to fetch it. // TODO(xulingqing): Find a better way to reject the request without using the fake jwks. - jwtPubKey = CreateFakeJwks(jwksURI) + jwtPubKey = CreateFakeJwks() } } return &envoy_jwt.JwtProvider_LocalJwks{ @@ -295,13 +296,19 @@ func (r *JwksResolver) BuildLocalJwks(jwksURI, jwtIssuer, jwtPubKey string) *env } } +var fakeJwks string + // CreateFakeJwks is a helper function to make a fake jwks when istiod failed to fetch it. -func CreateFakeJwks(jwksURI string) string { - // Create a fake jwksURI - fakeJwksURI := "Error-IstiodFailedToFetchJwksUri-" + jwksURI - // Encode jwksURI with base64 to make dynamic n in jwks - encodedString := base64.RawURLEncoding.EncodeToString([]byte(fakeJwksURI)) - return fmt.Sprintf(`{"keys":[ {"e":"AQAB","kid":"abc","kty":"RSA","n":"%s"}]}`, encodedString) +func CreateFakeJwks() string { + if fakeJwks == "" { + fakeJwksRSAKey, _ := rsa.GenerateKey(rand.Reader, 2048) + key, _ := jwk.FromRaw(fakeJwksRSAKey) + rsaKey, _ := key.(jwk.RSAPrivateKey) + res, _ := json.Marshal(rsaKey) + fakeJwks = fmt.Sprintf(`{"keys":[ %s]}`, string(res)) + } + + return fakeJwks } // Resolve jwks_uri through openID discovery. diff --git a/pilot/pkg/security/authn/v1beta1/policy_applier_test.go b/pilot/pkg/security/authn/v1beta1/policy_applier_test.go index 60c43cd180e6..69800c1390a5 100644 --- a/pilot/pkg/security/authn/v1beta1/policy_applier_test.go +++ b/pilot/pkg/security/authn/v1beta1/policy_applier_test.go @@ -687,7 +687,7 @@ func TestJwtFilter(t *testing.T) { JwksSourceSpecifier: &envoy_jwt.JwtProvider_LocalJwks{ LocalJwks: &core.DataSource{ Specifier: &core.DataSource_InlineString{ - InlineString: model.CreateFakeJwks("http://site.not.exist"), + InlineString: model.CreateFakeJwks(), }, }, }, @@ -919,6 +919,7 @@ func TestJwtFilter(t *testing.T) { push.JwtKeyResolver = model.NewJwksResolver( model.JwtPubKeyEvictionDuration, model.JwtPubKeyRefreshInterval, model.JwtPubKeyRefreshIntervalOnFailure, 10*time.Millisecond) + defer push.JwtKeyResolver.Close() push.ServiceIndex.HostnameAndNamespace[host.Name("jwt-token-issuer.mesh")] = map[string]*model.Service{} @@ -1166,7 +1167,7 @@ func TestConvertToEnvoyJwtConfig(t *testing.T) { JwksSourceSpecifier: &envoy_jwt.JwtProvider_LocalJwks{ LocalJwks: &core.DataSource{ Specifier: &core.DataSource_InlineString{ - InlineString: model.CreateFakeJwks(""), + InlineString: model.CreateFakeJwks(), }, }, }, @@ -1221,7 +1222,7 @@ func TestConvertToEnvoyJwtConfig(t *testing.T) { JwksSourceSpecifier: &envoy_jwt.JwtProvider_LocalJwks{ LocalJwks: &core.DataSource{ Specifier: &core.DataSource_InlineString{ - InlineString: model.CreateFakeJwks("http://site.not.exist"), + InlineString: model.CreateFakeJwks(), }, }, },