Skip to content

Commit 76b4300

Browse files
authored
✨ os cloud resource (#5284)
* ✨ Set `asset.kind` to `virtualmachine` when in cloud Signed-off-by: Salim Afiune Maya <afiune@mondoo.com> * ✨ asset kinds inside inventory package Signed-off-by: Salim Afiune Maya <afiune@mondoo.com> * 🧹 use new asset kinds Signed-off-by: Salim Afiune Maya <afiune@mondoo.com> * ✨ os cloud resource Signed-off-by: Salim Afiune Maya <afiune@mondoo.com> * 🛠️ move metadata crawler to its own package Signed-off-by: Salim Afiune Maya <afiune@mondoo.com> * ✨ adds test for new metadata crawler package Signed-off-by: Salim Afiune Maya <afiune@mondoo.com> * 🧹 clean os.lr.manifest.yaml Signed-off-by: Salim Afiune Maya <afiune@mondoo.com> * ✂️ revert asset kind changes on cloud providers Signed-off-by: Salim Afiune Maya <afiune@mondoo.com> * ✨ network conversion Signed-off-by: Salim Afiune Maya <afiune@mondoo.com> * ✨ use `clouddetect` by default on any `os` scan Signed-off-by: Salim Afiune Maya <afiune@mondoo.com> * 🐛 revert prev commit and instead swap default detectors Signed-off-by: Salim Afiune Maya <afiune@mondoo.com> * ⭐️ make clouddetect return cloud provider type Signed-off-by: Salim Afiune Maya <afiune@mondoo.com> * 🐛 skip the right multiline field Signed-off-by: Salim Afiune Maya <afiune@mondoo.com> * 🐛 rename resource `cloud.instance` to `cloudInstance` Signed-off-by: Salim Afiune Maya <afiune@mondoo.com> --------- Signed-off-by: Salim Afiune Maya <afiune@mondoo.com>
1 parent 09b17d6 commit 76b4300

25 files changed

+1535
-122
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ require (
319319
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
320320
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
321321
github.com/smarty/assertions v1.15.1 // indirect
322+
github.com/stretchr/objx v0.5.2 // indirect
322323
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 // indirect
323324
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
324325
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect

providers/os/id/awsec2/awsec2.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ type Identity struct {
1818
InstanceName string
1919
AccountID string
2020
}
21+
2122
type InstanceIdentifier interface {
2223
Identify() (Identity, error)
24+
RawMetadata() (any, error)
2325
}
2426

2527
func Resolve(conn shared.Connection, pf *inventory.Platform) (InstanceIdentifier, error) {

providers/os/id/awsec2/metadata_cmd.go

Lines changed: 66 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ package awsec2
55

66
import (
77
"context"
8+
"crypto/sha256"
9+
"encoding/hex"
810
"encoding/json"
9-
"fmt"
1011
"io"
1112
"strings"
1213

@@ -15,35 +16,16 @@ import (
1516
"github.com/aws/aws-sdk-go-v2/service/ec2"
1617
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
1718
"github.com/cockroachdb/errors"
19+
"github.com/rs/zerolog/log"
1820
"go.mondoo.com/cnquery/v11/providers-sdk/v1/inventory"
1921
"go.mondoo.com/cnquery/v11/providers/os/connection/shared"
20-
"go.mondoo.com/cnquery/v11/providers/os/resources/powershell"
22+
"go.mondoo.com/cnquery/v11/providers/os/id/metadata"
2123
)
2224

2325
const (
24-
identityUrl = `-H "X-aws-ec2-metadata-token: %s" -v http://169.254.169.254/latest/dynamic/instance-identity/document`
25-
tokenUrl = `-H "X-aws-ec2-metadata-token-ttl-seconds: 21600" -X PUT "http://169.254.169.254/latest/api/token"`
26-
tagNameUrl = `-H "X-aws-ec2-metadata-token: %s" -v http://169.254.169.254/latest/meta-data/tags/instance/Name`
27-
28-
identityUrlWindows = `
29-
$Headers = @{
30-
"X-aws-ec2-metadata-token" = %s
31-
}
32-
Invoke-RestMethod -TimeoutSec 1 -Headers $Headers -URI http://169.254.169.254/latest/dynamic/instance-identity/document -UseBasicParsing | ConvertTo-Json
33-
`
34-
35-
tokenUrlWindows = `
36-
$Headers = @{
37-
"X-aws-ec2-metadata-token-ttl-seconds" = "21600"
38-
}
39-
Invoke-RestMethod -Method Put -Uri "http://169.254.169.254/latest/api/token" -Headers $Headers -TimeoutSec 1 -UseBasicParsing
40-
`
41-
tagNameUrlWindows = `
42-
$Headers = @{
43-
"X-aws-ec2-metadata-token" = %s
44-
}
45-
Invoke-RestMethod -Method Put -Uri "http://169.254.169.254/latest/meta-data/tags/instance/Name" -Headers $Headers -TimeoutSec 1 -UseBasicParsing
46-
`
26+
identityURLPath = "dynamic/instance-identity/document"
27+
metadataURLPath = "meta-data/"
28+
tagNameURLPath = "meta-data/tags/instance/Name"
4729
)
4830

4931
func NewCommandInstanceMetadata(conn shared.Connection, pf *inventory.Platform, config *aws.Config) *CommandInstanceMetadata {
@@ -58,13 +40,26 @@ type CommandInstanceMetadata struct {
5840
conn shared.Connection
5941
platform *inventory.Platform
6042
config *aws.Config
43+
44+
// used internally to avoid fetching a token multiple times
45+
token string
46+
}
47+
48+
func (m *CommandInstanceMetadata) RawMetadata() (any, error) {
49+
return metadata.Crawl(m, metadataURLPath)
50+
}
51+
52+
func (m *CommandInstanceMetadata) GetMetadataValue(path string) (string, error) {
53+
return m.curlDocument(path)
6154
}
6255

6356
func (m *CommandInstanceMetadata) Identify() (Identity, error) {
6457
instanceDocument, err := m.instanceIdentityDocument()
6558
if err != nil {
6659
return Identity{}, err
6760
}
61+
log.Debug().Str("instance_document", instanceDocument).Msg("identity")
62+
6863
// parse into struct
6964
doc := imds.InstanceIdentityDocument{}
7065
if err := json.NewDecoder(strings.NewReader(instanceDocument)).Decode(&doc); err != nil {
@@ -102,81 +97,63 @@ func (m *CommandInstanceMetadata) Identify() (Identity, error) {
10297
}, nil
10398
}
10499

105-
type metadataType int
106-
107-
const (
108-
document metadataType = iota
109-
instanceNameTag
110-
)
100+
func (m *CommandInstanceMetadata) curlDocument(metadataPath string) (string, error) {
101+
token, err := m.getToken()
102+
if err != nil {
103+
return "", err
104+
}
111105

112-
func (m *CommandInstanceMetadata) curlDocument(metadataType metadataType) (string, error) {
106+
var commandString string
113107
switch {
114108
case m.platform.IsFamily(inventory.FAMILY_UNIX):
115-
cmd, err := m.conn.RunCommand("curl " + tokenUrl)
116-
if err != nil {
117-
return "", err
118-
}
119-
data, err := io.ReadAll(cmd.Stdout)
120-
if err != nil {
121-
return "", err
122-
}
123-
tokenString := strings.TrimSpace(string(data))
124-
125-
commandScript := ""
126-
switch metadataType {
127-
case document:
128-
commandScript = "curl " + fmt.Sprintf(identityUrl, tokenString)
129-
case instanceNameTag:
130-
commandScript = "curl " + fmt.Sprintf(tagNameUrl, tokenString)
131-
}
132-
133-
cmd, err = m.conn.RunCommand(commandScript)
134-
if err != nil {
135-
return "", err
136-
}
137-
data, err = io.ReadAll(cmd.Stdout)
138-
if err != nil {
139-
return "", err
140-
}
141-
142-
return strings.TrimSpace(string(data)), nil
109+
commandString = unixMetadataCmdString(token, metadataPath)
143110
case m.platform.IsFamily(inventory.FAMILY_WINDOWS):
144-
tokenPwshEncoded := powershell.Encode(tokenUrlWindows)
145-
cmd, err := m.conn.RunCommand(tokenPwshEncoded)
146-
if err != nil {
147-
return "", err
148-
}
149-
data, err := io.ReadAll(cmd.Stdout)
150-
if err != nil {
151-
return "", err
152-
}
153-
tokenString := strings.TrimSpace(string(data))
154-
155-
commandScript := ""
156-
switch metadataType {
157-
case document:
158-
commandScript = powershell.Encode(fmt.Sprintf(identityUrlWindows, tokenString))
159-
case instanceNameTag:
160-
commandScript = powershell.Encode(fmt.Sprintf(tagNameUrlWindows, tokenString))
161-
}
111+
commandString = windowsMetadataCmdString(token, metadataPath)
112+
default:
113+
return "", errors.New("your platform is not supported by aws metadata identifier resource")
114+
}
162115

163-
cmd, err = m.conn.RunCommand(commandScript)
164-
if err != nil {
165-
return "", err
166-
}
167-
data, err = io.ReadAll(cmd.Stdout)
168-
if err != nil {
169-
return "", err
170-
}
116+
log.Debug().Str("command_string", commandString).Msg("running os command")
117+
cmd, err := m.conn.RunCommand(commandString)
118+
if err != nil {
119+
return "", err
120+
}
121+
log.Debug().Str("hash", hashCmd(commandString)).Msg("executed")
122+
data, err := io.ReadAll(cmd.Stdout)
123+
log.Debug().Msg("read")
124+
return strings.TrimSpace(string(data)), err
125+
}
126+
127+
func hashCmd(message string) string {
128+
hash := sha256.New()
129+
hash.Write([]byte(message))
130+
return hex.EncodeToString(hash.Sum(nil))
131+
}
132+
func (m *CommandInstanceMetadata) getToken() (string, error) {
133+
if m.token != "" {
134+
return m.token, nil
135+
}
171136

172-
return strings.TrimSpace(string(data)), nil
137+
var commandString string
138+
switch {
139+
case m.platform.IsFamily(inventory.FAMILY_UNIX):
140+
commandString = unixTokenCmdString()
141+
case m.platform.IsFamily(inventory.FAMILY_WINDOWS):
142+
commandString = windowsTokenCmdString()
173143
default:
174144
return "", errors.New("your platform is not supported by aws metadata identifier resource")
175145
}
146+
147+
cmd, err := m.conn.RunCommand(commandString)
148+
if err != nil {
149+
return "", err
150+
}
151+
data, err := io.ReadAll(cmd.Stdout)
152+
return strings.TrimSpace(string(data)), err
176153
}
177154

178155
func (m *CommandInstanceMetadata) instanceNameTag() (string, error) {
179-
res, err := m.curlDocument(instanceNameTag)
156+
res, err := m.curlDocument(tagNameURLPath)
180157
if err != nil {
181158
return "", err
182159
}
@@ -187,5 +164,5 @@ func (m *CommandInstanceMetadata) instanceNameTag() (string, error) {
187164
}
188165

189166
func (m *CommandInstanceMetadata) instanceIdentityDocument() (string, error) {
190-
return m.curlDocument(document)
167+
return m.curlDocument(identityURLPath)
191168
}

providers/os/id/awsec2/metadata_local.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/aws/aws-sdk-go-v2/aws"
1111
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
1212
"github.com/aws/aws-sdk-go-v2/service/ec2"
13+
"go.mondoo.com/cnquery/v11/providers/os/id/metadata"
1314

1415
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
1516
)
@@ -25,6 +26,15 @@ type LocalEc2InstanceMetadata struct {
2526
config aws.Config
2627
}
2728

29+
func (m *LocalEc2InstanceMetadata) RawMetadata() (any, error) {
30+
return metadata.Crawl(m, "")
31+
}
32+
33+
func (m *LocalEc2InstanceMetadata) GetMetadataValue(path string) (string, error) {
34+
client := imds.NewFromConfig(m.config)
35+
return m.getMetadataValue(client, path)
36+
}
37+
2838
func (m *LocalEc2InstanceMetadata) Identify() (Identity, error) {
2939
metadata := imds.NewFromConfig(m.config)
3040
ec2svc := ec2.NewFromConfig(m.config)
@@ -68,7 +78,7 @@ func (m *LocalEc2InstanceMetadata) Identify() (Identity, error) {
6878

6979
// gets the metadata at the relative specified path. The base path is /latest/meta-data
7080
// so the path param needs to only specify which metadata path is requested
71-
func (m *LocalEc2InstanceMetadata) getMetadataValue(client *imds.Client, path string) (value string, err error) {
81+
func (m *LocalEc2InstanceMetadata) getMetadataValue(client *imds.Client, path string) (string, error) {
7282
output, err := client.GetMetadata(context.TODO(), &imds.GetMetadataInput{
7383
Path: path,
7484
})
@@ -80,6 +90,5 @@ func (m *LocalEc2InstanceMetadata) getMetadataValue(client *imds.Client, path st
8090
if err != nil {
8191
return "", err
8292
}
83-
resp := string(bytes)
84-
return resp, err
93+
return string(bytes), nil
8594
}

0 commit comments

Comments
 (0)