diff --git a/cmd/azqr/scan.go b/cmd/azqr/scan.go index 21da1509..b1185ea1 100644 --- a/cmd/azqr/scan.go +++ b/cmd/azqr/scan.go @@ -33,6 +33,7 @@ import ( "github.com/Azure/azqr/internal/scanners/apim" "github.com/Azure/azqr/internal/scanners/appcs" "github.com/Azure/azqr/internal/scanners/appi" + "github.com/Azure/azqr/internal/scanners/asp" "github.com/Azure/azqr/internal/scanners/cae" "github.com/Azure/azqr/internal/scanners/ci" "github.com/Azure/azqr/internal/scanners/cog" @@ -47,13 +48,13 @@ import ( "github.com/Azure/azqr/internal/scanners/logic" "github.com/Azure/azqr/internal/scanners/maria" "github.com/Azure/azqr/internal/scanners/mysql" - "github.com/Azure/azqr/internal/scanners/asp" "github.com/Azure/azqr/internal/scanners/psql" "github.com/Azure/azqr/internal/scanners/redis" "github.com/Azure/azqr/internal/scanners/sb" "github.com/Azure/azqr/internal/scanners/sigr" "github.com/Azure/azqr/internal/scanners/sql" "github.com/Azure/azqr/internal/scanners/st" + "github.com/Azure/azqr/internal/scanners/traf" "github.com/Azure/azqr/internal/scanners/vm" "github.com/Azure/azqr/internal/scanners/vnet" "github.com/Azure/azqr/internal/scanners/wps" @@ -466,6 +467,7 @@ func GetScanners() []scanners.IAzureScanner { &sb.ServiceBusScanner{}, &sigr.SignalRScanner{}, &sql.SQLScanner{}, + &traf.TrafficManagerScanner{}, &st.StorageScanner{}, &vm.VirtualMachineScanner{}, &vnet.VirtualNetworkScanner{}, diff --git a/cmd/azqr/traf.go b/cmd/azqr/traf.go new file mode 100644 index 00000000..2d3db3bf --- /dev/null +++ b/cmd/azqr/traf.go @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package azqr + +import ( + "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/scanners/traf" + "github.com/spf13/cobra" +) + +func init() { + scanCmd.AddCommand(trafCmd) +} + +var trafCmd = &cobra.Command{ + Use: "traf", + Short: "Scan Azure Traffic Manager", + Long: "Scan Azure Traffic Manager", + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + serviceScanners := []scanners.IAzureScanner{ + &traf.TrafficManagerScanner{}, + } + + scan(cmd, serviceScanners) + }, +} diff --git a/docs/content/en/docs/Rules/_index.md b/docs/content/en/docs/Rules/_index.md index 5352bd83..9bd32682 100644 --- a/docs/content/en/docs/Rules/_index.md +++ b/docs/content/en/docs/Rules/_index.md @@ -241,32 +241,39 @@ Azure Quick Review uses the following rules to identify Azure resources that may 231 | sqldb-005 | Reliability | SKU | SQL Database SKU | High | [Learn](https://docs.microsoft.com/en-us/azure/azure-sql/database/service-tiers-vcore?tabs=azure-portal) 232 | sqldb-006 | Operational Excellence | Naming Convention (CAF) | SQL Database Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) 233 | sqldb-007 | Operational Excellence | Tags | SQL Database should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -234 | st-001 | Reliability | Diagnostic Logs | Storage should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/storage/blobs/monitor-blob-storage) -235 | st-002 | Reliability | Availability Zones | Storage should have availability zones enabled | High | [Learn](https://learn.microsoft.com/EN-US/azure/reliability/migrate-storage) -236 | st-003 | Reliability | SLA | Storage should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/storage/) -237 | st-004 | Security | Private Endpoint | Storage should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/storage/common/storage-private-endpoints) -238 | st-005 | Reliability | SKU | Storage SKU | High | [Learn](https://learn.microsoft.com/en-us/rest/api/storagerp/srp_sku_types) -239 | st-006 | Operational Excellence | Naming Convention (CAF) | Storage Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -240 | st-007 | Security | HTTPS Only | Storage Account should use HTTPS only | High | [Learn](https://learn.microsoft.com/en-us/azure/storage/common/storage-require-secure-transfer) -241 | st-008 | Operational Excellence | Tags | Storage Account should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -242 | st-009 | Security | TLS | Storage Account should enforce TLS >= 1.2 | Low | [Learn](https://learn.microsoft.com/en-us/azure/storage/common/transport-layer-security-configure-minimum-version?tabs=portal) -243 | vm-001 | Reliability | Diagnostic Logs | Virtual Machine should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/azure-monitor/agents/diagnostics-extension-windows-install) -244 | vm-002 | Reliability | Availability Zones | Virtual Machine should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/virtual-machines/availability#availability-zones) -245 | vm-003 | Reliability | SLA | Virtual Machine should have a SLA | High | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1) -246 | vm-006 | Operational Excellence | Naming Convention (CAF) | Virtual Machine Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -247 | vm-007 | Operational Excellence | Tags | Virtual Machine should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -248 | vm-008 | Reliability | Reliability | Virtual Machine should use managed disks | High | [Learn](https://learn.microsoft.com/en-us/azure/architecture/checklist/resiliency-per-service#virtual-machines) -249 | vm-009 | Reliability | Reliability | Virtual Machine should host application or database data on a data disk | Low | [Learn](https://learn.microsoft.com/azure/virtual-machines/managed-disks-overview#data-disk) -250 | vnet-001 | Reliability | Diagnostic Logs | Virtual Network should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/virtual-network/monitor-virtual-network#collection-and-routing) -251 | vnet-002 | Reliability | Availability Zones | Virtual Network should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-overview#virtual-networks-and-availability-zones) -252 | vnet-006 | Operational Excellence | Naming Convention (CAF) | Virtual Network Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -253 | vnet-007 | Operational Excellence | Tags | Virtual Network should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -254 | vnet-008 | Security | Networking | Virtual Network: All Subnets should have a Network Security Group associated | High | [Learn](https://learn.microsoft.com/azure/virtual-network/concepts-and-best-practices) -255 | vnet-009 | Reliability | Reliability | Virtual NetworK should have at least two DNS servers assigned | High | [Learn](https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-name-resolution-for-vms-and-role-instances?tabs=redhat#specify-dns-servers) -256 | wps-001 | Reliability | Diagnostic Logs | Web Pub Sub should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/howto-troubleshoot-resource-logs) -257 | wps-002 | Reliability | Availability Zones | Web Pub Sub should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/concept-availability-zones) -258 | wps-003 | Reliability | SLA | Web Pub Sub should have a SLA | High | [Learn](https://azure.microsoft.com/en-gb/support/legal/sla/web-pubsub/) -259 | wps-004 | Security | Private Endpoint | Web Pub Sub should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/howto-secure-private-endpoints) -260 | wps-005 | Reliability | SKU | Web Pub Sub SKU | High | [Learn](https://azure.microsoft.com/en-us/pricing/details/web-pubsub/) -261 | wps-006 | Operational Excellence | Naming Convention (CAF) | Web Pub Sub Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -262 | wps-007 | Operational Excellence | Tags | Web Pub Sub should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +234 | traf-001 | Reliability | Diagnostic Logs | Traffic Manager should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-diagnostic-logs) +235 | traf-002 | Reliability | Availability Zones | Traffic Manager should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/architecture/high-availability/reference-architecture-traffic-manager-application-gateway) +236 | traf-003 | Reliability | SLA | Traffic Manager should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/traffic-manager/) +237 | traf-006 | Operational Excellence | Naming Convention (CAF) | Traffic Manager Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +238 | traf-007 | Operational Excellence | Tags | Traffic Manager should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +239 | traf-008 | Security | Networking | Traffic Manager should use at least 2 endpoints | High | [Learn](https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-endpoint-types) +240 | traf-009 | Reliability | Reliability | Traffic Manager: HTTP endpoints should be monitored using HTTPS | High | [Learn](https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-monitoring) +241 | st-001 | Reliability | Diagnostic Logs | Storage should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/storage/blobs/monitor-blob-storage) +242 | st-002 | Reliability | Availability Zones | Storage should have availability zones enabled | High | [Learn](https://learn.microsoft.com/EN-US/azure/reliability/migrate-storage) +243 | st-003 | Reliability | SLA | Storage should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/storage/) +244 | st-004 | Security | Private Endpoint | Storage should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/storage/common/storage-private-endpoints) +245 | st-005 | Reliability | SKU | Storage SKU | High | [Learn](https://learn.microsoft.com/en-us/rest/api/storagerp/srp_sku_types) +246 | st-006 | Operational Excellence | Naming Convention (CAF) | Storage Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +247 | st-007 | Security | HTTPS Only | Storage Account should use HTTPS only | High | [Learn](https://learn.microsoft.com/en-us/azure/storage/common/storage-require-secure-transfer) +248 | st-008 | Operational Excellence | Tags | Storage Account should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +249 | st-009 | Security | TLS | Storage Account should enforce TLS >= 1.2 | Low | [Learn](https://learn.microsoft.com/en-us/azure/storage/common/transport-layer-security-configure-minimum-version?tabs=portal) +250 | vm-001 | Reliability | Diagnostic Logs | Virtual Machine should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/azure-monitor/agents/diagnostics-extension-windows-install) +251 | vm-002 | Reliability | Availability Zones | Virtual Machine should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/virtual-machines/availability#availability-zones) +252 | vm-003 | Reliability | SLA | Virtual Machine should have a SLA | High | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1) +253 | vm-006 | Operational Excellence | Naming Convention (CAF) | Virtual Machine Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +254 | vm-007 | Operational Excellence | Tags | Virtual Machine should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +255 | vm-008 | Reliability | Reliability | Virtual Machine should use managed disks | High | [Learn](https://learn.microsoft.com/en-us/azure/architecture/checklist/resiliency-per-service#virtual-machines) +256 | vm-009 | Reliability | Reliability | Virtual Machine should host application or database data on a data disk | Low | [Learn](https://learn.microsoft.com/azure/virtual-machines/managed-disks-overview#data-disk) +257 | vnet-001 | Reliability | Diagnostic Logs | Virtual Network should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/virtual-network/monitor-virtual-network#collection-and-routing) +258 | vnet-002 | Reliability | Availability Zones | Virtual Network should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-overview#virtual-networks-and-availability-zones) +259 | vnet-006 | Operational Excellence | Naming Convention (CAF) | Virtual Network Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +260 | vnet-007 | Operational Excellence | Tags | Virtual Network should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +261 | vnet-008 | Security | Networking | Virtual Network: All Subnets should have a Network Security Group associated | High | [Learn](https://learn.microsoft.com/azure/virtual-network/concepts-and-best-practices) +262 | vnet-009 | Reliability | Reliability | Virtual NetworK should have at least two DNS servers assigned | High | [Learn](https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-name-resolution-for-vms-and-role-instances?tabs=redhat#specify-dns-servers) +263 | wps-001 | Reliability | Diagnostic Logs | Web Pub Sub should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/howto-troubleshoot-resource-logs) +264 | wps-002 | Reliability | Availability Zones | Web Pub Sub should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/concept-availability-zones) +265 | wps-003 | Reliability | SLA | Web Pub Sub should have a SLA | High | [Learn](https://azure.microsoft.com/en-gb/support/legal/sla/web-pubsub/) +266 | wps-004 | Security | Private Endpoint | Web Pub Sub should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/howto-secure-private-endpoints) +267 | wps-005 | Reliability | SKU | Web Pub Sub SKU | High | [Learn](https://azure.microsoft.com/en-us/pricing/details/web-pubsub/) +268 | wps-006 | Operational Excellence | Naming Convention (CAF) | Web Pub Sub Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +269 | wps-007 | Operational Excellence | Tags | Web Pub Sub should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) diff --git a/go.mod b/go.mod index a675f508..995a4e97 100644 --- a/go.mod +++ b/go.mod @@ -42,6 +42,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/sql/armsql v1.2.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription v1.2.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager v1.3.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/webpubsub/armwebpubsub v1.2.0 github.com/rs/zerolog v1.31.0 github.com/spf13/cobra v1.8.0 diff --git a/go.sum b/go.sum index 892c41d6..0742c86c 100644 --- a/go.sum +++ b/go.sum @@ -81,6 +81,8 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0/go.mod h1:T5RfihdXtBDxt1Ch2wobif3TvzTdumDy29kahv6AV9A= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription v1.2.0 h1:UrGzkHueDwAWDdjQxC+QaXHd4tVCkISYE9j7fSSXF8k= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription v1.2.0/go.mod h1:qskvSQeW+cxEE2bcKYyKimB1/KiQ9xpJ99bcHY0BX6c= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager v1.3.0 h1:e3kTG23M5ps+DjvPolK4dcgohDY8sHsXU7zrdHj1WzY= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager v1.3.0/go.mod h1:Os5dq8Cvvz97rJauZhZJAfKHN+OEvF/0nVmHzF4aVys= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/webpubsub/armwebpubsub v1.2.0 h1:U+zDy6lU9scW8b58JpcQAlI+lsitiVSjz/RzBqbS5gM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/webpubsub/armwebpubsub v1.2.0/go.mod h1:gz64akQ/0Cfq2ZQCNsGE5RmRpl9ySpuV4zURgjBuyB0= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.0 h1:hVeq+yCyUi+MsoO/CU95yqCIcdzra5ovzk8Q2BBpV2M= diff --git a/internal/scanners/agw/rules.go b/internal/scanners/agw/rules.go index fbc4b20a..d83656f1 100644 --- a/internal/scanners/agw/rules.go +++ b/internal/scanners/agw/rules.go @@ -105,7 +105,7 @@ func (a *ApplicationGatewayScanner) GetRules() map[string]scanners.AzureRule { "agw-008": { Id: "agw-008", Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySubcategoryMaintenance, + Subcategory: scanners.RulesSubcategoryReliabilityMaintenance, Description: "Application Gateway: Plan for backend maintenance by using connection draining", Severity: scanners.SeverityMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { diff --git a/internal/scanners/kv/rules.go b/internal/scanners/kv/rules.go index a0e57cbf..8f7ec2d0 100644 --- a/internal/scanners/kv/rules.go +++ b/internal/scanners/kv/rules.go @@ -96,7 +96,7 @@ func (a *KeyVaultScanner) GetRules() map[string]scanners.AzureRule { "kv-008": { Id: "kv-008", Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySubcategoryReliability, + Subcategory: scanners.RulesSubcategoryReliabilityReliability, Description: "Key Vault should have soft delete enabled", Severity: scanners.SeverityMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { @@ -108,7 +108,7 @@ func (a *KeyVaultScanner) GetRules() map[string]scanners.AzureRule { "kv-009": { Id: "kv-009", Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySubcategoryReliability, + Subcategory: scanners.RulesSubcategoryReliabilityReliability, Description: "Key Vault should have purge protection enabled", Severity: scanners.SeverityMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { diff --git a/internal/scanners/logic/rules.go b/internal/scanners/logic/rules.go index d8f0f136..1da912c6 100644 --- a/internal/scanners/logic/rules.go +++ b/internal/scanners/logic/rules.go @@ -42,7 +42,7 @@ func (a *LogicAppScanner) GetRules() map[string]scanners.AzureRule { "logic-004": { Id: "logic-004", Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, + Subcategory: scanners.RulesSubcategorySecurityFirewall, Description: "Logic App should limit access to Http Triggers", Severity: scanners.SeverityHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { diff --git a/internal/scanners/scanner.go b/internal/scanners/scanner.go index 52466cc8..d96e518d 100644 --- a/internal/scanners/scanner.go +++ b/internal/scanners/scanner.go @@ -29,7 +29,7 @@ type ( ScanContext struct { PrivateEndpoints map[string]bool DiagnosticsSettings map[string]bool - PublicIPs map[string]*armnetwork.PublicIPAddress + PublicIPs map[string]*armnetwork.PublicIPAddress } // IAzureScanner - Interface for all Azure Scanners @@ -209,14 +209,14 @@ const ( RulesCategoryOperationalExcellence = "Operational Excellence" RulesCategoryPerformanceEfficienccy = "Performance Efficiency" - RulesSubcategoryReliabilityAvailabilityZones = "Availability Zones" - RulesSubcategoryReliabilitySLA = "SLA" - RulesSubcategoryReliabilitySKU = "SKU" - RulesSubcategoryReliabilityScaling = "Scaling" - RulesSubcategoryReliabilityDiagnosticLogs = "Diagnostic Logs" - RulesSubcategoryReliabilityMonitoring = "Monitoring" - RulesSubcategoryReliabilitySubcategoryReliability = "Reliability" - RulesSubcategoryReliabilitySubcategoryMaintenance = "Maintenance" + RulesSubcategoryReliabilityAvailabilityZones = "Availability Zones" + RulesSubcategoryReliabilitySLA = "SLA" + RulesSubcategoryReliabilitySKU = "SKU" + RulesSubcategoryReliabilityScaling = "Scaling" + RulesSubcategoryReliabilityDiagnosticLogs = "Diagnostic Logs" + RulesSubcategoryReliabilityMonitoring = "Monitoring" + RulesSubcategoryReliabilityReliability = "Reliability" + RulesSubcategoryReliabilityMaintenance = "Maintenance" RulesSubcategoryOperationalExcellenceCAF = "Naming Convention (CAF)" RulesSubcategoryOperationalExcellenceTags = "Tags" diff --git a/internal/scanners/traf/rules.go b/internal/scanners/traf/rules.go new file mode 100644 index 00000000..60d83faa --- /dev/null +++ b/internal/scanners/traf/rules.go @@ -0,0 +1,113 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package traf + +import ( + "strings" + + "github.com/Azure/azqr/internal/ref" + "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager" +) + +// GetRules - Returns the rules for the TrafficManagerScanner +func (a *TrafficManagerScanner) GetRules() map[string]scanners.AzureRule { + return map[string]scanners.AzureRule{ + "traf-001": { + Id: "traf-001", + Category: scanners.RulesCategoryReliability, + Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, + Description: "Traffic Manager should have diagnostic settings enabled", + Severity: scanners.SeverityMedium, + Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { + service := target.(*armtrafficmanager.Profile) + _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] + return !ok, "" + }, + Url: "https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-diagnostic-logs", + Field: scanners.OverviewFieldDiagnostics, + }, + "traf-002": { + Id: "traf-002", + Category: scanners.RulesCategoryReliability, + Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, + Description: "Traffic Manager should have availability zones enabled", + Severity: scanners.SeverityHigh, + Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { + return false, "" + }, + Url: "https://learn.microsoft.com/en-us/azure/architecture/high-availability/reference-architecture-traffic-manager-application-gateway", + Field: scanners.OverviewFieldAZ, + }, + "traf-003": { + Id: "traf-003", + Category: scanners.RulesCategoryReliability, + Subcategory: scanners.RulesSubcategoryReliabilitySLA, + Description: "Traffic Manager should have a SLA", + Severity: scanners.SeverityHigh, + Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { + return false, "99.99%" + }, + Url: "https://www.azure.cn/en-us/support/sla/traffic-manager/", + Field: scanners.OverviewFieldSLA, + }, + "traf-006": { + Id: "traf-006", + Category: scanners.RulesCategoryOperationalExcellence, + Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, + Description: "Traffic Manager Name should comply with naming conventions", + Severity: scanners.SeverityLow, + Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { + c := target.(*armtrafficmanager.Profile) + caf := strings.HasPrefix(*c.Name, "traf") + return !caf, "" + }, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", + Field: scanners.OverviewFieldCAF, + }, + "traf-007": { + Id: "traf-007", + Category: scanners.RulesCategoryOperationalExcellence, + Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, + Description: "Traffic Manager should have tags", + Severity: scanners.SeverityLow, + Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { + c := target.(*armtrafficmanager.Profile) + return len(c.Tags) == 0, "" + }, + Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", + }, + "traf-008": { + Id: "traf-008", + Category: scanners.RulesCategoryReliability, + Subcategory: scanners.RulesSubcategoryReliabilityReliability, + Description: "Traffic Manager should use at least 2 endpoints", + Severity: scanners.SeverityHigh, + Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { + c := target.(*armtrafficmanager.Profile) + endpoints := 0 + for _, endpoint := range c.Properties.Endpoints { + if *endpoint.Properties.EndpointStatus == armtrafficmanager.EndpointStatusEnabled { + endpoints++ + } + } + return endpoints < 2, "" + }, + Url: "https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-endpoint-types", + }, + "traf-009": { + Id: "traf-009", + Category: scanners.RulesCategorySecurity, + Subcategory: scanners.RulesSubcategorySecurityHTTPS, + Description: "Traffic Manager: HTTP endpoints should be monitored using HTTPS", + Severity: scanners.SeverityHigh, + Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { + c := target.(*armtrafficmanager.Profile) + httpMonitor := *c.Properties.MonitorConfig.Port == int64(80) || *c.Properties.MonitorConfig.Port == int64(443) + return httpMonitor && c.Properties.MonitorConfig.Protocol != ref.Of(armtrafficmanager.MonitorProtocolHTTPS), "" + }, + Url: "https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-monitoring", + }, + } +} diff --git a/internal/scanners/traf/rules_test.go b/internal/scanners/traf/rules_test.go new file mode 100644 index 00000000..074de889 --- /dev/null +++ b/internal/scanners/traf/rules_test.go @@ -0,0 +1,193 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package traf + +import ( + "reflect" + "testing" + + "github.com/Azure/azqr/internal/ref" + "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager" +) + +func TestTrafficManagerScanner_Rules(t *testing.T) { + type fields struct { + rule string + target interface{} + scanContext *scanners.ScanContext + } + type want struct { + broken bool + result string + } + tests := []struct { + name string + fields fields + want want + }{ + { + name: "TrafficManagerScanner DiagnosticSettings", + fields: fields{ + rule: "traf-001", + target: &armtrafficmanager.Profile{ + ID: ref.Of("test"), + }, + scanContext: &scanners.ScanContext{ + DiagnosticsSettings: map[string]bool{ + "test": true, + }, + }, + }, + want: want{ + broken: false, + result: "", + }, + }, + { + name: "TrafficManagerScanner Availability Zones", + fields: fields{ + rule: "traf-002", + target: &armtrafficmanager.Profile{}, + scanContext: &scanners.ScanContext{}, + }, + want: want{ + broken: false, + result: "", + }, + }, + { + name: "TrafficManagerScanner SLA 99.99%", + fields: fields{ + rule: "traf-003", + target: &armtrafficmanager.Profile{}, + scanContext: &scanners.ScanContext{}, + }, + want: want{ + broken: false, + result: "99.99%", + }, + }, + { + name: "TrafficManagerScanner CAF", + fields: fields{ + rule: "traf-006", + target: &armtrafficmanager.Profile{ + Name: ref.Of("traf-test"), + }, + scanContext: &scanners.ScanContext{}, + }, + want: want{ + broken: false, + result: "", + }, + }, + { + name: "TrafficManagerScanner use at least 2 endpoints", + fields: fields{ + rule: "traf-008", + target: &armtrafficmanager.Profile{ + Properties: &armtrafficmanager.ProfileProperties{ + Endpoints: []*armtrafficmanager.Endpoint{ + { + Properties: &armtrafficmanager.EndpointProperties{ + EndpointStatus: ref.Of(armtrafficmanager.EndpointStatusEnabled), + }, + }, + { + Properties: &armtrafficmanager.EndpointProperties{ + EndpointStatus: ref.Of(armtrafficmanager.EndpointStatusEnabled), + }, + }, + }, + }, + }, + scanContext: &scanners.ScanContext{}, + }, + want: want{ + broken: false, + result: "", + }, + }, + { + name: "TrafficManagerScanner use at least 2 endpoints with 1 disabled", + fields: fields{ + rule: "traf-008", + target: &armtrafficmanager.Profile{ + Properties: &armtrafficmanager.ProfileProperties{ + Endpoints: []*armtrafficmanager.Endpoint{ + { + Properties: &armtrafficmanager.EndpointProperties{ + EndpointStatus: ref.Of(armtrafficmanager.EndpointStatusEnabled), + }, + }, + { + Properties: &armtrafficmanager.EndpointProperties{ + EndpointStatus: ref.Of(armtrafficmanager.EndpointStatusDisabled), + }, + }, + }, + }, + }, + scanContext: &scanners.ScanContext{}, + }, + want: want{ + broken: true, + result: "", + }, + }, + { + name: "TrafficManagerScanner HTTP endpoints should be monitored using HTTPS", + fields: fields{ + rule: "traf-009", + target: &armtrafficmanager.Profile{ + Properties: &armtrafficmanager.ProfileProperties{ + MonitorConfig: &armtrafficmanager.MonitorConfig{ + Protocol: ref.Of(armtrafficmanager.MonitorProtocolHTTP), + Port: ref.Of(int64(80)), + }, + }, + }, + scanContext: &scanners.ScanContext{}, + }, + want: want{ + broken: true, + result: "", + }, + }, + { + name: "TrafficManagerScanner HTTP endpoints (port 443) should be monitored using HTTPS", + fields: fields{ + rule: "traf-009", + target: &armtrafficmanager.Profile{ + Properties: &armtrafficmanager.ProfileProperties{ + MonitorConfig: &armtrafficmanager.MonitorConfig{ + Protocol: ref.Of(armtrafficmanager.MonitorProtocolHTTP), + Port: ref.Of(int64(443)), + }, + }, + }, + scanContext: &scanners.ScanContext{}, + }, + want: want{ + broken: true, + result: "", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &TrafficManagerScanner{} + rules := s.GetRules() + b, w := rules[tt.fields.rule].Eval(tt.fields.target, tt.fields.scanContext) + got := want{ + broken: b, + result: w, + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("TrafficManagerScanner Rule.Eval() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/scanners/traf/traf.go b/internal/scanners/traf/traf.go new file mode 100644 index 00000000..c9c74ab7 --- /dev/null +++ b/internal/scanners/traf/traf.go @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package traf + +import ( + "github.com/rs/zerolog/log" + + "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager" +) + +// TrafficManagerScanner - Scanner for TrafficManager +type TrafficManagerScanner struct { + config *scanners.ScannerConfig + client *armtrafficmanager.ClientFactory +} + +// Init - Initializes the TrafficManager +func (c *TrafficManagerScanner) Init(config *scanners.ScannerConfig) error { + c.config = config + var err error + c.client, err = armtrafficmanager.NewClientFactory(config.SubscriptionID, config.Cred, config.ClientOptions) + return err +} + +// Scan - Scans all TrafficManager in a Resource Group +func (c *TrafficManagerScanner) Scan(resourceGroupName string, scanContext *scanners.ScanContext) ([]scanners.AzureServiceResult, error) { + log.Info().Msgf("Scanning Trraffic Manager in Resource Group %s", resourceGroupName) + + vnets, err := c.list(resourceGroupName) + if err != nil { + return nil, err + } + engine := scanners.RuleEngine{} + rules := c.GetRules() + results := []scanners.AzureServiceResult{} + + for _, w := range vnets { + rr := engine.EvaluateRules(rules, w, scanContext) + + results = append(results, scanners.AzureServiceResult{ + SubscriptionID: c.config.SubscriptionID, + ResourceGroup: resourceGroupName, + ServiceName: *w.Name, + Type: *w.Type, + Location: *w.Location, + Rules: rr, + }) + } + return results, nil +} + +func (c *TrafficManagerScanner) list(resourceGroupName string) ([]*armtrafficmanager.Profile, error) { + pager := c.client.NewProfilesClient().NewListByResourceGroupPager(resourceGroupName, nil) + + vnets := make([]*armtrafficmanager.Profile, 0) + for pager.More() { + resp, err := pager.NextPage(c.config.Ctx) + if err != nil { + return nil, err + } + vnets = append(vnets, resp.Value...) + } + return vnets, nil +} diff --git a/internal/scanners/vm/rules.go b/internal/scanners/vm/rules.go index bbc7f634..6bba7dd0 100644 --- a/internal/scanners/vm/rules.go +++ b/internal/scanners/vm/rules.go @@ -92,7 +92,7 @@ func (a *VirtualMachineScanner) GetRules() map[string]scanners.AzureRule { "vm-008": { Id: "vm-008", Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySubcategoryReliability, + Subcategory: scanners.RulesSubcategoryReliabilityReliability, Description: "Virtual Machine should use managed disks", Severity: scanners.SeverityHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { @@ -105,7 +105,7 @@ func (a *VirtualMachineScanner) GetRules() map[string]scanners.AzureRule { "vm-009": { Id: "vm-009", Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySubcategoryReliability, + Subcategory: scanners.RulesSubcategoryReliabilityReliability, Description: "Virtual Machine should host application or database data on a data disk", Severity: scanners.SeverityLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { diff --git a/internal/scanners/vnet/rules.go b/internal/scanners/vnet/rules.go index 2e4c8a75..a9bab949 100644 --- a/internal/scanners/vnet/rules.go +++ b/internal/scanners/vnet/rules.go @@ -89,7 +89,7 @@ func (a *VirtualNetworkScanner) GetRules() map[string]scanners.AzureRule { "vnet-009": { Id: "vnet-009", Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySubcategoryReliability, + Subcategory: scanners.RulesSubcategoryReliabilityReliability, Description: "Virtual NetworK should have at least two DNS servers assigned", Severity: scanners.SeverityHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) {