go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/_motor/discovery/k8s/list_nodes.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package k8s 5 6 import ( 7 "regexp" 8 "strconv" 9 "strings" 10 11 "github.com/pkg/errors" 12 "go.mondoo.com/cnquery/motor/asset" 13 "go.mondoo.com/cnquery/motor/motorid/gce" 14 "go.mondoo.com/cnquery/motor/platform" 15 "go.mondoo.com/cnquery/motor/providers" 16 "go.mondoo.com/cnquery/motor/providers/k8s" 17 v1 "k8s.io/api/core/v1" 18 ) 19 20 // ListNodes lits all nodes in the cluster. 21 func ListNodes(p k8s.KubernetesProvider, connection *providers.Config, clusterIdentifier string) ([]*asset.Asset, []nodeRelationshipInfo, error) { 22 nodes, err := p.Nodes() 23 if err != nil { 24 return nil, nil, err 25 } 26 27 assets := []*asset.Asset{} 28 nodeRelationshipInfos := []nodeRelationshipInfo{} 29 for i := range nodes { 30 node := nodes[i] 31 asset, err := createAssetFromObject(&node, p.Runtime(), connection, clusterIdentifier) 32 if err != nil { 33 return nil, nil, errors.Wrap(err, "failed to create asset from node") 34 } 35 36 assets = append(assets, asset) 37 nInfo, _ := detectNodeRelationshipInfo(node) 38 if nInfo.hostInstanceAsset != nil { 39 asset.RelatedAssets = append(asset.RelatedAssets, nInfo.hostInstanceAsset) 40 } 41 nodeRelationshipInfos = append(nodeRelationshipInfos, nInfo) 42 } 43 44 return assets, nodeRelationshipInfos, nil 45 } 46 47 type nodeRelationshipInfo struct { 48 cloudAccountAsset *asset.Asset 49 hostInstanceAsset *asset.Asset 50 } 51 52 var ( 53 gkeProviderIDInfoRegexp = regexp.MustCompile("^gce://([\\-0-9a-zA-Z]+)/([\\-0-9a-zA-Z]+)/.*") 54 aksProviderIDInstanceRegexp = regexp.MustCompile("^azure:///(.+)$") 55 ) 56 57 func detectNodeRelationshipInfo(node v1.Node) (nodeRelationshipInfo, bool) { 58 for k := range node.Labels { 59 if strings.HasPrefix(k, "eks.amazonaws.com") { 60 // The node info doesn't seem to have the AWS Account id 61 return nodeRelationshipInfo{}, false 62 } else if strings.HasPrefix(k, "cloud.google.com/gke") { 63 return gkeRelationshipInfo(node) 64 } else if strings.HasPrefix(k, "kubernetes.azure.com") { 65 return aksRelationshipInfo(node) 66 } 67 } 68 hostname := node.Labels["kubernetes.io/hostname"] 69 if hostname == "" { 70 return nodeRelationshipInfo{}, false 71 } 72 return nodeRelationshipInfo{ 73 hostInstanceAsset: &asset.Asset{ 74 Name: hostname, 75 PlatformIds: []string{"//platformid.api.mondoo.app/hostname/" + hostname}, 76 }, 77 }, true 78 } 79 80 func gkeRelationshipInfo(node v1.Node) (nodeRelationshipInfo, bool) { 81 matches := gkeProviderIDInfoRegexp.FindStringSubmatch(node.Spec.ProviderID) 82 if len(matches) != 3 { 83 return nodeRelationshipInfo{}, false 84 } 85 project := matches[1] 86 zone := matches[2] 87 instanceID := node.Annotations["container.googleapis.com/instance_id"] 88 instanceIDInt, err := strconv.ParseUint(instanceID, 10, 64) 89 if err != nil { 90 return nodeRelationshipInfo{}, false 91 } 92 if project != "" && zone != "" && instanceID != "" { 93 cloudAccountAsset := &asset.Asset{ 94 Name: "GCP project " + project, 95 Platform: &platform.Platform{ 96 Kind: providers.Kind_KIND_API, 97 Runtime: providers.RUNTIME_GCP, 98 Title: "Google Cloud Platform", 99 }, 100 PlatformIds: []string{"//platformid.api.mondoo.app/runtime/gcp/projects/" + project}, 101 } 102 hostInstanceAsset := &asset.Asset{ 103 Name: node.Labels["kubernetes.io/hostname"], 104 Platform: &platform.Platform{ 105 Kind: providers.Kind_KIND_VIRTUAL_MACHINE, 106 Runtime: providers.RUNTIME_GCP_COMPUTE, 107 Arch: node.Labels["kubernetes.io/arch"], 108 }, 109 PlatformIds: []string{gce.MondooGcpInstanceID(project, zone, instanceIDInt)}, 110 RelatedAssets: []*asset.Asset{cloudAccountAsset}, 111 } 112 return nodeRelationshipInfo{ 113 cloudAccountAsset: cloudAccountAsset, 114 hostInstanceAsset: hostInstanceAsset, 115 }, true 116 } 117 return nodeRelationshipInfo{}, false 118 } 119 120 func aksRelationshipInfo(node v1.Node) (nodeRelationshipInfo, bool) { 121 matches := aksProviderIDInstanceRegexp.FindStringSubmatch(node.Spec.ProviderID) 122 if len(matches) != 2 { 123 return nodeRelationshipInfo{}, false 124 } 125 parts := strings.Split(matches[1], "/") 126 if len(parts) < 2 || parts[0] != "subscriptions" { 127 return nodeRelationshipInfo{}, false 128 } 129 sub := parts[1] 130 cloudAccountAsset := &asset.Asset{ 131 Name: "Azure subscription " + sub, 132 Platform: &platform.Platform{ 133 Kind: providers.Kind_KIND_API, 134 Runtime: providers.RUNTIME_AZ, 135 }, 136 PlatformIds: []string{"//platformid.api.mondoo.app/runtime/azure/subscriptions/" + sub}, 137 } 138 hostInstanceAsset := &asset.Asset{ 139 Name: node.Labels["kubernetes.io/hostname"], 140 Platform: &platform.Platform{ 141 Kind: providers.Kind_KIND_VIRTUAL_MACHINE, 142 Runtime: providers.RUNTIME_AZ_COMPUTE, 143 Arch: node.Labels["kubernetes.io/arch"], 144 }, 145 PlatformIds: []string{"//platformid.api.mondoo.app/runtime/azure/" + matches[1]}, 146 RelatedAssets: []*asset.Asset{cloudAccountAsset}, 147 } 148 return nodeRelationshipInfo{ 149 cloudAccountAsset: cloudAccountAsset, 150 hostInstanceAsset: hostInstanceAsset, 151 }, true 152 }