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

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package k8s
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"regexp"
    10  	"strings"
    11  
    12  	"github.com/gobwas/glob"
    13  	"github.com/rs/zerolog/log"
    14  	"go.mondoo.com/cnquery"
    15  	"go.mondoo.com/cnquery/motor/asset"
    16  	"go.mondoo.com/cnquery/motor/discovery/common"
    17  	"go.mondoo.com/cnquery/motor/providers"
    18  	"go.mondoo.com/cnquery/motor/providers/k8s"
    19  	"go.mondoo.com/cnquery/motor/providers/k8s/resources"
    20  	"go.mondoo.com/cnquery/motor/vault"
    21  	"k8s.io/apimachinery/pkg/api/errors"
    22  )
    23  
    24  var _ common.ContextInitializer = (*NamespaceResolver)(nil)
    25  
    26  type NamespaceResolver struct{}
    27  
    28  func (r *NamespaceResolver) Name() string {
    29  	return "Kubernetes Namespace Resolver"
    30  }
    31  
    32  func (r *NamespaceResolver) AvailableDiscoveryTargets() []string {
    33  	return []string{
    34  		common.DiscoveryAuto,
    35  		common.DiscoveryAll,
    36  		DiscoveryClusters,
    37  		DiscoveryPods,
    38  		DiscoveryJobs,
    39  		DiscoveryCronJobs,
    40  		DiscoveryStatefulSets,
    41  		DiscoveryDeployments,
    42  		DiscoveryReplicaSets,
    43  		DiscoveryDaemonSets,
    44  		DiscoveryContainerImages,
    45  		DiscoveryAdmissionReviews,
    46  		DiscoveryIngresses,
    47  	}
    48  }
    49  
    50  func (r *NamespaceResolver) InitCtx(ctx context.Context) context.Context {
    51  	return resources.SetDiscoveryCache(ctx, resources.NewDiscoveryCache())
    52  }
    53  
    54  func (r *NamespaceResolver) Resolve(ctx context.Context, root *asset.Asset, tc *providers.Config, credsResolver vault.Resolver, sfn common.QuerySecretFn, userIdDetectors ...providers.PlatformIdDetector) ([]*asset.Asset, error) {
    55  	features := cnquery.GetFeatures(ctx)
    56  	resolved := []*asset.Asset{}
    57  
    58  	nsFilter := NamespaceFilterOpts{}
    59  	includeNamespaces := tc.Options["namespaces"]
    60  	if len(includeNamespaces) > 0 {
    61  		nsFilter.include = append(nsFilter.include, strings.Split(includeNamespaces, ",")...)
    62  	}
    63  
    64  	p, err := k8s.New(ctx, tc)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	// Put a Warn() message if a Namespace that doesn't exist was part of the
    70  	// list of Namespaces to include. We can only check that if the k8s user is allowed to list the
    71  	// cluster namespaces.
    72  	clusterNamespaces, err := p.Namespaces()
    73  	if err != nil {
    74  		if errors.IsForbidden(err) {
    75  			// If the user does not have permissions to list the cluster namespaces, we cannot do glob matching.
    76  			// We can only work with exact matching in that case
    77  			if containsGlob, _ := regexp.MatchString(`[\*\/\\\[\]\{\}\?]`, includeNamespaces); containsGlob {
    78  				return nil, fmt.Errorf("glob patterns are not allowed for k8s users with no list namespace permissions")
    79  			}
    80  			log.Warn().Msg("cannot list cluster namespaces, skipping check for non-existent namespaces...")
    81  		} else {
    82  			return nil, err
    83  		}
    84  	} else {
    85  		for _, ns := range nsFilter.include {
    86  			foundNamespace := false
    87  			g, err := glob.Compile(ns)
    88  			if err != nil {
    89  				log.Error().Err(err).Str("namespaceFilter", ns).Msg("failed to parse Namespace filter glob")
    90  				return nil, err
    91  			}
    92  			for _, clusterNs := range clusterNamespaces {
    93  				if g.Match(clusterNs.Name) {
    94  					foundNamespace = true
    95  					break
    96  				}
    97  			}
    98  			if !foundNamespace {
    99  				log.Warn().Msgf("Namespace filter %q did not match any Namespaces in cluster", ns)
   100  			}
   101  		}
   102  	}
   103  
   104  	excludeNamespaces := tc.Options["namespaces-exclude"]
   105  	if len(excludeNamespaces) > 0 {
   106  		nsFilter.exclude = strings.Split(excludeNamespaces, ",")
   107  	}
   108  
   109  	log.Debug().Strs("namespacesIncludeFilter", nsFilter.include).Strs("namespacesExcludeFilter", nsFilter.exclude).Msg("resolve k8s assets")
   110  
   111  	resourcesFilter, err := resourceFilters(tc)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	nss, err := ListNamespaces(p, tc, "", nsFilter, resourcesFilter, nil)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	resolved = append(resolved, nss...)
   122  	for _, ns := range nss {
   123  		identifier := ns.PlatformIds[0]
   124  		ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(identifier)
   125  		additionalAssets, err := addSeparateAssets(tc, p, nsFilter, resourcesFilter, identifier, ownershipDir, features)
   126  		if err != nil {
   127  			return nil, err
   128  		}
   129  		resolved = append(resolved, additionalAssets...)
   130  	}
   131  
   132  	return resolved, nil
   133  }