istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/analysis/analyzers/authz/authorizationpolicies.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 authz 16 17 import ( 18 "fmt" 19 "strings" 20 21 klabels "k8s.io/apimachinery/pkg/labels" 22 23 "istio.io/api/mesh/v1alpha1" 24 "istio.io/api/security/v1beta1" 25 "istio.io/istio/pkg/config" 26 "istio.io/istio/pkg/config/analysis" 27 "istio.io/istio/pkg/config/analysis/analyzers/util" 28 "istio.io/istio/pkg/config/analysis/msg" 29 "istio.io/istio/pkg/config/resource" 30 "istio.io/istio/pkg/config/schema/gvk" 31 ) 32 33 // AuthorizationPoliciesAnalyzer checks the validity of authorization policies 34 type AuthorizationPoliciesAnalyzer struct{} 35 36 var ( 37 _ analysis.Analyzer = &AuthorizationPoliciesAnalyzer{} 38 meshConfig *v1alpha1.MeshConfig 39 ) 40 41 func (a *AuthorizationPoliciesAnalyzer) Metadata() analysis.Metadata { 42 return analysis.Metadata{ 43 Name: "auth.AuthorizationPoliciesAnalyzer", 44 Description: "Checks the validity of authorization policies", 45 Inputs: []config.GroupVersionKind{ 46 gvk.MeshConfig, 47 gvk.AuthorizationPolicy, 48 gvk.Namespace, 49 gvk.Pod, 50 }, 51 } 52 } 53 54 func (a *AuthorizationPoliciesAnalyzer) Analyze(c analysis.Context) { 55 podLabelsMap := initPodLabelsMap(c) 56 57 c.ForEach(gvk.AuthorizationPolicy, func(r *resource.Instance) bool { 58 a.analyzeNoMatchingWorkloads(r, c, podLabelsMap) 59 a.analyzeNamespaceNotFound(r, c) 60 return true 61 }) 62 } 63 64 func (a *AuthorizationPoliciesAnalyzer) analyzeNoMatchingWorkloads(r *resource.Instance, c analysis.Context, podLabelsMap map[string][]klabels.Set) { 65 ap := r.Message.(*v1beta1.AuthorizationPolicy) 66 apNs := r.Metadata.FullName.Namespace.String() 67 68 // If AuthzPolicy is mesh-wide 69 if meshWidePolicy(apNs, c) { 70 // If it has selector, need further analysis 71 if ap.GetSelector() != nil { 72 apSelector := klabels.SelectorFromSet(ap.GetSelector().MatchLabels) 73 // If there is at least one pod matching the selector within the whole mesh 74 if !hasMatchingPodsRunning(apSelector, podLabelsMap) { 75 c.Report(gvk.AuthorizationPolicy, msg.NewNoMatchingWorkloadsFound(r, apSelector.String())) 76 } 77 } 78 79 // If AuthzPolicy is mesh-wide and selectorless, 80 // no need to keep the analysis 81 return 82 } 83 84 // If the AuthzPolicy is namespace-wide and there are present Pods, 85 // no messages should be triggered. 86 if ap.GetSelector() == nil { 87 if len(podLabelsMap[apNs]) == 0 { 88 c.Report(gvk.AuthorizationPolicy, msg.NewNoMatchingWorkloadsFound(r, "")) 89 } 90 return 91 } 92 93 // If the AuthzPolicy has Selector, then need to find a matching Pod. 94 apSelector := klabels.SelectorFromSet(ap.GetSelector().MatchLabels) 95 if !hasMatchingPodsRunningIn(apSelector, podLabelsMap[apNs]) { 96 c.Report(gvk.AuthorizationPolicy, msg.NewNoMatchingWorkloadsFound(r, apSelector.String())) 97 } 98 } 99 100 // Returns true when the namespace is the root namespace. 101 // It takes the MeshConfig names istio, if not the last instance found. 102 func meshWidePolicy(ns string, c analysis.Context) bool { 103 mConf := fetchMeshConfig(c) 104 return mConf != nil && ns == mConf.GetRootNamespace() 105 } 106 107 func fetchMeshConfig(c analysis.Context) *v1alpha1.MeshConfig { 108 if meshConfig != nil { 109 return meshConfig 110 } 111 112 c.ForEach(gvk.MeshConfig, func(r *resource.Instance) bool { 113 meshConfig = r.Message.(*v1alpha1.MeshConfig) 114 return r.Metadata.FullName.Name != util.MeshConfigName 115 }) 116 117 return meshConfig 118 } 119 120 func hasMatchingPodsRunning(selector klabels.Selector, podLabelsMap map[string][]klabels.Set) bool { 121 for _, setList := range podLabelsMap { 122 if hasMatchingPodsRunningIn(selector, setList) { 123 return true 124 } 125 } 126 return false 127 } 128 129 func hasMatchingPodsRunningIn(selector klabels.Selector, setList []klabels.Set) bool { 130 hasMatchingPods := false 131 for _, labels := range setList { 132 if selector.Matches(labels) { 133 hasMatchingPods = true 134 break 135 } 136 } 137 return hasMatchingPods 138 } 139 140 func (a *AuthorizationPoliciesAnalyzer) analyzeNamespaceNotFound(r *resource.Instance, c analysis.Context) { 141 ap := r.Message.(*v1beta1.AuthorizationPolicy) 142 143 for i, rule := range ap.Rules { 144 for j, from := range rule.From { 145 for k, ns := range append(from.Source.Namespaces, from.Source.NotNamespaces...) { 146 if !matchNamespace(ns, c) { 147 m := msg.NewReferencedResourceNotFound(r, "namespace", ns) 148 149 nsIndex := k 150 if nsIndex >= len(from.Source.Namespaces) { 151 nsIndex -= len(from.Source.Namespaces) 152 } 153 154 if line, ok := util.ErrorLine(r, fmt.Sprintf(util.AuthorizationPolicyNameSpace, i, j, nsIndex)); ok { 155 m.Line = line 156 } 157 158 c.Report(gvk.AuthorizationPolicy, m) 159 } 160 } 161 } 162 } 163 } 164 165 func matchNamespace(exp string, c analysis.Context) bool { 166 match := false 167 c.ForEach(gvk.Namespace, func(r *resource.Instance) bool { 168 ns := r.Metadata.FullName.String() 169 match = namespaceMatch(ns, exp) 170 return !match 171 }) 172 173 return match 174 } 175 176 func namespaceMatch(ns, exp string) bool { 177 if strings.EqualFold(exp, "*") { 178 return true 179 } 180 if strings.HasPrefix(exp, "*") { 181 return strings.HasSuffix(ns, strings.TrimPrefix(exp, "*")) 182 } 183 if strings.HasSuffix(exp, "*") { 184 return strings.HasPrefix(ns, strings.TrimSuffix(exp, "*")) 185 } 186 187 return strings.EqualFold(ns, exp) 188 } 189 190 // Build a map indexed by namespace with in-mesh Pod's labels 191 func initPodLabelsMap(c analysis.Context) map[string][]klabels.Set { 192 podLabelsMap := make(map[string][]klabels.Set) 193 194 c.ForEach(gvk.Pod, func(r *resource.Instance) bool { 195 pLabels := klabels.Set(r.Metadata.Labels) 196 197 ns := r.Metadata.FullName.Namespace.String() 198 if podLabelsMap[ns] == nil { 199 podLabelsMap[ns] = make([]klabels.Set, 0) 200 } 201 202 if util.PodInMesh(r, c) { 203 podLabelsMap[ns] = append(podLabelsMap[ns], pLabels) 204 } 205 206 if util.PodInAmbientMode(r) { 207 podLabelsMap[ns] = append(podLabelsMap[ns], pLabels) 208 } 209 210 return true 211 }) 212 213 return podLabelsMap 214 }