go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/_motor/discovery/k8s/container_utils.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package k8s
     5  
     6  import (
     7  	"strings"
     8  
     9  	"go.mondoo.com/cnquery/types"
    10  	v1 "k8s.io/api/core/v1"
    11  )
    12  
    13  const DockerPullablePrefix = "docker-pullable://"
    14  
    15  type ContainerImage struct {
    16  	image         string
    17  	resolvedImage string
    18  	pullSecrets   []v1.Secret
    19  }
    20  
    21  func ResolveUniqueContainerImages(cs []v1.Container, ps []v1.Secret) map[string]ContainerImage {
    22  	imagesSet := make(map[string]ContainerImage)
    23  	for _, c := range cs {
    24  		imagesSet[c.Image] = ContainerImage{image: c.Image, resolvedImage: c.Image, pullSecrets: ps}
    25  	}
    26  	return imagesSet
    27  }
    28  
    29  func ResolveUniqueContainerImagesFromStatus(cs []v1.ContainerStatus, ps []v1.Secret) map[string]ContainerImage {
    30  	imagesSet := make(map[string]ContainerImage)
    31  	for _, c := range cs {
    32  		image, resolvedImage := ResolveContainerImageFromStatus(c)
    33  		imagesSet[resolvedImage] = ContainerImage{image: image, resolvedImage: resolvedImage, pullSecrets: ps}
    34  	}
    35  	return imagesSet
    36  }
    37  
    38  func ResolveContainerImageFromStatus(containerStatus v1.ContainerStatus) (string, string) {
    39  	image := containerStatus.Image
    40  	resolvedImage := containerStatus.ImageID
    41  	if strings.HasPrefix(resolvedImage, DockerPullablePrefix) {
    42  		resolvedImage = strings.TrimPrefix(resolvedImage, DockerPullablePrefix)
    43  	}
    44  
    45  	// stopped pods may not include the resolved image
    46  	// pods with imagePullPolicy: Never do not have a proper ImageId value as it contains only the
    47  	// sha but not the repository. If we use that value, it will cause issues later because we will
    48  	// eventually try to pull an image by providing just the sha without a repo.
    49  	if len(resolvedImage) == 0 || !strings.Contains(resolvedImage, "@") {
    50  		resolvedImage = containerStatus.Image
    51  	}
    52  
    53  	return image, resolvedImage
    54  }
    55  
    56  // UniqueImagesForPod returns the unique container images for a pod. Images are compared based on their digest
    57  // if that is available in the pod status. If there is no pod status set, the container image tag is used.
    58  func UniqueImagesForPod(pod v1.Pod, credsStore *credsStore) map[string]ContainerImage {
    59  	imagesSet := make(map[string]ContainerImage)
    60  
    61  	pullSecrets := make([]v1.Secret, 0, len(pod.Spec.ImagePullSecrets))
    62  	for _, ps := range pod.Spec.ImagePullSecrets {
    63  		s, err := credsStore.Get(pod.Namespace, ps.Name) // TODO: figure out if we want to do anything with the error here
    64  		if err == nil {
    65  			pullSecrets = append(pullSecrets, *s)
    66  		}
    67  	}
    68  
    69  	// it is best to read the image from the container status since it is resolved
    70  	// and more accurate, for static file scan we also need to fall-back to pure spec
    71  	// since the status will not be set
    72  	imagesSet = types.MergeMaps(imagesSet, ResolveUniqueContainerImagesFromStatus(pod.Status.InitContainerStatuses, pullSecrets))
    73  
    74  	// fall-back to spec
    75  	if len(pod.Spec.InitContainers) > 0 && len(pod.Status.InitContainerStatuses) == 0 {
    76  		imagesSet = types.MergeMaps(imagesSet, ResolveUniqueContainerImages(pod.Spec.InitContainers, pullSecrets))
    77  	}
    78  
    79  	imagesSet = types.MergeMaps(imagesSet, ResolveUniqueContainerImagesFromStatus(pod.Status.ContainerStatuses, pullSecrets))
    80  
    81  	// fall-back to spec
    82  	if len(pod.Spec.Containers) > 0 && len(pod.Status.ContainerStatuses) == 0 {
    83  		imagesSet = types.MergeMaps(imagesSet, ResolveUniqueContainerImages(pod.Spec.Containers, pullSecrets))
    84  	}
    85  	return imagesSet
    86  }