internal/resources/fetching/fetchers/azure/assets_enrincher_virtual_machine.go (98 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 fetchers import ( "context" "errors" "github.com/mitchellh/mapstructure" "github.com/elastic/cloudbeat/internal/resources/fetching/cycle" "github.com/elastic/cloudbeat/internal/resources/providers/azurelib/inventory" ) type vmNetworkSecurityGroupEnricher struct{} type networkProfile struct { NetworkInterfaces []networkInterface `mapstructure:"networkInterfaces"` } type networkInterface struct { Id string `mapstructure:"id"` } type extensionNetworkSecurityRules struct { Access string `mapstructure:"access"` DestinationPortRange string `mapstructure:"destinationPortRange"` DestinationPortRanges []string `mapstructure:"destinationPortRanges"` Direction string `mapstructure:"direction"` Protocol string `mapstructure:"protocol"` SourceAddressPrefix string `mapstructure:"sourceAddressPrefix"` SourceAddressPrefixes []string `mapstructure:"sourceAddressPrefixes"` } type networkSecurityRules struct { Properties extensionNetworkSecurityRules `mapstructure:"properties"` } func (e vmNetworkSecurityGroupEnricher) Enrich(_ context.Context, _ cycle.Metadata, assets []inventory.AzureAsset) error { // VMs are connected to NSGs via Network Interfaces (https://learn.microsoft.com/en-us/azure/virtual-network/network-overview) // Therefore, aggregate security rules by Network Interface to easy access later on securityRulesByNetworkInterface := make(map[string][]extensionNetworkSecurityRules) var errAgg error for _, asset := range assets { if asset.Type != inventory.NetworkSecurityGroupAssetType { continue } if err := e.extractNetworkSecurityGroupRules(asset, securityRulesByNetworkInterface); err != nil { errAgg = errors.Join(errAgg, err) } } for idx, asset := range assets { if asset.Type != inventory.VirtualMachineAssetType { continue } if err := e.addNetworkRulesToAssetExtensions(&asset, securityRulesByNetworkInterface); err != nil { errAgg = errors.Join(errAgg, err) } assets[idx] = asset } return errAgg } func (e vmNetworkSecurityGroupEnricher) extractNetworkSecurityGroupRules(asset inventory.AzureAsset, securityRulesByNetworkInterface map[string][]extensionNetworkSecurityRules) error { var nics []networkInterface if err := mapstructure.Decode(asset.Properties["networkInterfaces"], &nics); err != nil { return err } var rawSecurityRules []networkSecurityRules if err := mapstructure.Decode(asset.Properties["securityRules"], &rawSecurityRules); err != nil { return err } securityRules := make([]extensionNetworkSecurityRules, 0, len(rawSecurityRules)) for _, rule := range rawSecurityRules { securityRules = append(securityRules, rule.Properties) } for _, nic := range nics { interfaceRules, exists := securityRulesByNetworkInterface[nic.Id] if !exists { interfaceRules = make([]extensionNetworkSecurityRules, 0, len(securityRules)) } securityRulesByNetworkInterface[nic.Id] = append(interfaceRules, securityRules...) } return nil } func (e vmNetworkSecurityGroupEnricher) addNetworkRulesToAssetExtensions(vm *inventory.AzureAsset, securityRuleByNetworkInterface map[string][]extensionNetworkSecurityRules) error { var profile networkProfile if err := mapstructure.Decode(vm.Properties["networkProfile"], &profile); err != nil { return err } rules := make([]map[string]any, 0) for _, nic := range profile.NetworkInterfaces { for _, rule := range securityRuleByNetworkInterface[nic.Id] { rules = append(rules, map[string]any{ "access": rule.Access, "destinationPortRange": rule.DestinationPortRange, "destinationPortRanges": rule.DestinationPortRanges, "direction": rule.Direction, "protocol": rule.Protocol, "sourceAddressPrefix": rule.SourceAddressPrefix, "sourceAddressPrefixes": rule.SourceAddressPrefixes, }) } } if len(rules) == 0 { return nil } vm.AddExtension(inventory.ExtensionNetwork, map[string]any{ "securityRules": rules, }) return nil }