internal/resources/providers/azurelib/inventory/keyvault_provider.go (166 lines of code) (raw):

// 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 import ( "context" "fmt" "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/monitor/armmonitor" "github.com/samber/lo" "github.com/elastic/cloudbeat/internal/infra/clog" "github.com/elastic/cloudbeat/internal/resources/utils/maps" "github.com/elastic/cloudbeat/internal/resources/utils/pointers" ) type azureKeyVaultWrapper struct { AssetKeyVaultKeys func(ctx context.Context, subscriptionID string, resourceGroupName string, vaultName string) ([]armkeyvault.KeysClientListResponse, error) AssetKeyVaultSecrets func(ctx context.Context, subscriptionID string, resourceGroupName string, vaultName string) ([]armkeyvault.SecretsClientListResponse, error) AssetDiagnosticSettings func(ctx context.Context, resourceURI string, options *armmonitor.DiagnosticSettingsClientListOptions) ([]armmonitor.DiagnosticSettingsClientListResponse, error) } func defaultAzureKeyVaultWrapper(diagnosticSettingsClient *armmonitor.DiagnosticSettingsClient, credentials azcore.TokenCredential) *azureKeyVaultWrapper { return &azureKeyVaultWrapper{ AssetKeyVaultKeys: func(ctx context.Context, subscriptionID string, resourceGroupName string, vaultName string) ([]armkeyvault.KeysClientListResponse, error) { client, err := armkeyvault.NewKeysClient(subscriptionID, credentials, nil) if err != nil { return nil, err } return readPager(ctx, client.NewListPager(resourceGroupName, vaultName, nil)) }, AssetKeyVaultSecrets: func(ctx context.Context, subscriptionID string, resourceGroupName string, vaultName string) ([]armkeyvault.SecretsClientListResponse, error) { client, err := armkeyvault.NewSecretsClient(subscriptionID, credentials, nil) if err != nil { return nil, err } return readPager(ctx, client.NewListPager(resourceGroupName, vaultName, nil)) }, AssetDiagnosticSettings: func(ctx context.Context, resourceURI string, options *armmonitor.DiagnosticSettingsClientListOptions) ([]armmonitor.DiagnosticSettingsClientListResponse, error) { return readPager(ctx, diagnosticSettingsClient.NewListPager(resourceURI, options)) }, } } type KeyVaultProviderAPI interface { ListKeyVaultKeys(ctx context.Context, vault AzureAsset) ([]AzureAsset, error) ListKeyVaultSecrets(ctx context.Context, vault AzureAsset) ([]AzureAsset, error) ListKeyVaultDiagnosticSettings(ctx context.Context, vault AzureAsset) ([]AzureAsset, error) } func NewKeyVaultProvider(log *clog.Logger, diagnosticSettingsClient *armmonitor.DiagnosticSettingsClient, credentials azcore.TokenCredential) KeyVaultProviderAPI { return &keyVaultProvider{ log: log, client: defaultAzureKeyVaultWrapper(diagnosticSettingsClient, credentials), } } type keyVaultProvider struct { log *clog.Logger client *azureKeyVaultWrapper } func (p *keyVaultProvider) ListKeyVaultDiagnosticSettings(ctx context.Context, vault AzureAsset) ([]AzureAsset, error) { p.log.Info("Listing Azure Vault Diagnostic Settings") responses, err := p.client.AssetDiagnosticSettings(ctx, vault.Id, nil) if err != nil { return nil, fmt.Errorf("error while retrieving vault diagnostic settings: vaultId: %v, error: %w", vault.Id, err) } return lo.FlatMap(responses, func(res armmonitor.DiagnosticSettingsClientListResponse, _ int) []AzureAsset { return lo.FilterMap(res.Value, func(setting *armmonitor.DiagnosticSettingsResource, _ int) (AzureAsset, bool) { return p.transformDiagnosticSetting(setting, vault) }) }), nil } func (p *keyVaultProvider) transformDiagnosticSetting(setting *armmonitor.DiagnosticSettingsResource, vault AzureAsset) (AzureAsset, bool) { if setting == nil { return AzureAsset{}, false } properties := map[string]any{} maps.AddIfNotNil(properties, "storageAccountId", setting.Properties.StorageAccountID) maps.AddIfSliceNotEmpty(properties, "logs", setting.Properties.Logs) if len(properties) == 0 { properties = nil } return AzureAsset{ Id: pointers.Deref(setting.ID), Name: pointers.Deref(setting.Name), DisplayName: "", Location: "", ResourceGroup: vault.ResourceGroup, SubscriptionId: vault.SubscriptionId, TenantId: vault.TenantId, Type: pointers.Deref(setting.Type), Properties: properties, }, true } func (p *keyVaultProvider) ListKeyVaultKeys(ctx context.Context, vault AzureAsset) ([]AzureAsset, error) { p.log.Info("Listing Azure Vault Keys") responses, err := p.client.AssetKeyVaultKeys(ctx, vault.SubscriptionId, vault.ResourceGroup, vault.Name) if err != nil { return nil, fmt.Errorf("error while retrieving vault keys: %w", err) } return lo.FlatMap(responses, func(res armkeyvault.KeysClientListResponse, _ int) []AzureAsset { return lo.FilterMap(res.Value, func(key *armkeyvault.Key, _ int) (AzureAsset, bool) { return p.transformKey(key, vault) }) }), nil } func (p *keyVaultProvider) transformKey(key *armkeyvault.Key, vault AzureAsset) (AzureAsset, bool) { if key == nil { return AzureAsset{}, false } properties := map[string]any{} maps.AddIfMapNotEmpty(properties, "tags", key.Tags) maps.AddIfNotNil(properties, "attributes", key.Properties.Attributes) maps.AddIfNotNil(properties, "curveName", key.Properties.CurveName) maps.AddIfSliceNotEmpty(properties, "keyOps", key.Properties.KeyOps) maps.AddIfNotNil(properties, "keySize", key.Properties.KeySize) maps.AddIfNotNil(properties, "keyUri", key.Properties.KeyURI) maps.AddIfNotNil(properties, "keyUriWithVersion", key.Properties.KeyURIWithVersion) maps.AddIfNotNil(properties, "kty", key.Properties.Kty) maps.AddIfNotNil(properties, "releasePolicy", key.Properties.ReleasePolicy) maps.AddIfNotNil(properties, "rotationPolicy", key.Properties.RotationPolicy) if len(properties) == 0 { properties = nil } return AzureAsset{ Id: pointers.Deref(key.ID), Name: pointers.Deref(key.Name), DisplayName: "", Location: pointers.Deref(key.Location), ResourceGroup: vault.ResourceGroup, SubscriptionId: vault.SubscriptionId, TenantId: vault.TenantId, Type: pointers.Deref(key.Type), Properties: properties, }, true } func (p *keyVaultProvider) ListKeyVaultSecrets(ctx context.Context, vault AzureAsset) ([]AzureAsset, error) { p.log.Info("Listing Azure Vault Secrets") responses, err := p.client.AssetKeyVaultSecrets(ctx, vault.SubscriptionId, vault.ResourceGroup, vault.Name) if err != nil { return nil, fmt.Errorf("error while retrieving vault secrets: %w", err) } return lo.FlatMap(responses, func(res armkeyvault.SecretsClientListResponse, _ int) []AzureAsset { return lo.FilterMap(res.Value, func(secret *armkeyvault.Secret, _ int) (AzureAsset, bool) { return p.transformSecret(secret, vault) }) }), nil } func (p *keyVaultProvider) transformSecret(secret *armkeyvault.Secret, vault AzureAsset) (AzureAsset, bool) { if secret == nil { return AzureAsset{}, false } properties := map[string]any{} maps.AddIfMapNotEmpty(properties, "tags", secret.Tags) maps.AddIfNotNil(properties, "attributes", secret.Properties.Attributes) maps.AddIfNotNil(properties, "contentType", secret.Properties.ContentType) maps.AddIfNotNil(properties, "secretUri", secret.Properties.SecretURI) maps.AddIfNotNil(properties, "secretUrlWithVersion", secret.Properties.SecretURIWithVersion) // secret.Properties.Value // do not use if len(properties) == 0 { properties = nil } return AzureAsset{ Id: pointers.Deref(secret.ID), Name: pointers.Deref(secret.Name), DisplayName: "", Location: pointers.Deref(secret.Location), ResourceGroup: vault.ResourceGroup, SubscriptionId: vault.SubscriptionId, TenantId: vault.TenantId, Type: pointers.Deref(secret.Type), Properties: properties, }, true }