go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/id/awsec2/metadata_local.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package awsec2
     5  
     6  import (
     7  	"context"
     8  	"io"
     9  
    10  	"github.com/aws/aws-sdk-go-v2/aws"
    11  	"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
    12  	"github.com/aws/aws-sdk-go-v2/service/ec2"
    13  
    14  	ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
    15  )
    16  
    17  func NewLocal(cfg aws.Config) *LocalEc2InstanceMetadata {
    18  	return &LocalEc2InstanceMetadata{config: cfg}
    19  }
    20  
    21  // Ec2InstanceMetadata returns the instance id
    22  // TODO: we may want to implement instance verification as documented in
    23  // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html
    24  type LocalEc2InstanceMetadata struct {
    25  	config aws.Config
    26  }
    27  
    28  func (m *LocalEc2InstanceMetadata) Identify() (Identity, error) {
    29  	metadata := imds.NewFromConfig(m.config)
    30  	ec2svc := ec2.NewFromConfig(m.config)
    31  	ctx := context.Background()
    32  	doc, err := metadata.GetInstanceIdentityDocument(ctx, &imds.GetInstanceIdentityDocumentInput{})
    33  	if err != nil {
    34  		return Identity{}, err
    35  	}
    36  	name := ""
    37  	// try and fetch this from the metadata, if the tag metadata service is enabled.
    38  	nameTag, err := m.getMetadataValue(metadata, "tags/instance/Name")
    39  	if err == nil {
    40  		name = nameTag
    41  	} else {
    42  		// if not enabled, try and use the aws api as a fallback. this only works if the aws config is setup
    43  		// correctly on the ec2 instance.
    44  		filters := []ec2types.Filter{
    45  			{
    46  				Name:   aws.String("resource-id"),
    47  				Values: []string{doc.InstanceID},
    48  			},
    49  		}
    50  		tags, err := ec2svc.DescribeTags(ctx, &ec2.DescribeTagsInput{Filters: filters})
    51  		if err == nil {
    52  			for _, t := range tags.Tags {
    53  				if t.Key != nil && *t.Key == "Name" && t.Value != nil {
    54  					name = *t.Value
    55  				}
    56  			}
    57  		}
    58  	}
    59  	return Identity{
    60  		InstanceName: name,
    61  		InstanceID:   MondooInstanceID(doc.AccountID, doc.Region, doc.InstanceID),
    62  		AccountID:    "//platformid.api.mondoo.app/runtime/aws/accounts/" + doc.AccountID,
    63  	}, nil
    64  }
    65  
    66  // gets the metadata at the relative specified path. The base path is /latest/meta-data
    67  // so the path param needs to only specify which metadata path is requested
    68  func (m *LocalEc2InstanceMetadata) getMetadataValue(client *imds.Client, path string) (value string, err error) {
    69  	output, err := client.GetMetadata(context.TODO(), &imds.GetMetadataInput{
    70  		Path: path,
    71  	})
    72  	if err != nil {
    73  		return "", err
    74  	}
    75  	defer output.Content.Close()
    76  	bytes, err := io.ReadAll(output.Content)
    77  	if err != nil {
    78  		return "", err
    79  	}
    80  	resp := string(bytes)
    81  	return resp, err
    82  }