istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/analysis/analyzers/injection/injection.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 "strings" 21 22 v1 "k8s.io/api/core/v1" 23 24 "istio.io/api/annotation" 25 "istio.io/api/label" 26 "istio.io/istio/pkg/config" 27 "istio.io/istio/pkg/config/analysis" 28 "istio.io/istio/pkg/config/analysis/analyzers/util" 29 "istio.io/istio/pkg/config/analysis/msg" 30 "istio.io/istio/pkg/config/constants" 31 "istio.io/istio/pkg/config/resource" 32 "istio.io/istio/pkg/config/schema/gvk" 33 "istio.io/istio/pkg/slices" 34 ) 35 36 // Analyzer checks conditions related to Istio sidecar injection. 37 type Analyzer struct{} 38 39 var _ analysis.Analyzer = &Analyzer{} 40 41 // We assume that enablement is via an istio-injection=enabled or istio.io/rev namespace label 42 // In theory, there can be alternatives using Mutatingwebhookconfiguration, but they're very uncommon 43 // See https://istio.io/docs/ops/troubleshooting/injection/ for more info. 44 var ( 45 RevisionInjectionLabelName = label.IoIstioRev.Name 46 ) 47 48 // Metadata implements Analyzer 49 func (a *Analyzer) Metadata() analysis.Metadata { 50 return analysis.Metadata{ 51 Name: "injection.Analyzer", 52 Description: "Checks conditions related to Istio sidecar injection", 53 Inputs: []config.GroupVersionKind{ 54 gvk.Namespace, 55 gvk.Pod, 56 gvk.ConfigMap, 57 }, 58 } 59 } 60 61 // Analyze implements Analyzer 62 func (a *Analyzer) Analyze(c analysis.Context) { 63 enableNamespacesByDefault := false 64 injectedNamespaces := make(map[string]bool) 65 66 c.ForEach(gvk.Namespace, func(r *resource.Instance) bool { 67 if r.Metadata.FullName.String() == constants.IstioSystemNamespace { 68 return true 69 } 70 71 ns := r.Metadata.FullName.String() 72 73 injectionLabel, okInjectionLabel := r.Metadata.Labels[util.InjectionLabelName] 74 nsRevision, okNewInjectionLabel := r.Metadata.Labels[RevisionInjectionLabelName] 75 76 istioLabels := make([]string, 0) 77 if okInjectionLabel { 78 istioLabels = append(istioLabels, fmt.Sprintf("%s=%s", util.InjectionLabelName, injectionLabel)) 79 } 80 for _, l := range []string{RevisionInjectionLabelName, constants.DataplaneModeLabel} { 81 if _, ok := r.Metadata.Labels[l]; ok && (!okInjectionLabel || injectionLabel == "enabled") { 82 istioLabels = append(istioLabels, fmt.Sprintf("%s=%s", l, r.Metadata.Labels[l])) 83 } 84 } 85 if len(istioLabels) > 1 { 86 m := msg.NewNamespaceMultipleInjectionLabels(r, istioLabels) 87 c.Report(gvk.Namespace, m) 88 } 89 90 if r.Metadata.Labels[constants.DataplaneModeLabel] == constants.DataplaneModeAmbient { 91 return true 92 } 93 94 // verify the enableNamespacesByDefault flag in injection configmaps 95 c.ForEach(gvk.ConfigMap, func(r *resource.Instance) bool { 96 injectionCMName := util.GetInjectorConfigMapName(nsRevision) 97 if r.Metadata.FullName.Name.String() == injectionCMName { 98 cm := r.Message.(*v1.ConfigMap) 99 enableNamespacesByDefault = GetEnableNamespacesByDefaultFromInjectedConfigMap(cm) 100 return false 101 } 102 return true 103 }) 104 105 if injectionLabel == "" && !okNewInjectionLabel { 106 // if Istio is installed with sidecarInjectorWebhook.enableNamespacesByDefault=true 107 // (in the istio-sidecar-injector configmap), we need to reverse this logic and treat this as an injected namespace 108 if enableNamespacesByDefault { 109 m := msg.NewNamespaceInjectionEnabledByDefault(r) 110 c.Report(gvk.Namespace, m) 111 return true 112 } 113 114 m := msg.NewNamespaceNotInjected(r, ns, ns) 115 116 if line, ok := util.ErrorLine(r, fmt.Sprintf(util.MetadataName)); ok { 117 m.Line = line 118 } 119 120 c.Report(gvk.Namespace, m) 121 return true 122 } 123 124 if injectionLabel != util.InjectionLabelEnableValue { 125 // If legacy label has any value other than the enablement value, they are deliberately not injecting it, so ignore 126 return true 127 } 128 129 injectedNamespaces[ns] = true 130 131 return true 132 }) 133 134 c.ForEach(gvk.Pod, func(r *resource.Instance) bool { 135 pod := r.Message.(*v1.PodSpec) 136 137 if !injectedNamespaces[r.Metadata.FullName.Namespace.String()] { 138 return true 139 } 140 141 // If a pod has injection explicitly disabled, no need to check further 142 inj := r.Metadata.Annotations[annotation.SidecarInject.Name] 143 if v, ok := r.Metadata.Labels[label.SidecarInject.Name]; ok { 144 inj = v 145 } 146 if strings.EqualFold(inj, "false") { 147 return true 148 } 149 150 if pod.HostNetwork { 151 return true 152 } 153 154 proxyImage := "" 155 for _, container := range append(slices.Clone(pod.Containers), pod.InitContainers...) { 156 if container.Name == util.IstioProxyName { 157 proxyImage = container.Image 158 break 159 } 160 } 161 162 if proxyImage == "" { 163 c.Report(gvk.Pod, msg.NewPodMissingProxy(r, r.Metadata.FullName.String())) 164 } 165 166 return true 167 }) 168 } 169 170 // GetInjectedConfigMapValuesStruct retrieves value of sidecarInjectorWebhook.enableNamespacesByDefault 171 // defined in the sidecar injector configuration. 172 func GetEnableNamespacesByDefaultFromInjectedConfigMap(cm *v1.ConfigMap) bool { 173 var injectedCMValues map[string]any 174 if err := json.Unmarshal([]byte(cm.Data[util.InjectionConfigMapValue]), &injectedCMValues); err != nil { 175 return false 176 } 177 178 injectionEnable := injectedCMValues[util.InjectorWebhookConfigKey].(map[string]any)[util.InjectorWebhookConfigValue] 179 return injectionEnable.(bool) 180 }