istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/analysis/analyzers/injection/injection-image.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 injection 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "sort" 21 "strings" 22 23 v1 "k8s.io/api/core/v1" 24 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 "istio.io/istio/pkg/slices" 32 ) 33 34 // ImageAnalyzer checks the image of auto-injection configured with the running proxies on pods. 35 type ImageAnalyzer struct{} 36 37 var _ analysis.Analyzer = &ImageAnalyzer{} 38 39 // injectionConfigMap is a snippet of the sidecar injection ConfigMap 40 type injectionConfigMap struct { 41 Global global `json:"global"` 42 } 43 44 type global struct { 45 Hub string `json:"hub"` 46 Tag string `json:"tag"` 47 Proxy proxy `json:"proxy"` 48 } 49 50 type proxy struct { 51 Image string `json:"image"` 52 } 53 54 // Metadata implements Analyzer. 55 func (a *ImageAnalyzer) Metadata() analysis.Metadata { 56 return analysis.Metadata{ 57 Name: "injection.ImageAnalyzer", 58 Description: "Checks the image of auto-injection configured with the running proxies on pods", 59 Inputs: []config.GroupVersionKind{ 60 gvk.Namespace, 61 gvk.Pod, 62 gvk.ConfigMap, 63 gvk.MeshConfig, 64 gvk.ProxyConfig, 65 }, 66 } 67 } 68 69 // Analyze implements Analyzer. 70 func (a *ImageAnalyzer) Analyze(c analysis.Context) { 71 proxyImageMap := make(map[string]string) 72 73 // when multiple injector configmaps exist, we may need to assess them respectively. 74 c.ForEach(gvk.ConfigMap, func(r *resource.Instance) bool { 75 cmName := r.Metadata.FullName.Name.String() 76 if strings.HasPrefix(cmName, "istio-sidecar-injector") { 77 cm := r.Message.(*v1.ConfigMap) 78 proxyImageMap[cmName] = GetIstioProxyImage(cm) 79 80 return true 81 } 82 return true 83 }) 84 85 if len(proxyImageMap) == 0 { 86 return 87 } 88 89 injectedNamespaces := make(map[string]string) 90 namespaceMismatchedPods := make(map[string][]string) 91 namespaceResources := make(map[string]*resource.Instance) 92 // Collect the list of namespaces that have istio injection enabled. 93 c.ForEach(gvk.Namespace, func(r *resource.Instance) bool { 94 namespaceResources[r.Metadata.FullName.String()] = r 95 nsRevision, okNewInjectionLabel := r.Metadata.Labels[RevisionInjectionLabelName] 96 if r.Metadata.Labels[util.InjectionLabelName] == util.InjectionLabelEnableValue || okNewInjectionLabel { 97 if okNewInjectionLabel { 98 injectedNamespaces[r.Metadata.FullName.String()] = nsRevision 99 } else { 100 injectedNamespaces[r.Metadata.FullName.String()] = "default" 101 } 102 } else { 103 return true 104 } 105 106 return true 107 }) 108 109 resolver := util.NewEffectiveProxyConfigResolver(c) 110 111 c.ForEach(gvk.Pod, func(r *resource.Instance) bool { 112 var injectionCMName string 113 pod := r.Message.(*v1.PodSpec) 114 115 if nsRevision, ok := injectedNamespaces[r.Metadata.FullName.Namespace.String()]; ok { 116 // Generate the injection configmap name with different revision for every pod 117 injectionCMName = util.GetInjectorConfigMapName(nsRevision) 118 } else { 119 return true 120 } 121 122 // If the pod has been annotated with a custom sidecar, then ignore as 123 // it always overrides the injector logic. 124 if r.Metadata.Annotations["sidecar.istio.io/proxyImage"] != "" { 125 return true 126 } 127 128 variant := resolver.ImageType(r) 129 130 for _, container := range append(slices.Clone(pod.Containers), pod.InitContainers...) { 131 if container.Name != util.IstioProxyName { 132 continue 133 } 134 135 proxyImage, okImage := proxyImageMap[injectionCMName] 136 if !okImage { 137 return true 138 } 139 if container.Image != proxyImage && container.Image != fmt.Sprintf("%s-%s", proxyImage, variant) { 140 namespaceMismatchedPods[r.Metadata.FullName.Namespace.String()] = append( 141 namespaceMismatchedPods[r.Metadata.FullName.Namespace.String()], r.Metadata.FullName.Name.String()) 142 } 143 } 144 145 return true 146 }) 147 for ns, pods := range namespaceMismatchedPods { 148 sort.Strings(pods) 149 c.Report(gvk.Namespace, msg.NewPodsIstioProxyImageMismatchInNamespace(namespaceResources[ns], pods)) 150 } 151 } 152 153 // GetIstioProxyImage retrieves the proxy image name defined in the sidecar injector 154 // configuration. 155 func GetIstioProxyImage(cm *v1.ConfigMap) string { 156 var m injectionConfigMap 157 if err := json.Unmarshal([]byte(cm.Data[util.InjectionConfigMapValue]), &m); err != nil { 158 return "" 159 } 160 // The injector template has a similar '{ contains "/" ... }' conditional 161 if strings.Contains(m.Global.Proxy.Image, "/") { 162 return m.Global.Proxy.Image 163 } 164 return fmt.Sprintf("%s/%s:%s", m.Global.Hub, m.Global.Proxy.Image, m.Global.Tag) 165 }