Skip to content

Commit

Permalink
Implement Azure fetchers and asset provider (#1310)
Browse files Browse the repository at this point in the history
* Implement Azure fetchers and asset provider

* Fixing subscription to env

* up

* CR comments

---------

Co-authored-by: Orestis Floros <[email protected]>
  • Loading branch information
jeniawhite and orestisfl authored Sep 21, 2023
1 parent a961057 commit 83fa2d8
Show file tree
Hide file tree
Showing 11 changed files with 865 additions and 4 deletions.
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
cloud.google.com/go/asset v1.14.1
cloud.google.com/go/iam v1.1.2
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.1
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.8.1
github.com/aquasecurity/go-dep-parser v0.0.0-20230605080024-b71d9356a6c6
github.com/aquasecurity/trivy v0.42.1
github.com/aquasecurity/trivy-db v0.0.0-20230515061101-378ab9ed302c
Expand Down Expand Up @@ -94,7 +95,7 @@ require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652 // indirect
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0-beta.3
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
Expand Down Expand Up @@ -454,7 +455,7 @@ require (
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
google.golang.org/grpc v1.55.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
google.golang.org/protobuf v1.30.0
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,14 @@ github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9mo
github.com/Azure/azure-sdk-for-go v56.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 h1:/iHxaJhsFr0+xVFfbMr5vxz848jyiWuIEDhYq3y5odY=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0-beta.3 h1:XV/HZqgyUQQAc1/UwYXK/p9PyPuDrprwSXcKARy183U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0-beta.3/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.1 h1:LNHhpdK7hzUcx/k1LIcuh5k7k1LGIWLQfCjaneSj7Fc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.1/go.mod h1:uE9zaUfEQT/nbQjVi2IblCG9iaLtZsuYZ8ne+PuQ02M=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.8.1 h1:nGiU2ovpbtkcC3x+g/wNHV4S9TOIYe2/yOVAj3wiGHI=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.8.1/go.mod h1:T3ZgvD1aRKu12mEA0fU3PPvI7V0Nh0wzIdK0QMBhf0Y=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
Expand Down
4 changes: 4 additions & 0 deletions resources/fetching/fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ const (
KeyManagement = "key-management"
ProjectManagement = "project-management"
DataProcessing = "data-processing"

// Azure resource types
AzureVMType = "azure-vm"
AzureStorageAccountType = "azure-storage-account"
)

// Fetcher represents a data fetcher.
Expand Down
103 changes: 103 additions & 0 deletions resources/fetching/fetchers/azure/assets_fetcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package fetchers

import (
"context"

"github.com/elastic/elastic-agent-libs/logp"
"golang.org/x/exp/maps"

"github.com/elastic/cloudbeat/resources/fetching"
"github.com/elastic/cloudbeat/resources/providers/azurelib/inventory"
)

type AzureAssetsFetcher struct {
log *logp.Logger
resourceCh chan fetching.ResourceInfo
provider inventory.ServiceAPI
}

type AzureResource struct {
Type string
SubType string
Asset inventory.AzureAsset `json:"asset,omitempty"`
}

var AzureResourceTypes = map[string]string{
inventory.VirtualMachineAssetType: fetching.AzureVMType,
inventory.StorageAccountAssetType: fetching.AzureStorageAccountType,
}

func NewAzureAssetsFetcher(log *logp.Logger, ch chan fetching.ResourceInfo, provider inventory.ServiceAPI) *AzureAssetsFetcher {
return &AzureAssetsFetcher{
log: log,
resourceCh: ch,
provider: provider,
}
}

func (f *AzureAssetsFetcher) Fetch(ctx context.Context, cMetadata fetching.CycleMetadata) error {
f.log.Info("Starting AzureAssetsFetcher.Fetch")
// TODO: Maybe we should use a query per type instead of listing all assets in a single query
// This might be relevant if we'd like to fetch assets in parallel in order to evaluate a rule that uses multiple resources
assets, err := f.provider.ListAllAssetTypesByName(maps.Keys(AzureResourceTypes))
if err != nil {
return err
}

for _, asset := range assets {
select {
case <-ctx.Done():
f.log.Infof("AzureAssetsFetcher.Fetch context err: %s", ctx.Err().Error())
return nil
case f.resourceCh <- fetching.ResourceInfo{
CycleMetadata: cMetadata,
Resource: &AzureResource{
Type: AzureResourceTypes[asset.Type],
SubType: getAzureSubType(asset.Type),
Asset: asset,
},
}:
}
}

return nil
}

func getAzureSubType(assetType string) string {
return ""
}

func (f *AzureAssetsFetcher) Stop() {}

func (r *AzureResource) GetData() any {
return r.Asset
}

func (r *AzureResource) GetMetadata() (fetching.ResourceMetadata, error) {
return fetching.ResourceMetadata{
ID: r.Asset.Id,
Type: r.Type,
SubType: r.SubType,
Name: r.Asset.Name,
Region: r.Asset.Location,
}, nil
}

func (r *AzureResource) GetElasticCommonData() (map[string]any, error) { return nil, nil }
121 changes: 121 additions & 0 deletions resources/fetching/fetchers/azure/assets_fetcher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package fetchers

import (
"context"
"testing"

"github.com/samber/lo"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"

"github.com/elastic/cloudbeat/resources/fetching"
"github.com/elastic/cloudbeat/resources/providers/azurelib/inventory"
"github.com/elastic/cloudbeat/resources/utils/testhelper"
)

type AzureAssetsFetcherTestSuite struct {
suite.Suite

resourceCh chan fetching.ResourceInfo
}

func TestAzureAssetsFetcherTestSuite(t *testing.T) {
s := new(AzureAssetsFetcherTestSuite)

suite.Run(t, s)
}

func (s *AzureAssetsFetcherTestSuite) SetupTest() {
s.resourceCh = make(chan fetching.ResourceInfo, 50)
}

func (s *AzureAssetsFetcherTestSuite) TearDownTest() {
close(s.resourceCh)
}

func (s *AzureAssetsFetcherTestSuite) TestFetcher_Fetch() {
ctx := context.Background()
mockInventoryService := &inventory.MockServiceAPI{}
fetcher := AzureAssetsFetcher{
log: testhelper.NewLogger(s.T()),
resourceCh: s.resourceCh,
provider: mockInventoryService,
}

mockAssets := []inventory.AzureAsset{
{
Id: "id1",
Name: "name1",
Location: "location1",
Properties: map[string]interface{}{"key1": "value1"},
ResourceGroup: "rg1",
SubscriptionId: "subId1",
TenantId: "tenantId1",
Type: inventory.VirtualMachineAssetType,
},
{
Id: "id2",
Name: "name2",
Location: "location2",
Properties: map[string]interface{}{"key2": "value2"},
ResourceGroup: "rg2",
SubscriptionId: "subId2",
TenantId: "tenantId2",
Type: inventory.StorageAccountAssetType,
},
}

mockInventoryService.On("ListAllAssetTypesByName", mock.MatchedBy(func(assets []string) bool {
return true
})).Return(
mockAssets, nil,
)

err := fetcher.Fetch(ctx, fetching.CycleMetadata{})
s.NoError(err)
results := testhelper.CollectResources(s.resourceCh)

s.Equal(len(AzureResourceTypes), len(results))

lo.ForEach(results, func(r fetching.ResourceInfo, index int) {
data := r.GetData()
s.NotNil(data)
resource := data.(inventory.AzureAsset)
s.NotEmpty(resource)
s.Equal(mockAssets[index].Id, resource.Id)
s.Equal(mockAssets[index].Name, resource.Name)
s.Equal(mockAssets[index].Location, resource.Location)
s.Equal(mockAssets[index].Properties, resource.Properties)
s.Equal(mockAssets[index].ResourceGroup, resource.ResourceGroup)
s.Equal(mockAssets[index].SubscriptionId, resource.SubscriptionId)
s.Equal(mockAssets[index].TenantId, resource.TenantId)
s.Equal(mockAssets[index].Type, resource.Type)
meta, err := r.GetMetadata()
s.NoError(err)
s.NotNil(meta)
s.NoError(err)
s.NotEmpty(meta)
s.Equal(mockAssets[index].Id, meta.ID)
s.Equal(AzureResourceTypes[mockAssets[index].Type], meta.Type)
s.Equal("", meta.SubType)
s.Equal(mockAssets[index].Name, meta.Name)
s.Equal(mockAssets[index].Location, meta.Region)
})
}
37 changes: 37 additions & 0 deletions resources/fetching/preset/azure_factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package preset

import (
"github.com/elastic/elastic-agent-libs/logp"

"github.com/elastic/cloudbeat/resources/fetching"
fetchers "github.com/elastic/cloudbeat/resources/fetching/fetchers/azure"
"github.com/elastic/cloudbeat/resources/fetching/registry"
"github.com/elastic/cloudbeat/resources/providers/azurelib/inventory"
)

func NewCisAzureFactory(log *logp.Logger, ch chan fetching.ResourceInfo, inventory inventory.ServiceAPI) (registry.FetchersMap, error) {
log.Infof("Initializing Azure fetchers")
m := make(registry.FetchersMap)

assetsFetcher := fetchers.NewAzureAssetsFetcher(log, ch, inventory)
m["azure_cloud_assets_fetcher"] = registry.RegisteredFetcher{Fetcher: assetsFetcher}

return m, nil
}
23 changes: 23 additions & 0 deletions resources/providers/azurelib/inventory/asset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package inventory

const (
VirtualMachineAssetType = "microsoft.compute/virtualmachines"
StorageAccountAssetType = "microsoft.storage/storageaccounts"
)
Loading

0 comments on commit 83fa2d8

Please sign in to comment.