go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/_motor/discovery/k8s/resolver_cluster.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package k8s 5 6 import ( 7 "context" 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/platform/detector" 15 "go.mondoo.com/cnquery/motor/providers" 16 "go.mondoo.com/cnquery/motor/providers/k8s" 17 "go.mondoo.com/cnquery/motor/providers/k8s/resources" 18 "go.mondoo.com/cnquery/motor/providers/local" 19 "go.mondoo.com/cnquery/motor/vault" 20 "go.mondoo.com/cnquery/resources/packs/os/kubectl" 21 ) 22 23 var _ common.ContextInitializer = (*ClusterResolver)(nil) 24 25 type ClusterResolver struct{} 26 27 func (r *ClusterResolver) Name() string { 28 return "Kubernetes Cluster Resolver" 29 } 30 31 func (r *ClusterResolver) AvailableDiscoveryTargets() []string { 32 return []string{ 33 common.DiscoveryAuto, 34 common.DiscoveryAll, 35 DiscoveryClusters, 36 DiscoveryPods, 37 DiscoveryJobs, 38 DiscoveryCronJobs, 39 DiscoveryStatefulSets, 40 DiscoveryDeployments, 41 DiscoveryReplicaSets, 42 DiscoveryDaemonSets, 43 DiscoveryContainerImages, 44 DiscoveryAdmissionReviews, 45 DiscoveryIngresses, 46 DiscoveryNamespaces, 47 } 48 } 49 50 func (r *ClusterResolver) Resolve(ctx context.Context, root *asset.Asset, tc *providers.Config, credsResolver vault.Resolver, sfn common.QuerySecretFn, userIdDetectors ...providers.PlatformIdDetector) ([]*asset.Asset, error) { 51 features := cnquery.GetFeatures(ctx) 52 resolved := []*asset.Asset{} 53 nsFilter := NamespaceFilterOpts{} 54 excludeNamespaces := tc.Options["namespaces-exclude"] 55 if len(excludeNamespaces) > 0 { 56 nsFilter.exclude = strings.Split(excludeNamespaces, ",") 57 } 58 59 var k8sctlConfig *kubectl.KubectlConfig 60 localProvider, err := local.New() 61 if err == nil { 62 k8sctlConfig, err = kubectl.LoadKubeConfig(localProvider) 63 if err != nil { 64 return nil, err 65 } 66 } 67 68 p, err := k8s.New(ctx, tc) 69 if err != nil { 70 return nil, err 71 } 72 73 resourcesFilter, err := resourceFilters(tc) 74 if err != nil { 75 return nil, err 76 } 77 78 if tc.IncludesDiscoveryTarget(common.DiscoveryAuto) { 79 log.Info().Msg("discovery option auto is used. This will detect the assets: cluster, cronjobs, daemonsets, deployments, ingresses, jobs, pods, replicasets, statefulsets") 80 } 81 82 clusterIdentifier, err := p.Identifier() 83 if err != nil { 84 return nil, err 85 } 86 87 // detect platform info for the asset 88 detector := detector.New(p) 89 pf, err := detector.Platform() 90 if err != nil { 91 return nil, err 92 } 93 94 // Only discover cluster and nodes if there are no resource filters. 95 var clusterAsset *asset.Asset 96 ownershipDir := k8s.NewEmptyPlatformIdOwnershipDirectory(clusterIdentifier) 97 if tc.IncludesOneOfDiscoveryTarget(common.DiscoveryAll, common.DiscoveryAuto, DiscoveryClusters) && 98 len(resourcesFilter) == 0 { 99 var clusterName string 100 // the name is still a bit unreliable 101 // see https://github.com/kubernetes/kubernetes/issues/44954 102 if len(tc.Options["context"]) > 0 { 103 clusterName = tc.Options["context"] 104 log.Info().Str("cluster-name", clusterName).Msg("use cluster name from --context") 105 } else { 106 clusterName = "" 107 108 if tc.Options[k8s.OPTION_MANIFEST] != "" || tc.Options[k8s.OPTION_ADMISSION] != "" || tc.Options[k8s.OPTION_IMMEMORY_CONTENT] != "" { 109 clusterName, _ = p.Name() 110 } else { 111 // try to parse context from kubectl config 112 if clusterName == "" && k8sctlConfig != nil && len(k8sctlConfig.CurrentContext) > 0 { 113 clusterName = k8sctlConfig.CurrentClusterName() 114 log.Info().Str("cluster-name", clusterName).Msg("use cluster name from kube config") 115 } 116 117 // fallback to first node name if we could not gather the name from kubeconfig 118 if clusterName == "" { 119 name, err := p.Name() 120 if err == nil { 121 clusterName = name 122 log.Info().Str("cluster-name", clusterName).Msg("use cluster name from node name") 123 } 124 } 125 126 clusterName = "K8s Cluster " + clusterName 127 } 128 } 129 130 clusterAsset = &asset.Asset{ 131 PlatformIds: []string{clusterIdentifier}, 132 Name: clusterName, 133 Platform: pf, 134 Connections: []*providers.Config{tc}, // pass-in the current config 135 State: asset.State_STATE_RUNNING, 136 } 137 resolved = append(resolved, clusterAsset) 138 139 if features.IsActive(cnquery.K8sNodeDiscovery) { 140 // nodes are only added as related assets because we have no policies to scan them 141 nodes, nodeRelationshipInfos, err := ListNodes(p, tc, clusterIdentifier) 142 if err == nil && len(nodes) > 0 { 143 ri := nodeRelationshipInfos[0] 144 if ri.cloudAccountAsset != nil { 145 clusterAsset.RelatedAssets = append(clusterAsset.RelatedAssets, ri.cloudAccountAsset) 146 } 147 clusterAsset.RelatedAssets = append(clusterAsset.RelatedAssets, nodes...) 148 } 149 } 150 } 151 152 additionalAssets, err := addSeparateAssets(tc, p, nsFilter, resourcesFilter, clusterIdentifier, ownershipDir, features) 153 if err != nil { 154 return nil, err 155 } 156 157 if clusterAsset != nil { 158 isRelatedFn := func(a *asset.Asset) bool { 159 return a.Platform.GetKind() == providers.Kind_KIND_K8S_OBJECT 160 } 161 162 for _, aa := range additionalAssets { 163 if isRelatedFn(aa) { 164 clusterAsset.RelatedAssets = append(clusterAsset.RelatedAssets, aa) 165 } 166 } 167 } 168 resolved = append(resolved, additionalAssets...) 169 170 return resolved, nil 171 } 172 173 func (r *ClusterResolver) InitCtx(ctx context.Context) context.Context { 174 return resources.SetDiscoveryCache(ctx, resources.NewDiscoveryCache()) 175 }