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 }