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

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package awsecs
     5  
     6  import (
     7  	"encoding/json"
     8  	"io"
     9  	"regexp"
    10  	"strings"
    11  
    12  	"github.com/aws/aws-sdk-go/aws/arn"
    13  	"github.com/cockroachdb/errors"
    14  	"github.com/rs/zerolog/log"
    15  	"go.mondoo.com/cnquery/providers-sdk/v1/inventory"
    16  	"go.mondoo.com/cnquery/providers/os/connection/shared"
    17  	"go.mondoo.com/cnquery/providers/os/id/containerid"
    18  )
    19  
    20  const (
    21  	identityUrl = "${ECS_CONTAINER_METADATA_URI_V4}"
    22  )
    23  
    24  func MondooECSContainerID(containerArn string) string {
    25  	var account, region, id string
    26  	if arn.IsARN(containerArn) {
    27  		if p, err := arn.Parse(containerArn); err == nil {
    28  			account = p.AccountID
    29  			region = p.Region
    30  			id = p.Resource
    31  		}
    32  	}
    33  	return "//platformid.api.mondoo.app/runtime/aws/ecs/v1/accounts/" + account + "/regions/" + region + "/" + id
    34  }
    35  
    36  var VALID_MONDOO_ECSCONTAINER_ID = regexp.MustCompile(`^//platformid.api.mondoo.app/runtime/aws/ecs/v1/accounts/\d{12}/regions\/(us(-gov)?|ap|ca|cn|eu|sa)-(central|(north|south)?(east|west)?)-\d\/container\/.+$`)
    37  
    38  type ECSContainer struct {
    39  	Account string
    40  	Region  string
    41  	Id      string
    42  }
    43  
    44  func ParseMondooECSContainerId(path string) (*ECSContainer, error) {
    45  	if !IsValidMondooECSContainerId(path) {
    46  		return nil, errors.New("invalid aws ecs container id")
    47  	}
    48  	keyValues := strings.Split(path, "/")
    49  	if len(keyValues) != 15 {
    50  		return nil, errors.New("invalid ecs container id length")
    51  	}
    52  	return &ECSContainer{Account: keyValues[8], Region: keyValues[10], Id: strings.Join(keyValues[12:], "/")}, nil
    53  }
    54  
    55  func IsValidMondooECSContainerId(path string) bool {
    56  	return VALID_MONDOO_ECSCONTAINER_ID.MatchString(path)
    57  }
    58  
    59  type Identity struct {
    60  	ContainerArn      string
    61  	Name              string
    62  	RuntimeID         string
    63  	PlatformIds       []string
    64  	AccountPlatformID string
    65  }
    66  type InstanceIdentifier interface {
    67  	Identify() (Identity, error)
    68  }
    69  
    70  func Resolve(conn shared.Connection, pf *inventory.Platform) (InstanceIdentifier, error) {
    71  	return &containerMetadata{conn, pf}, nil
    72  }
    73  
    74  type containerMetadata struct {
    75  	conn     shared.Connection
    76  	platform *inventory.Platform
    77  }
    78  
    79  func (m *containerMetadata) Identify() (Identity, error) {
    80  	log.Debug().Msg("getting ecs container identity")
    81  
    82  	containerDocument, err := m.containerIdentityDocument()
    83  	if err != nil {
    84  		return Identity{}, err
    85  	}
    86  	// parse into struct
    87  	doc := EcrContainerIdentityDoc{}
    88  	if err := json.NewDecoder(strings.NewReader(containerDocument)).Decode(&doc); err != nil {
    89  		return Identity{}, errors.Wrap(err, "failed to decode ECS container identity document")
    90  	}
    91  	var accountID string
    92  	if arn.IsARN(doc.ContainerArn) {
    93  		if p, err := arn.Parse(doc.ContainerArn); err == nil {
    94  			accountID = p.AccountID
    95  		}
    96  	}
    97  	return Identity{
    98  		Name:              doc.Name,
    99  		ContainerArn:      doc.ContainerArn,
   100  		RuntimeID:         doc.DockerId,
   101  		AccountPlatformID: "//platformid.api.mondoo.app/runtime/aws/accounts/" + accountID,
   102  		PlatformIds:       []string{MondooECSContainerID(doc.ContainerArn), containerid.MondooContainerID(doc.DockerId)},
   103  	}, nil
   104  }
   105  
   106  func (m *containerMetadata) curlDocument(url string) (string, error) {
   107  	cmd, err := m.conn.RunCommand("curl " + url)
   108  	if err != nil {
   109  		return "", err
   110  	}
   111  	data, err := io.ReadAll(cmd.Stdout)
   112  	if err != nil {
   113  		return "", err
   114  	}
   115  
   116  	return strings.TrimSpace(string(data)), nil
   117  }
   118  
   119  func (m *containerMetadata) containerIdentityDocument() (string, error) {
   120  	return m.curlDocument(identityUrl)
   121  }
   122  
   123  type EcrContainerIdentityDoc struct {
   124  	DockerId     string `json:"DockerId"`
   125  	Name         string `json:"Name"`
   126  	ContainerArn string `json:"ContainerARN"`
   127  }