go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/provider/platform.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package provider 5 6 import ( 7 "errors" 8 "fmt" 9 10 "github.com/rs/zerolog/log" 11 "go.mondoo.com/cnquery/providers-sdk/v1/inventory" 12 "go.mondoo.com/cnquery/providers/os/connection/shared" 13 "go.mondoo.com/cnquery/providers/os/detector" 14 "go.mondoo.com/cnquery/providers/os/id/awsec2" 15 "go.mondoo.com/cnquery/providers/os/id/awsecs" 16 "go.mondoo.com/cnquery/providers/os/id/clouddetect" 17 "go.mondoo.com/cnquery/providers/os/id/hostname" 18 "go.mondoo.com/cnquery/providers/os/id/ids" 19 "go.mondoo.com/cnquery/providers/os/id/machineid" 20 "go.mondoo.com/cnquery/providers/os/id/sshhostkey" 21 ) 22 23 type PlatformFingerprint struct { 24 PlatformIDs []string 25 Name string 26 Runtime string 27 Kind string 28 RelatedAssets []PlatformFingerprint 29 } 30 31 type PlatformInfo struct { 32 IDs []string 33 Name string 34 RelatedPlatformIDs []string 35 } 36 37 func IdentifyPlatform(conn shared.Connection, p *inventory.Platform, idDetectors []string) (*PlatformFingerprint, error) { 38 var ok bool 39 if p == nil { 40 p, ok = detector.DetectOS(conn) 41 if !ok { 42 return nil, errors.New("cannot detect os") 43 } 44 } 45 46 var fingerprint PlatformFingerprint 47 var ids []string 48 var relatedIds []string 49 50 for i := range idDetectors { 51 idDetector := idDetectors[i] 52 platformInfo, err := GatherPlatformInfo(conn, p, idDetector) 53 if err != nil { 54 // we only err if we found zero platform ids, if we try multiple, a fail of an individual one is okay 55 log.Debug().Err(err).Str("detector", string(idDetector)).Msg("could not determine platform info") 56 continue 57 } 58 if len(platformInfo.IDs) > 0 { 59 ids = append(ids, platformInfo.IDs...) 60 } 61 if len(platformInfo.RelatedPlatformIDs) > 0 { 62 relatedIds = append(relatedIds, platformInfo.RelatedPlatformIDs...) 63 } 64 65 if len(platformInfo.Name) > 0 { 66 fingerprint.Name = platformInfo.Name 67 } else { 68 // check if we get a name for the asset, eg. aws instance id 69 for _, id := range platformInfo.IDs { 70 name := GatherNameForPlatformId(id) 71 if name != "" { 72 fingerprint.Name = name 73 } 74 } 75 } 76 // check whether we can extract runtime and kind information 77 for _, id := range platformInfo.IDs { 78 runtime, kind := ExtractPlatformAndKindFromPlatformId(id) 79 if runtime != "" { 80 fingerprint.Runtime = runtime 81 fingerprint.Kind = kind 82 } 83 } 84 } 85 86 // if we found zero platform ids something went wrong 87 if len(ids) == 0 { 88 return nil, errors.New("could not determine a platform identifier") 89 } 90 91 fingerprint.PlatformIDs = ids 92 for _, v := range relatedIds { 93 fingerprint.RelatedAssets = append(fingerprint.RelatedAssets, PlatformFingerprint{ 94 PlatformIDs: []string{v}, 95 Name: GatherNameForPlatformId(v), 96 }) 97 } 98 99 log.Debug().Interface("id-detector", idDetectors).Strs("platform-ids", ids).Msg("detected platform ids") 100 return &fingerprint, nil 101 } 102 103 func GatherNameForPlatformId(id string) string { 104 if awsec2.IsValidMondooInstanceId(id) { 105 structId, _ := awsec2.ParseMondooInstanceID(id) 106 return structId.Id 107 } else if accountID, err := awsec2.ParseMondooAccountID(id); err == nil { 108 return fmt.Sprintf("AWS Account %s", accountID) 109 } 110 return "" 111 } 112 113 func ExtractPlatformAndKindFromPlatformId(id string) (string, string) { 114 if awsec2.ParseEc2PlatformID(id) != nil { 115 return "aws-ec2", "virtual-machine" 116 } else if awsec2.IsValidMondooAccountId(id) { 117 return "aws", "api" 118 } else if awsecs.IsValidMondooECSContainerId(id) { 119 return "aws-ecs", "container" 120 } 121 return "", "" 122 } 123 124 func GatherPlatformInfo(conn shared.Connection, pf *inventory.Platform, idDetector string) (*PlatformInfo, error) { 125 var identifier string 126 switch { 127 case idDetector == ids.IdDetector_Hostname: 128 // NOTE: we need to be careful with hostname's since they are not required to be unique 129 hostname, ok := hostname.Hostname(conn, pf) 130 if ok && len(hostname) > 0 { 131 identifier = "//platformid.api.mondoo.app/hostname/" + hostname 132 return &PlatformInfo{ 133 IDs: []string{identifier}, 134 Name: hostname, 135 RelatedPlatformIDs: []string{}, 136 }, nil 137 } 138 return &PlatformInfo{}, nil 139 case idDetector == ids.IdDetector_MachineID: 140 guid, hostErr := machineid.MachineId(conn, pf) 141 if hostErr == nil && len(guid) > 0 { 142 identifier = "//platformid.api.mondoo.app/machineid/" + guid 143 return &PlatformInfo{ 144 IDs: []string{identifier}, 145 Name: "", 146 RelatedPlatformIDs: []string{}, 147 }, hostErr 148 } 149 return &PlatformInfo{}, nil 150 case idDetector == ids.IdDetector_AwsEcs: 151 metadata, err := awsecs.Resolve(conn, pf) 152 if err != nil { 153 return nil, err 154 } 155 ident, err := metadata.Identify() 156 if err != nil { 157 return nil, err 158 } 159 if len(ident.PlatformIds) != 0 { 160 return &PlatformInfo{ 161 IDs: ident.PlatformIds, 162 Name: ident.Name, 163 RelatedPlatformIDs: []string{ident.AccountPlatformID}, 164 }, nil 165 } 166 return &PlatformInfo{}, nil 167 case idDetector == ids.IdDetector_CloudDetect: 168 identifier, name, relatedIdentifiers := clouddetect.Detect(conn, pf) 169 if identifier != "" { 170 return &PlatformInfo{ 171 IDs: []string{identifier}, 172 Name: name, 173 RelatedPlatformIDs: relatedIdentifiers, 174 }, nil 175 } 176 return &PlatformInfo{}, nil 177 case idDetector == ids.IdDetector_SshHostkey: 178 identifier, err := sshhostkey.Detect(conn, pf) 179 if err != nil { 180 return nil, err 181 } 182 return &PlatformInfo{ 183 IDs: identifier, 184 Name: "", 185 RelatedPlatformIDs: []string{}, 186 }, nil 187 default: 188 return nil, errors.New(fmt.Sprintf("the provided id-detector is not supported: %s", idDetector)) 189 } 190 }