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  }