Repository: cloud-custodian/cloud-custodian

Issue:
azure - Filter on SSL settings of Web App/Function App
We need a filter that supports looking at the SSL settings in Azure App Services (and Function Apps) which are Kudu/Webapps. 

You can see these in the portal, but they aren't actually ARM settings, they are a special API that talks to Kudu.  It appears there is a documented REST endpoint for these that is RBAC auth, so we should be able to do this with a custom filter and hand crafted HTTP call - pretty similar to how we call the Functions Zip Deploy code on Kudu to deploy functions.


Discussion:
The basic "Protocol Settings" seem to be available through ARM APIs:

* [minTlsVersion](https://docs.microsoft.com/en-us/rest/api/appservice/webapps/getconfiguration)
* [httpsOnly and clientCertEnabled](https://docs.microsoft.com/en-us/rest/api/appservice/webapps/get) (available directly on the resource)

So, adding a custom value filter on the `Get Configuration` call would be helpful
`clientCertEnabled` is about the user having certs (it is a form of authentication), but it looks like `hostNameSslStates` should have info confirming SSL state.

**Exit Criteria:**

- Add value filter (`configuration`) subclass on GetConfiguration API call
- Publish a docs example with a few App Service scenarios for identifying non-compliant resources
  - Finding App Services (websites/API's) which are not enforcing SSL with cert properly configured, and specific TLS versions
  - Finding sites with `properties.alwaysOn` disabled


@stefangordon If no one else is working on this I will do it
@jomalsan sure! I believe the Virtual Machine InstanceViewFilter is a similar idea to the subclassed value filter we are talking about here, except it is able to use the SDK to make the call and I'm not sure there is an SDK for this one, it may just be Requests with the auth token (we do this for functions deployment elsewhere).

Resolving patch:
diff --git a/tools/c7n_azure/c7n_azure/resources/web_app.py b/tools/c7n_azure/c7n_azure/resources/web_app.py
--- a/tools/c7n_azure/c7n_azure/resources/web_app.py
+++ b/tools/c7n_azure/c7n_azure/resources/web_app.py
@@ -15,6 +15,8 @@
 from c7n_azure.provider import resources
 from c7n_azure.resources.arm import ArmResourceManager
 
+from c7n.filters.core import ValueFilter, type_schema
+
 
 @resources.register('webapp')
 class WebApp(ArmResourceManager):
@@ -58,6 +60,20 @@ class WebApp(ArmResourceManager):
                 threshold: 1000
                 timeframe: 72
 
+    :example:
+
+    This policy will find all web apps with minimum TLS encryption version not equal to 1.2
+
+    .. code-block:: yaml
+
+        policies:
+          - name: webapp-min-tls-enforcement
+            resource: azure.webapp
+            filters:
+              - type: configuration
+                key: minTlsVersion
+                value: '1.2'
+                op: ne
     """
 
     class resource_type(ArmResourceManager.resource_type):
@@ -74,3 +90,19 @@ class resource_type(ArmResourceManager.resource_type):
             'properties.hostNames[0]'
         )
         resource_type = 'Microsoft.Web/sites'
+
+
+@WebApp.filter_registry.register('configuration')
+class ConfigurationFilter(ValueFilter):
+    schema = type_schema('configuration', rinherit=ValueFilter.schema)
+    schema_alias = True
+
+    def __call__(self, i):
+        if 'c7n:configuration' not in i:
+            client = self.manager.get_client().web_apps
+            instance = (
+                client.get_configuration(i['resourceGroup'], i['name'])
+            )
+            i['c7n:configuration'] = instance.serialize(keep_readonly=True)['properties']
+
+        return super(ConfigurationFilter, self).__call__(i['c7n:configuration'])


Test patch:
diff --git a/tools/c7n_azure/tests/cassettes/WebAppTest.test_find_by_min_tls.json b/tools/c7n_azure/tests/cassettes/WebAppTest.test_find_by_min_tls.json
new file mode 100644
--- /dev/null
+++ b/tools/c7n_azure/tests/cassettes/WebAppTest.test_find_by_min_tls.json
@@ -0,0 +1,330 @@
+{
+    "version": 1,
+    "interactions": [
+        {
+            "request": {
+                "method": "GET",
+                "uri": "https://management.azure.com/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/providers/Microsoft.Web/sites?api-version=2018-11-01",
+                "body": null,
+                "headers": {}
+            },
+            "response": {
+                "status": {
+                    "code": 200,
+                    "message": "OK"
+                },
+                "headers": {
+                    "etag": [
+                        "\"1D56349B97AFBF6\""
+                    ],
+                    "cache-control": [
+                        "no-cache"
+                    ],
+                    "content-type": [
+                        "application/json"
+                    ],
+                    "date": [
+                        "Wed, 04 Sep 2019 18:18:10 GMT"
+                    ],
+                    "content-length": [
+                        "22603"
+                    ]
+                },
+                "body": {
+                    "data": {
+                        "value": [
+                            {
+                                "id": "/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/test_webapp/providers/Microsoft.Web/sites/cctestwebapp",
+                                "name": "cctestwebapp",
+                                "type": "Microsoft.Web/sites",
+                                "kind": "api",
+                                "location": "Central US",
+                                "tags": {
+                                    "hidden-related:/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourcegroups/anzoloch-test-api/providers/Microsoft.Web/serverfarms/ServicePlane63a2834-992c": "empty"
+                                },
+                                "properties": {
+                                    "name": "cctestwebapp",
+                                    "state": "Running",
+                                    "hostNames": [
+                                        "cctestwebapp.azurewebsites.net"
+                                    ],
+                                    "webSpace": "test_webapp-CentralUSwebspace",
+                                    "selfLink": "https://waws-prod-dm1-131.api.azurewebsites.windows.net:454/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/webspaces/test_webapp-CentralUSwebspace/sites/cctestwebapp",
+                                    "repositorySiteName": "cctestwebapp",
+                                    "owner": null,
+                                    "usageState": "Normal",
+                                    "enabled": true,
+                                    "adminEnabled": true,
+                                    "enabledHostNames": [
+                                        "cctestwebapp.azurewebsites.net",
+                                        "cctestwebapp.scm.azurewebsites.net"
+                                    ],
+                                    "siteProperties": {
+                                        "metadata": null,
+                                        "properties": [
+                                            {
+                                                "name": "LinuxFxVersion",
+                                                "value": ""
+                                            },
+                                            {
+                                                "name": "WindowsFxVersion",
+                                                "value": null
+                                            }
+                                        ],
+                                        "appSettings": null
+                                    },
+                                    "availabilityState": "Normal",
+                                    "sslCertificates": [],
+                                    "csrs": [],
+                                    "cers": null,
+                                    "siteMode": "Limited",
+                                    "hostNameSslStates": [
+                                        {
+                                            "name": "cctestwebapp.azurewebsites.net",
+                                            "sslState": "Disabled",
+                                            "ipBasedSslResult": null,
+                                            "virtualIP": null,
+                                            "thumbprint": null,
+                                            "toUpdate": null,
+                                            "toUpdateIpBasedSsl": null,
+                                            "ipBasedSslState": "NotConfigured",
+                                            "hostType": "Standard"
+                                        },
+                                        {
+                                            "name": "cctestwebapp.scm.azurewebsites.net",
+                                            "sslState": "Disabled",
+                                            "ipBasedSslResult": null,
+                                            "virtualIP": null,
+                                            "thumbprint": null,
+                                            "toUpdate": null,
+                                            "toUpdateIpBasedSsl": null,
+                                            "ipBasedSslState": "NotConfigured",
+                                            "hostType": "Repository"
+                                        }
+                                    ],
+                                    "computeMode": "Dedicated",
+                                    "serverFarm": null,
+                                    "serverFarmId": "/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/test_webapp/providers/Microsoft.Web/serverfarms/ccctestServicePlanehoa5wg43utld4",
+                                    "reserved": false,
+                                    "isXenon": false,
+                                    "hyperV": false,
+                                    "lastModifiedTimeUtc": "2019-09-04T17:53:57.0733333",
+                                    "storageRecoveryDefaultState": "Running",
+                                    "contentAvailabilityState": "Normal",
+                                    "runtimeAvailabilityState": "Normal",
+                                    "siteConfig": null,
+                                    "deploymentId": "cctestwebapp",
+                                    "trafficManagerHostNames": null,
+                                    "sku": "Standard",
+                                    "scmSiteAlsoStopped": false,
+                                    "targetSwapSlot": null,
+                                    "hostingEnvironment": null,
+                                    "hostingEnvironmentProfile": null,
+                                    "clientAffinityEnabled": true,
+                                    "clientCertEnabled": false,
+                                    "clientCertExclusionPaths": null,
+                                    "hostNamesDisabled": false,
+                                    "domainVerificationIdentifiers": null,
+                                    "kind": "api",
+                                    "inboundIpAddress": "23.101.119.44",
+                                    "possibleInboundIpAddresses": "23.101.119.44",
+                                    "outboundIpAddresses": "23.101.119.44,23.99.215.110,23.99.213.182,168.61.218.60,23.99.208.251",
+                                    "possibleOutboundIpAddresses": "23.101.119.44,23.99.215.110,23.99.213.182,168.61.218.60,23.99.208.251,23.99.211.22,23.99.209.158,23.99.214.44",
+                                    "containerSize": 0,
+                                    "dailyMemoryTimeQuota": 0,
+                                    "suspendedTill": null,
+                                    "siteDisabledReason": 0,
+                                    "functionExecutionUnitsCache": null,
+                                    "maxNumberOfWorkers": null,
+                                    "homeStamp": "waws-prod-dm1-131",
+                                    "cloningInfo": null,
+                                    "hostingEnvironmentId": null,
+                                    "tags": {
+                                        "hidden-related:/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourcegroups/anzoloch-test-api/providers/Microsoft.Web/serverfarms/ServicePlane63a2834-992c": "empty"
+                                    },
+                                    "resourceGroup": "test_webapp",
+                                    "defaultHostName": "cctestwebapp.azurewebsites.net",
+                                    "slotSwapStatus": null,
+                                    "httpsOnly": false,
+                                    "redundancyMode": "None",
+                                    "inProgressOperationId": null,
+                                    "geoDistributions": null,
+                                    "privateEndpointConnections": null
+                                }
+                            }
+                        ],
+                        "nextLink": null,
+                        "id": null
+                    }
+                }
+            }
+        },
+        {
+            "request": {
+                "method": "GET",
+                "uri": "https://management.azure.com/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/test_webapp/providers/Microsoft.Web/sites/cctestwebapp/config/web?api-version=2018-11-01",
+                "body": null,
+                "headers": {}
+            },
+            "response": {
+                "status": {
+                    "code": 200,
+                    "message": "OK"
+                },
+                "headers": {
+                    "cache-control": [
+                        "no-cache"
+                    ],
+                    "content-type": [
+                        "application/json"
+                    ],
+                    "date": [
+                        "Wed, 04 Sep 2019 18:18:12 GMT"
+                    ],
+                    "content-length": [
+                        "3133"
+                    ]
+                },
+                "body": {
+                    "data": {
+                        "id": "/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourceGroups/test_webapp/providers/Microsoft.Web/sites/cctestwebapp/config/web",
+                        "name": "cctestwebapp",
+                        "type": "Microsoft.Web/sites/config",
+                        "location": "Central US",
+                        "tags": {
+                            "hidden-related:/subscriptions/ea42f556-5106-4743-99b0-c129bfa71a47/resourcegroups/anzoloch-test-api/providers/Microsoft.Web/serverfarms/ServicePlane63a2834-992c": "empty"
+                        },
+                        "properties": {
+                            "numberOfWorkers": 1,
+                            "defaultDocuments": [
+                                "Default.htm",
+                                "Default.html",
+                                "Default.asp",
+                                "index.htm",
+                                "index.html",
+                                "iisstart.htm",
+                                "default.aspx",
+                                "index.php",
+                                "hostingstart.html"
+                            ],
+                            "netFrameworkVersion": "v4.0",
+                            "phpVersion": "5.6",
+                            "pythonVersion": "",
+                            "nodeVersion": "",
+                            "linuxFxVersion": "",
+                            "windowsFxVersion": null,
+                            "requestTracingEnabled": false,
+                            "remoteDebuggingEnabled": false,
+                            "remoteDebuggingVersion": "VS2017",
+                            "httpLoggingEnabled": false,
+                            "azureMonitorLogCategories": null,
+                            "logsDirectorySizeLimit": 35,
+                            "detailedErrorLoggingEnabled": false,
+                            "publishingUsername": "$cctestwebapp",
+                            "publishingPassword": null,
+                            "appSettings": null,
+                            "metadata": null,
+                            "connectionStrings": null,
+                            "machineKey": null,
+                            "handlerMappings": null,
+                            "documentRoot": null,
+                            "scmType": "None",
+                            "use32BitWorkerProcess": true,
+                            "webSocketsEnabled": false,
+                            "alwaysOn": false,
+                            "javaVersion": null,
+                            "javaContainer": null,
+                            "javaContainerVersion": null,
+                            "appCommandLine": "",
+                            "managedPipelineMode": "Integrated",
+                            "virtualApplications": [
+                                {
+                                    "virtualPath": "/",
+                                    "physicalPath": "site\\wwwroot",
+                                    "preloadEnabled": false,
+                                    "virtualDirectories": null
+                                }
+                            ],
+                            "winAuthAdminState": 0,
+                            "winAuthTenantState": 0,
+                            "customAppPoolIdentityAdminState": false,
+                            "customAppPoolIdentityTenantState": false,
+                            "runtimeADUser": null,
+                            "runtimeADUserPassword": null,
+                            "loadBalancing": "LeastRequests",
+                            "routingRules": [],
+                            "experiments": {
+                                "rampUpRules": []
+                            },
+                            "limits": null,
+                            "autoHealEnabled": false,
+                            "autoHealRules": null,
+                            "tracingOptions": null,
+                            "vnetName": "",
+                            "siteAuthEnabled": false,
+                            "siteAuthSettings": {
+                                "enabled": null,
+                                "unauthenticatedClientAction": null,
+                                "tokenStoreEnabled": null,
+                                "allowedExternalRedirectUrls": null,
+                                "defaultProvider": null,
+                                "clientId": null,
+                                "clientSecret": null,
+                                "clientSecretCertificateThumbprint": null,
+                                "issuer": null,
+                                "allowedAudiences": null,
+                                "additionalLoginParams": null,
+                                "isAadAutoProvisioned": false,
+                                "googleClientId": null,
+                                "googleClientSecret": null,
+                                "googleOAuthScopes": null,
+                                "facebookAppId": null,
+                                "facebookAppSecret": null,
+                                "facebookOAuthScopes": null,
+                                "twitterConsumerKey": null,
+                                "twitterConsumerSecret": null,
+                                "microsoftAccountClientId": null,
+                                "microsoftAccountClientSecret": null,
+                                "microsoftAccountOAuthScopes": null
+                            },
+                            "cors": null,
+                            "push": null,
+                            "apiDefinition": null,
+                            "apiManagementConfig": null,
+                            "autoSwapSlotName": null,
+                            "localMySqlEnabled": false,
+                            "managedServiceIdentityId": null,
+                            "xManagedServiceIdentityId": null,
+                            "ipSecurityRestrictions": [
+                                {
+                                    "ipAddress": "Any",
+                                    "action": "Allow",
+                                    "priority": 1,
+                                    "name": "Allow all",
+                                    "description": "Allow all access"
+                                }
+                            ],
+                            "scmIpSecurityRestrictions": [
+                                {
+                                    "ipAddress": "Any",
+                                    "action": "Allow",
+                                    "priority": 1,
+                                    "name": "Allow all",
+                                    "description": "Allow all access"
+                                }
+                            ],
+                            "scmIpSecurityRestrictionsUseMain": false,
+                            "http20Enabled": false,
+                            "minTlsVersion": "1.0",
+                            "ftpsState": "AllAllowed",
+                            "reservedInstanceCount": 0,
+                            "preWarmedInstanceCount": null,
+                            "healthCheckPath": null,
+                            "fileChangeAuditEnabled": false
+                        }
+                    }
+                }
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/tools/c7n_azure/tests/test_webapp.py b/tools/c7n_azure/tests/test_webapp.py
--- a/tools/c7n_azure/tests/test_webapp.py
+++ b/tools/c7n_azure/tests/test_webapp.py
@@ -44,3 +44,27 @@ def test_find_by_name(self):
         })
         resources = p.run()
         self.assertEqual(len(resources), 1)
+
+    @arm_template('webapp.json')
+    def test_find_by_min_tls(self):
+        # webapp.json deploys a webapp with minTlsVerion='1.0'
+        p = self.load_policy({
+            'name': 'test-azure-webapp',
+            'resource': 'azure.webapp',
+            'filters': [
+                {
+                    'type': 'value',
+                    'key': 'name',
+                    'op': 'eq',
+                    'value_type': 'normalize',
+                    'value': 'cctestwebapp'},
+                {
+                    'type': 'configuration',
+                    'key': 'minTlsVersion',
+                    'value': '1.2',
+                    'op': 'ne'
+                }
+            ]
+        })
+        resources = p.run()
+        self.assertEqual(len(resources), 1)