From cdbd1b63c5618cef00f30d4b5d8ccef5dbe517be Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 17:42:44 +0100 Subject: [PATCH] [8.x](backport #2654) [Asset Inventory][GCP] Populate `related.entity` field for resources implemented so far (#2806) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Asset Inventory][GCP] Populate `related.entity` field for resources implemented so far (#2654) (cherry picked from commit daba272a6b634d852a7102db51c5be0d2b81899f) Co-authored-by: Kuba SoboĊ„ --- internal/inventory/asset.go | 11 ++- .../inventory/gcpfetcher/fetcher_assets.go | 75 +++++++++++++++++++ .../gcpfetcher/fetcher_assets_test.go | 1 + internal/inventory/inventory.go | 7 +- 4 files changed, 91 insertions(+), 3 deletions(-) diff --git a/internal/inventory/asset.go b/internal/inventory/asset.go index 33f85a2f89..99604dee43 100644 --- a/internal/inventory/asset.go +++ b/internal/inventory/asset.go @@ -238,8 +238,9 @@ type AssetEvent struct { // Asset contains the identifiers of the asset type Asset struct { - Id []string `json:"id"` - Name string `json:"name"` + Id []string `json:"id"` + RelatedEntityId []string `json:"related_entity_id"` + Name string `json:"name"` AssetClassification Tags map[string]string `json:"tags"` Raw any `json:"raw"` @@ -354,6 +355,12 @@ func WithRawAsset(raw any) AssetEnricher { } } +func WithRelatedAssetIds(ids []string) AssetEnricher { + return func(a *AssetEvent) { + a.Asset.RelatedEntityId = ids + } +} + func WithTags(tags map[string]string) AssetEnricher { return func(a *AssetEvent) { if len(tags) == 0 { diff --git a/internal/inventory/gcpfetcher/fetcher_assets.go b/internal/inventory/gcpfetcher/fetcher_assets.go index 59e01b76a6..374a8db1c0 100644 --- a/internal/inventory/gcpfetcher/fetcher_assets.go +++ b/internal/inventory/gcpfetcher/fetcher_assets.go @@ -21,6 +21,8 @@ import ( "context" "github.com/elastic/elastic-agent-libs/logp" + "github.com/samber/lo" + "google.golang.org/protobuf/types/known/structpb" "github.com/elastic/cloudbeat/internal/inventory" gcpinventory "github.com/elastic/cloudbeat/internal/resources/providers/gcplib/inventory" @@ -86,6 +88,9 @@ func (f *assetsInventory) fetch(ctx context.Context, assetChan chan<- inventory. []string{item.Name}, item.Name, inventory.WithRawAsset(item), + inventory.WithRelatedAssetIds( + f.findRelatedAssetIds(classification.SubType, item), + ), inventory.WithCloud(inventory.AssetCloud{ Provider: inventory.GcpCloudProvider, Account: inventory.AssetCloudAccount{ @@ -103,3 +108,73 @@ func (f *assetsInventory) fetch(ctx context.Context, assetChan chan<- inventory. ) } } + +func (f *assetsInventory) findRelatedAssetIds(subType inventory.AssetSubType, item *gcpinventory.ExtendedGcpAsset) []string { + ids := []string{} + ids = append(ids, item.Ancestors...) + if item.Resource != nil { + ids = append(ids, item.Resource.Parent) + } + + ids = append(ids, f.findRelatedAssetIdsForSubType(subType, item)...) + + ids = lo.Compact(ids) + ids = lo.Uniq(ids) + return ids +} + +func (f *assetsInventory) findRelatedAssetIdsForSubType(subType inventory.AssetSubType, item *gcpinventory.ExtendedGcpAsset) []string { + ids := []string{} + + var fields map[string]*structpb.Value + if item.Resource != nil && item.Resource.Data != nil { + fields = item.GetResource().GetData().GetFields() + } + + switch subType { + case inventory.SubTypeGcpInstance: + if v, ok := fields["networkInterfaces"]; ok { + for _, networkInterface := range v.GetListValue().GetValues() { + networkInterfaceFields := networkInterface.GetStructValue().GetFields() + ids = appendIfExists(ids, networkInterfaceFields, "network") + ids = appendIfExists(ids, networkInterfaceFields, "subnetwork") + } + } + if v, ok := fields["serviceAccounts"]; ok { + for _, serviceAccount := range v.GetListValue().GetValues() { + serviceAccountFields := serviceAccount.GetStructValue().GetFields() + ids = appendIfExists(ids, serviceAccountFields, "email") + } + } + if v, ok := fields["disks"]; ok { + for _, disk := range v.GetListValue().GetValues() { + diskFields := disk.GetStructValue().GetFields() + ids = appendIfExists(ids, diskFields, "source") + } + } + ids = appendIfExists(ids, fields, "machineType") + ids = appendIfExists(ids, fields, "zone") + case inventory.SubTypeGcpFirewall, inventory.SubTypeGcpSubnet: + ids = appendIfExists(ids, fields, "network") + case inventory.SubTypeGcpProject, inventory.SubTypeGcpBucket: + if item.IamPolicy == nil { + break + } + for _, binding := range item.IamPolicy.Bindings { + ids = append(ids, binding.Role) + ids = append(ids, binding.Members...) + } + default: + return ids + } + + return ids +} + +func appendIfExists(slice []string, fields map[string]*structpb.Value, key string) []string { + value, ok := fields[key] + if !ok { + return slice + } + return append(slice, value.GetStringValue()) +} diff --git a/internal/inventory/gcpfetcher/fetcher_assets_test.go b/internal/inventory/gcpfetcher/fetcher_assets_test.go index 12a04e7787..5e8b39c3c8 100644 --- a/internal/inventory/gcpfetcher/fetcher_assets_test.go +++ b/internal/inventory/gcpfetcher/fetcher_assets_test.go @@ -53,6 +53,7 @@ func TestAccountFetcher_Fetch_Assets(t *testing.T) { []string{"/projects//some_resource"}, "/projects//some_resource", inventory.WithRawAsset(assets[0]), + inventory.WithRelatedAssetIds([]string{}), inventory.WithCloud(inventory.AssetCloud{ Provider: inventory.GcpCloudProvider, Account: inventory.AssetCloudAccount{ diff --git a/internal/inventory/inventory.go b/internal/inventory/inventory.go index c5a5b171ca..3cc7438127 100644 --- a/internal/inventory/inventory.go +++ b/internal/inventory/inventory.go @@ -103,6 +103,11 @@ func (a *AssetInventory) Run(ctx context.Context) { func (a *AssetInventory) publish(assets []AssetEvent) { events := lo.Map(assets, func(e AssetEvent, _ int) beat.Event { + var relatedEntity []string + relatedEntity = append(relatedEntity, e.Asset.Id...) + if len(e.Asset.RelatedEntityId) > 0 { + relatedEntity = append(relatedEntity, e.Asset.RelatedEntityId...) + } return beat.Event{ Meta: mapstr.M{libevents.FieldMetaIndex: generateIndex(e.Asset)}, Timestamp: a.now(), @@ -113,7 +118,7 @@ func (a *AssetInventory) publish(assets []AssetEvent) { "network": e.Network, "iam": e.IAM, "resource_policies": e.ResourcePolicies, - "related.entity": e.Asset.Id, + "related.entity": relatedEntity, }, } })