istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/analysis/analyzers/virtualservice/jwtclaimroute.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package virtualservice 16 17 import ( 18 klabels "k8s.io/apimachinery/pkg/labels" 19 20 "istio.io/api/networking/v1alpha3" 21 "istio.io/api/security/v1beta1" 22 "istio.io/istio/pkg/config" 23 "istio.io/istio/pkg/config/analysis" 24 "istio.io/istio/pkg/config/analysis/analyzers/util" 25 "istio.io/istio/pkg/config/analysis/msg" 26 "istio.io/istio/pkg/config/constants" 27 "istio.io/istio/pkg/config/resource" 28 "istio.io/istio/pkg/config/schema/gvk" 29 "istio.io/istio/pkg/jwt" 30 ) 31 32 type JWTClaimRouteAnalyzer struct{} 33 34 var _ analysis.Analyzer = &JWTClaimRouteAnalyzer{} 35 36 // Metadata implements Analyzer 37 func (s *JWTClaimRouteAnalyzer) Metadata() analysis.Metadata { 38 return analysis.Metadata{ 39 Name: "virtualservice.JWTClaimRouteAnalyzer", 40 Description: "Checks the VirtualService using JWT claim based routing has corresponding RequestAuthentication", 41 Inputs: []config.GroupVersionKind{ 42 gvk.VirtualService, 43 gvk.RequestAuthentication, 44 gvk.Gateway, 45 gvk.Pod, 46 }, 47 } 48 } 49 50 // Analyze implements Analyzer 51 func (s *JWTClaimRouteAnalyzer) Analyze(c analysis.Context) { 52 requestAuthNByNamespace := map[string][]klabels.Selector{} 53 c.ForEach(gvk.RequestAuthentication, func(r *resource.Instance) bool { 54 ns := r.Metadata.FullName.Namespace.String() 55 if _, found := requestAuthNByNamespace[ns]; !found { 56 requestAuthNByNamespace[ns] = []klabels.Selector{} 57 } 58 ra := r.Message.(*v1beta1.RequestAuthentication) 59 raSelector := klabels.SelectorFromSet(ra.GetSelector().GetMatchLabels()) 60 requestAuthNByNamespace[ns] = append(requestAuthNByNamespace[ns], raSelector) 61 return true 62 }) 63 64 c.ForEach(gvk.VirtualService, func(r *resource.Instance) bool { 65 s.analyze(r, c, requestAuthNByNamespace) 66 return true 67 }) 68 } 69 70 func (s *JWTClaimRouteAnalyzer) analyze(r *resource.Instance, c analysis.Context, requestAuthNByNamespace map[string][]klabels.Selector) { 71 // Check if the virtual service is using JWT claim based routing. 72 vs := r.Message.(*v1alpha3.VirtualService) 73 var vsRouteKey string 74 if vsRouteKey = routeBasedOnJWTClaimKey(vs); vsRouteKey == "" { 75 return 76 } 77 vsNs := r.Metadata.FullName.Namespace 78 79 // Check if the virtual service is applied to gateway. 80 for _, gwName := range vs.Gateways { 81 if gwName == util.MeshGateway { 82 continue 83 } 84 85 gwFullName := resource.NewShortOrFullName(vsNs, gwName) 86 gwRes := c.Find(gvk.Gateway, gwFullName) 87 if gwRes == nil { 88 // The gateway does not exist, this should already be covered by the gateway analyzer. 89 continue 90 } 91 92 gw := gwRes.Message.(*v1alpha3.Gateway) 93 gwSelector := klabels.SelectorFromSet(gw.Selector) 94 95 // Check each pod selected by the gateway. 96 c.ForEach(gvk.Pod, func(rPod *resource.Instance) bool { 97 podLabels := klabels.Set(rPod.Metadata.Labels) 98 if !gwSelector.Matches(podLabels) { 99 return true 100 } 101 102 // Check if there is request authentication applied to the pod. 103 var hasRequestAuthNForPod bool 104 105 raSelectors := requestAuthNByNamespace[constants.IstioSystemNamespace] 106 raSelectors = append(raSelectors, requestAuthNByNamespace[rPod.Metadata.FullName.Namespace.String()]...) 107 for _, raSelector := range raSelectors { 108 if raSelector.Matches(podLabels) { 109 hasRequestAuthNForPod = true 110 break 111 } 112 } 113 if !hasRequestAuthNForPod { 114 m := msg.NewJwtClaimBasedRoutingWithoutRequestAuthN(r, vsRouteKey, gwFullName.String(), rPod.Metadata.FullName.Name.String()) 115 c.Report(gvk.VirtualService, m) 116 } 117 return true 118 }) 119 } 120 } 121 122 func routeBasedOnJWTClaimKey(vs *v1alpha3.VirtualService) string { 123 for _, httpRoute := range vs.GetHttp() { 124 for _, match := range httpRoute.GetMatch() { 125 for key := range match.GetHeaders() { 126 if jwt.ToRoutingClaim(key).Match { 127 return key 128 } 129 } 130 for key := range match.GetWithoutHeaders() { 131 if jwt.ToRoutingClaim(key).Match { 132 return key 133 } 134 } 135 } 136 } 137 return "" 138 }