-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Azure fetchers and asset provider #1310
Changes from all commits
bc33202
bed328c
229dc83
643aefa
c20fa48
46e80d3
b13c32f
4044768
4cdd79c
8f08bc3
53cb17c
52eeb17
5a58aa8
a9b164c
c05df25
79f6482
c3d188a
d1c4cee
3731861
5b216f1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 } |
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) | ||||||||||||||||||||||
Comment on lines
+111
to
+114
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
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) | ||||||||||||||||||||||
Comment on lines
+115
to
+119
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I would construct an expected |
||||||||||||||||||||||
}) | ||||||||||||||||||||||
} |
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 | ||
} |
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" | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any benefit to that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume that it might help us If we will need to collect multiple resource types to evaluate a single rule.