internal/scanners/asp/rules.go (522 lines of code) (raw):

// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. package asp import ( "strings" "github.com/Azure/azqr/internal/models" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2" ) // GetRecommendations - Returns the rules for the AppServiceScanner func (a *AppServiceScanner) GetRecommendations() map[string]models.AzqrRecommendation { result := a.getPlanRules() for k, v := range a.getAppRules() { result[k] = v } for k, v := range a.getFunctionRules() { result[k] = v } for k, v := range a.getLogicRules() { result[k] = v } return result } func (a *AppServiceScanner) getPlanRules() map[string]models.AzqrRecommendation { return map[string]models.AzqrRecommendation{ "asp-001": { RecommendationID: "asp-001", ResourceType: "Microsoft.Web/serverfarms", Category: models.CategoryMonitoringAndAlerting, Recommendation: "Plan should have diagnostic settings enabled", Impact: models.ImpactLow, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { service := target.(*armappservice.Plan) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, }, "asp-003": { RecommendationID: "asp-003", ResourceType: "Microsoft.Web/serverfarms", Category: models.CategoryHighAvailability, Recommendation: "Plan should have a SLA", RecommendationType: models.TypeSLA, Impact: models.ImpactHigh, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { i := target.(*armappservice.Plan) sku := string(*i.SKU.Tier) sla := "None" if sku != "Free" && sku != "Shared" { sla = "99.95%" } return sla == "None", sla }, LearnMoreUrl: "https://www.azure.cn/en-us/support/sla/app-service/", }, "asp-006": { RecommendationID: "asp-006", ResourceType: "Microsoft.Web/serverfarms", Category: models.CategoryGovernance, Recommendation: "Plan Name should comply with naming conventions", Impact: models.ImpactLow, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Plan) caf := strings.HasPrefix(*c.Name, "asp") return !caf, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "asp-007": { RecommendationID: "asp-007", ResourceType: "Microsoft.Web/serverfarms", Category: models.CategoryGovernance, Recommendation: "Plan should have tags", Impact: models.ImpactLow, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Plan) return len(c.Tags) == 0, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, } } func (a *AppServiceScanner) getAppRules() map[string]models.AzqrRecommendation { return map[string]models.AzqrRecommendation{ "app-001": { RecommendationID: "app-001", ResourceType: "Microsoft.Web/sites", Category: models.CategoryMonitoringAndAlerting, Recommendation: "App Service should have diagnostic settings enabled", Impact: models.ImpactLow, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { service := target.(*armappservice.Site) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/app-service/troubleshoot-diagnostic-logs#send-logs-to-azure-monitor", }, "app-004": { RecommendationID: "app-004", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "App Service should have private endpoints enabled", Impact: models.ImpactHigh, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { i := target.(*armappservice.Site) _, pe := scanContext.PrivateEndpoints[*i.ID] return !pe, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/app-service/networking/private-endpoint", }, "app-006": { RecommendationID: "app-006", ResourceType: "Microsoft.Web/sites", Category: models.CategoryGovernance, Recommendation: "App Service Name should comply with naming conventions", Impact: models.ImpactLow, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) caf := strings.HasPrefix(*c.Name, "app") return !caf, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "app-007": { RecommendationID: "app-007", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "App Service should use HTTPS only", Impact: models.ImpactHigh, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) h := *c.Properties.HTTPSOnly return !h, "" }, LearnMoreUrl: "https://learn.microsoft.com/azure/app-service/configure-ssl-bindings#enforce-https", }, "app-008": { RecommendationID: "app-008", ResourceType: "Microsoft.Web/sites", Category: models.CategoryGovernance, Recommendation: "App Service should have tags", Impact: models.ImpactLow, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) return len(c.Tags) == 0, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "app-009": { RecommendationID: "app-009", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "App Service should use VNET integration", Impact: models.ImpactMedium, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.VirtualNetworkSubnetID == nil || len(*c.Properties.VirtualNetworkSubnetID) == 0, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration", }, "app-010": { RecommendationID: "app-010", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "App Service should have VNET Route all enabled for VNET integration", Impact: models.ImpactMedium, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.VnetRouteAllEnabled == nil || !*c.Properties.VnetRouteAllEnabled, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration", }, "app-011": { RecommendationID: "app-011", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "App Service should use TLS 1.2", Impact: models.ImpactHigh, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { broken := scanContext.SiteConfig.Properties.MinTLSVersion == nil || *scanContext.SiteConfig.Properties.MinTLSVersion != armappservice.SupportedTLSVersionsOne2 return broken, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/app-service/overview-tls", }, "app-012": { RecommendationID: "app-012", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "App Service remote debugging should be disabled", Impact: models.ImpactHigh, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { broken := scanContext.SiteConfig.Properties.RemoteDebuggingEnabled == nil || *scanContext.SiteConfig.Properties.RemoteDebuggingEnabled return broken, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging-azure-app-service?view=vs-2022#enable-remote-debugging", }, "app-013": { RecommendationID: "app-013", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "App Service should not allow insecure FTP", Impact: models.ImpactHigh, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { broken := scanContext.SiteConfig.Properties.FtpsState == nil || *scanContext.SiteConfig.Properties.FtpsState == armappservice.FtpsStateAllAllowed return broken, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/app-service/deploy-ftp?tabs=portal", }, "app-014": { RecommendationID: "app-014", ResourceType: "Microsoft.Web/sites", Category: models.CategoryScalability, Recommendation: "App Service should have Always On enabled", Impact: models.ImpactHigh, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { broken := scanContext.SiteConfig.Properties.AlwaysOn == nil || !*scanContext.SiteConfig.Properties.AlwaysOn return broken, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/app-service/configure-common?tabs=portal", }, "app-015": { RecommendationID: "app-015", ResourceType: "Microsoft.Web/sites", Category: models.CategoryHighAvailability, Recommendation: "App Service should avoid using Client Affinity", Impact: models.ImpactMedium, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.ClientAffinityEnabled != nil && *c.Properties.ClientAffinityEnabled, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-app-service/reliability#checklist", }, "app-016": { RecommendationID: "app-016", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "App Service should use Managed Identities", Impact: models.ImpactMedium, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { // c := target.(*armappservice.Site) // c.Identity == nil || c.Identity.Type == nil || *c.Identity.Type == armappservice.ManagedServiceIdentityTypeNone // not working because SDK set's Identity to nil even when configured. ok := scanContext.SiteConfig.Properties.ManagedServiceIdentityID != nil || scanContext.SiteConfig.Properties.XManagedServiceIdentityID != nil return !ok, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity?tabs=portal%2Chttp", }, } } func (a *AppServiceScanner) getFunctionRules() map[string]models.AzqrRecommendation { return map[string]models.AzqrRecommendation{ "func-001": { RecommendationID: "func-001", ResourceType: "Microsoft.Web/sites", Category: models.CategoryMonitoringAndAlerting, Recommendation: "Function should have diagnostic settings enabled", Impact: models.ImpactLow, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { service := target.(*armappservice.Site) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/azure-functions/functions-monitor-log-analytics?tabs=csharp", }, "func-004": { RecommendationID: "func-004", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "Function should have private endpoints enabled", Impact: models.ImpactHigh, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { i := target.(*armappservice.Site) _, pe := scanContext.PrivateEndpoints[*i.ID] return !pe, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/azure-functions/functions-create-vnet", }, "func-006": { RecommendationID: "func-006", ResourceType: "Microsoft.Web/sites", Category: models.CategoryGovernance, Recommendation: "Function Name should comply with naming conventions", Impact: models.ImpactLow, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) caf := strings.HasPrefix(*c.Name, "func") return !caf, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "func-007": { RecommendationID: "func-007", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "Function should use HTTPS only", Impact: models.ImpactHigh, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) h := c.Properties.HTTPSOnly != nil && *c.Properties.HTTPSOnly return !h, "" }, LearnMoreUrl: "https://learn.microsoft.com/azure/app-service/configure-ssl-bindings#enforce-https", }, "func-008": { RecommendationID: "func-008", ResourceType: "Microsoft.Web/sites", Category: models.CategoryGovernance, Recommendation: "Function should have tags", Impact: models.ImpactLow, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) return len(c.Tags) == 0, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "func-009": { RecommendationID: "func-009", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "Function should use VNET integration", Impact: models.ImpactMedium, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.VirtualNetworkSubnetID == nil || len(*c.Properties.VirtualNetworkSubnetID) == 0, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration", }, "func-010": { RecommendationID: "func-010", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "Function should have VNET Route all enabled for VNET integration", Impact: models.ImpactMedium, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.VnetRouteAllEnabled == nil || !*c.Properties.VnetRouteAllEnabled, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration", }, "func-011": { RecommendationID: "func-011", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "Function should use TLS 1.2", Impact: models.ImpactMedium, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { broken := scanContext.SiteConfig.Properties.MinTLSVersion == nil || *scanContext.SiteConfig.Properties.MinTLSVersion != armappservice.SupportedTLSVersionsOne2 return broken, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/app-service/overview-tls", }, "func-012": { RecommendationID: "func-012", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "Function remote debugging should be disabled", Impact: models.ImpactMedium, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { broken := scanContext.SiteConfig.Properties.RemoteDebuggingEnabled == nil || *scanContext.SiteConfig.Properties.RemoteDebuggingEnabled return broken, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging-azure-app-service?view=vs-2022#enable-remote-debugging", }, "func-013": { RecommendationID: "func-013", ResourceType: "Microsoft.Web/sites", Category: models.CategoryHighAvailability, Recommendation: "Function should avoid using Client Affinity", Impact: models.ImpactMedium, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.ClientAffinityEnabled != nil && *c.Properties.ClientAffinityEnabled, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-app-service/reliability#checklist", }, "func-014": { RecommendationID: "func-014", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "Function should use Managed Identities", Impact: models.ImpactMedium, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { // c := target.(*armappservice.Site) // c.Identity == nil || c.Identity.Type == nil || *c.Identity.Type == armappservice.ManagedServiceIdentityTypeNone // not working because SDK set's Identity to nil even when configured. ok := scanContext.SiteConfig.Properties.ManagedServiceIdentityID != nil || scanContext.SiteConfig.Properties.XManagedServiceIdentityID != nil return !ok, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity?tabs=portal%2Chttp", }, } } func (a *AppServiceScanner) getLogicRules() map[string]models.AzqrRecommendation { return map[string]models.AzqrRecommendation{ "logics-001": { RecommendationID: "logics-001", ResourceType: "Microsoft.Web/sites", Category: models.CategoryMonitoringAndAlerting, Recommendation: "Logic App should have diagnostic settings enabled", Impact: models.ImpactLow, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { service := target.(*armappservice.Site) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/logic-apps/monitor-workflows-collect-diagnostic-data", }, "logics-004": { RecommendationID: "logics-004", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "Logic App should have private endpoints enabled", Impact: models.ImpactHigh, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { i := target.(*armappservice.Site) _, pe := scanContext.PrivateEndpoints[*i.ID] return !pe, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/logic-apps/secure-single-tenant-workflow-virtual-network-private-endpoint", }, "logics-006": { RecommendationID: "logics-006", ResourceType: "Microsoft.Web/sites", Category: models.CategoryGovernance, Recommendation: "Logic App Name should comply with naming conventions", Impact: models.ImpactLow, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) caf := strings.HasPrefix(*c.Name, "logic") return !caf, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "logics-007": { RecommendationID: "logics-007", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "Logic App should use HTTPS only", Impact: models.ImpactHigh, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) h := c.Properties.HTTPSOnly != nil && *c.Properties.HTTPSOnly return !h, "" }, LearnMoreUrl: "https://learn.microsoft.com/azure/app-service/configure-ssl-bindings#enforce-https", }, "logics-008": { RecommendationID: "logics-008", ResourceType: "Microsoft.Web/sites", Category: models.CategoryGovernance, Recommendation: "Logic App should have tags", Impact: models.ImpactLow, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) return len(c.Tags) == 0, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "logics-009": { RecommendationID: "logics-009", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "Logic App should use VNET integration", Impact: models.ImpactMedium, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.VirtualNetworkSubnetID == nil || len(*c.Properties.VirtualNetworkSubnetID) == 0, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration", }, "logics-010": { RecommendationID: "logics-010", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "Logic App should have VNET Route all enabled for VNET integration", Impact: models.ImpactMedium, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.VnetRouteAllEnabled == nil || !*c.Properties.VnetRouteAllEnabled, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration", }, "logics-011": { RecommendationID: "logics-011", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "Logic App should use TLS 1.2", Impact: models.ImpactMedium, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { broken := scanContext.SiteConfig.Properties.MinTLSVersion == nil || *scanContext.SiteConfig.Properties.MinTLSVersion != armappservice.SupportedTLSVersionsOne2 return broken, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/app-service/overview-tls", }, "logics-012": { RecommendationID: "logics-012", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "Logic App remote debugging should be disabled", Impact: models.ImpactMedium, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { broken := scanContext.SiteConfig.Properties.RemoteDebuggingEnabled == nil || *scanContext.SiteConfig.Properties.RemoteDebuggingEnabled return broken, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging-azure-app-service?view=vs-2022#enable-remote-debugging", }, "logics-013": { RecommendationID: "logics-013", ResourceType: "Microsoft.Web/sites", Category: models.CategoryHighAvailability, Recommendation: "Logic App should avoid using Client Affinity", Impact: models.ImpactMedium, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.ClientAffinityEnabled != nil && *c.Properties.ClientAffinityEnabled, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-app-service/reliability#checklist", }, "logics-014": { RecommendationID: "logics-014", ResourceType: "Microsoft.Web/sites", Category: models.CategorySecurity, Recommendation: "Logic App should use Managed Identities", Impact: models.ImpactMedium, Eval: func(target interface{}, scanContext *models.ScanContext) (bool, string) { // c := target.(*armappservice.Site) // c.Identity == nil || c.Identity.Type == nil || *c.Identity.Type == armappservice.ManagedServiceIdentityTypeNone // not working because SDK set's Identity to nil even when configured. ok := scanContext.SiteConfig.Properties.ManagedServiceIdentityID != nil || scanContext.SiteConfig.Properties.XManagedServiceIdentityID != nil return !ok, "" }, LearnMoreUrl: "https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity?tabs=portal%2Chttp", }, } }