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  }