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

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package k8s
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	"github.com/rs/zerolog/log"
    11  	"go.mondoo.com/cnquery"
    12  	"go.mondoo.com/cnquery/motor/asset"
    13  	"go.mondoo.com/cnquery/motor/discovery/common"
    14  	"go.mondoo.com/cnquery/motor/providers"
    15  	"go.mondoo.com/cnquery/motor/providers/k8s"
    16  )
    17  
    18  type K8sResourceIdentifier struct {
    19  	Type      string
    20  	Namespace string
    21  	Name      string
    22  }
    23  
    24  // addSeparateAssets Depending on config options it will search for additional assets which should be listed separately.
    25  func addSeparateAssets(
    26  	tc *providers.Config,
    27  	p k8s.KubernetesProvider,
    28  	nsFilter NamespaceFilterOpts,
    29  	resourcesFilter map[string][]K8sResourceIdentifier,
    30  	clusterIdentifier string,
    31  	od *k8s.PlatformIdOwnershipDirectory,
    32  	features cnquery.Features,
    33  ) ([]*asset.Asset, error) {
    34  	resolved := []*asset.Asset{}
    35  
    36  	// discover deployments
    37  	if tc.IncludesOneOfDiscoveryTarget(common.DiscoveryAll, common.DiscoveryAuto, DiscoveryDeployments) {
    38  		// fetch deployment information
    39  		log.Debug().Strs("namespace", nsFilter.include).Msg("search for deployments")
    40  		connection := tc.Clone()
    41  		deployments, err := ListDeployments(p, connection, clusterIdentifier, nsFilter, resourcesFilter, od)
    42  		if err != nil {
    43  			log.Error().Err(err).Msg("could not fetch k8s deployments")
    44  			return nil, err
    45  		}
    46  		resolved = append(resolved, deployments...)
    47  	}
    48  
    49  	// discover k8s pods
    50  	if tc.IncludesOneOfDiscoveryTarget(common.DiscoveryAll, common.DiscoveryAuto, DiscoveryPods) {
    51  		// fetch pod information
    52  		log.Debug().Strs("namespace", nsFilter.include).Msg("search for pods")
    53  		connection := tc.Clone()
    54  		pods, err := ListPods(p, connection, clusterIdentifier, nsFilter, resourcesFilter, od)
    55  		if err != nil {
    56  			log.Error().Err(err).Msg("could not fetch k8s pods")
    57  			return nil, err
    58  		}
    59  		resolved = append(resolved, pods...)
    60  	}
    61  
    62  	// discover k8s pod images
    63  	if tc.IncludesOneOfDiscoveryTarget(common.DiscoveryAll, DiscoveryContainerImages) {
    64  		// fetch pod information
    65  		log.Debug().Strs("namespace", nsFilter.include).Msg("search for pods images")
    66  		containerimages, err := ListPodImages(p, nsFilter, od)
    67  		if err != nil {
    68  			log.Error().Err(err).Msg("could not fetch k8s pods images")
    69  			return nil, err
    70  		}
    71  		resolved = append(resolved, containerimages...)
    72  	}
    73  
    74  	// discovery k8s daemonsets
    75  	if tc.IncludesOneOfDiscoveryTarget(common.DiscoveryAll, common.DiscoveryAuto, DiscoveryDaemonSets) {
    76  		log.Debug().Strs("namespace", nsFilter.include).Msg("search for daemonsets")
    77  		connection := tc.Clone()
    78  		daemonsets, err := ListDaemonSets(p, connection, clusterIdentifier, nsFilter, resourcesFilter, od)
    79  		if err != nil {
    80  			log.Error().Err(err).Msg("could not fetch k8s daemonsets")
    81  			return nil, err
    82  		}
    83  		resolved = append(resolved, daemonsets...)
    84  	}
    85  
    86  	// discover cronjobs
    87  	if tc.IncludesOneOfDiscoveryTarget(common.DiscoveryAll, common.DiscoveryAuto, DiscoveryCronJobs) {
    88  		log.Debug().Strs("namespace", nsFilter.include).Msg("search for cronjobs")
    89  		connection := tc.Clone()
    90  		cronjobs, err := ListCronJobs(p, connection, clusterIdentifier, nsFilter, resourcesFilter, od)
    91  		if err != nil {
    92  			log.Error().Err(err).Msg("could not fetch k8s cronjobs")
    93  			return nil, err
    94  		}
    95  		resolved = append(resolved, cronjobs...)
    96  	}
    97  
    98  	// discover jobs
    99  	if tc.IncludesOneOfDiscoveryTarget(common.DiscoveryAll, common.DiscoveryAuto, DiscoveryJobs, DiscoveryJobs) {
   100  		log.Debug().Strs("namespace", nsFilter.include).Msg("search for jobs")
   101  		connection := tc.Clone()
   102  		jobs, err := ListJobs(p, connection, clusterIdentifier, nsFilter, resourcesFilter, od)
   103  		if err != nil {
   104  			log.Error().Err(err).Msg("could not fetch k8s jobs")
   105  			return nil, err
   106  		}
   107  		resolved = append(resolved, jobs...)
   108  	}
   109  
   110  	// discover statefulsets
   111  	if tc.IncludesOneOfDiscoveryTarget(common.DiscoveryAll, common.DiscoveryAuto, DiscoveryStatefulSets) {
   112  		log.Debug().Strs("namespace", nsFilter.include).Msg("search for statefulsets")
   113  		connection := tc.Clone()
   114  		statefulsets, err := ListStatefulSets(p, connection, clusterIdentifier, nsFilter, resourcesFilter, od)
   115  		if err != nil {
   116  			log.Error().Err(err).Msg("could not fetch k8s statefulsets")
   117  			return nil, err
   118  		}
   119  		resolved = append(resolved, statefulsets...)
   120  	}
   121  
   122  	// discover replicasets
   123  	if tc.IncludesOneOfDiscoveryTarget(common.DiscoveryAll, common.DiscoveryAuto, DiscoveryReplicaSets) {
   124  		log.Debug().Strs("namespace", nsFilter.include).Msg("search for replicasets")
   125  		connection := tc.Clone()
   126  		replicasets, err := ListReplicaSets(p, connection, clusterIdentifier, nsFilter, resourcesFilter, od)
   127  		if err != nil {
   128  			log.Error().Err(err).Msg("could not fetch k8s replicasets")
   129  			return nil, err
   130  		}
   131  		resolved = append(resolved, replicasets...)
   132  	}
   133  
   134  	// discover admissionreviews
   135  	if tc.IncludesOneOfDiscoveryTarget(common.DiscoveryAll, DiscoveryAdmissionReviews) {
   136  		log.Debug().Msg("search for admissionreviews")
   137  		connection := tc.Clone()
   138  		admissionReviews, err := ListAdmissionReviews(p, connection, clusterIdentifier, od)
   139  		if err != nil {
   140  			log.Error().Err(err).Msg("could not fetch k8s admissionreviews")
   141  			return nil, err
   142  		}
   143  		resolved = append(resolved, admissionReviews...)
   144  	}
   145  
   146  	// discover ingresses
   147  	if tc.IncludesOneOfDiscoveryTarget(common.DiscoveryAll, common.DiscoveryAuto, DiscoveryIngresses) {
   148  		log.Debug().Msg("search for ingresses")
   149  		connection := tc.Clone()
   150  		ingresses, err := ListIngresses(p, connection, clusterIdentifier, nsFilter, resourcesFilter, od)
   151  		if err != nil {
   152  			log.Error().Err(err).Msg("could not fetch k8s ingresses")
   153  		}
   154  		resolved = append(resolved, ingresses...)
   155  	}
   156  
   157  	// build a lookup on the k8s uid to look up individual assets to link
   158  	platformIdToAssetMap := map[string]*asset.Asset{}
   159  	for _, assetObj := range resolved {
   160  		for _, platformId := range assetObj.PlatformIds {
   161  			platformIdToAssetMap[platformId] = assetObj
   162  		}
   163  	}
   164  
   165  	for id, a := range platformIdToAssetMap {
   166  		ownedBy := od.OwnedBy(id)
   167  		for _, ownerPlatformId := range ownedBy {
   168  			if aa, ok := platformIdToAssetMap[ownerPlatformId]; ok {
   169  				a.RelatedAssets = append(a.RelatedAssets, aa)
   170  			} else {
   171  				// If the owner object is not scanned we can still add an asset as we know most of the information
   172  				// from the ownerReference field
   173  				if platformEntry, ok := od.GetKubernetesObjectData(ownerPlatformId); ok {
   174  					platformData, err := createPlatformData(platformEntry.Kind, providers.RUNTIME_KUBERNETES_CLUSTER)
   175  					if err != nil || (!features.IsActive(cnquery.K8sNodeDiscovery) && platformData.Name == "k8s-node") {
   176  						continue
   177  					}
   178  					a.RelatedAssets = append(a.RelatedAssets, &asset.Asset{
   179  						PlatformIds: []string{ownerPlatformId},
   180  						Platform:    platformData,
   181  						Name:        platformEntry.Namespace + "/" + platformEntry.Name,
   182  					})
   183  				}
   184  			}
   185  		}
   186  	}
   187  	return resolved, nil
   188  }
   189  
   190  // resourceFilters parses the resource filters from the provider config
   191  func resourceFilters(tc *providers.Config) (map[string][]K8sResourceIdentifier, error) {
   192  	resourcesFilter := make(map[string][]K8sResourceIdentifier)
   193  	if fOpt, ok := tc.Options["k8s-resources"]; ok {
   194  		fs := strings.Split(fOpt, ",")
   195  		for _, f := range fs {
   196  			ids := strings.Split(strings.TrimSpace(f), ":")
   197  			resType := ids[0]
   198  			var ns, name string
   199  			if _, ok := resourcesFilter[resType]; !ok {
   200  				resourcesFilter[resType] = []K8sResourceIdentifier{}
   201  			}
   202  
   203  			switch len(ids) {
   204  			case 3:
   205  				// Namespaced resources have the format type:ns:name
   206  				ns = ids[1]
   207  				name = ids[2]
   208  			case 2:
   209  				// Non-namespaced resources have the format type:name
   210  				name = ids[1]
   211  			default:
   212  				return nil, fmt.Errorf("invalid k8s resource filter: %s", f)
   213  			}
   214  
   215  			resourcesFilter[resType] = append(resourcesFilter[resType], K8sResourceIdentifier{Type: resType, Namespace: ns, Name: name})
   216  		}
   217  	}
   218  	return resourcesFilter, nil
   219  }