Securing Azure PaaS Leveraging Azure Policies

Sergio Jimenez
6 min readOct 15, 2023

--

In light of the expanding threat landscape aimed at large enterprises and their cloud assets, fortifying the security of Platform as a Service (PaaS) offerings have grown significantly in importance. By default Azure offerings such as Azure Storage Accounts allow for access via a public IP by default. With no straightforward built-in way to manage whitelisting within PaaS resources currently. We can reduce our attack surface by disabling this setting in our ARM templates or during the creation of the resource through the Azure Portal or with IaC. We can even enforce that this setting be disabled through the use of built-in Azure Policies. However, what are we left to do when we need public access to these PaaS resources from select networks?

We can leverage custom-built policies to enforce a policy of type modify to update our resource IP rules to an allowed list of select networks.

Storage Accounts currently have a limit of 200 supported network rules https://learn.microsoft.com/en-us/azure/storage/common/storage-network-security?tabs=azure-portal#:~:text=Each%20storage%20account%20supports%20up%20to%20200%20virtual%20network%20rules.

Auditing with Built-in Azure Policies

Azure offers built-in policies that can help us audit whether public network access on our PaaS assets is enabled. We can follow their documentation here to help get that set up as a policy for our subscription.

We can take this further however and take the basics of this audit policy and create a custom policy with the effect of modify to update our deployed resources with our allowed list of IPs.

Custom Azure Policy with Modify Effect

Before enforcing a policy against the resources in our subscription, we need the following:

1. Policy definition creation

2. Policy definition assignment

3. Remediation to bring all resources within scope into compliance

1. Policy definition creation

With the policy below we can accomplish a few things. At the time of deployment when ARM validation checks for policy compliance, we can modify the resource to limit network access to select networks without interrupting developers from continuing their deployment of their resources so long as the template is valid. As a secondary precaution, if we are not able to modify the resource with our policy, we still report on the resource through the audit conflict effect that we set in our action portion of the policy.

{
"mode": "All",
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Storage/storageAccounts"
},
{
"field": "Microsoft.Storage/storageAccounts/networkAcls.ipRules[*].value",
"notIn": "[parameters('allowedIpRules')]"
},
{
"count": {
"field": "Microsoft.Storage/storageAccounts/networkAcls.ipRules[*]"
},
"notEquals": "[length(parameters('networkAclsIpRules'))]"
}
]
},
"then": {
"effect": "[parameters('effect')]",
"details": {
"conflictEffect": "audit",
"roleDefinitionIds": [
"/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab"
],
"operations": [
{
"operation": "addOrReplace",
"field": "Microsoft.Storage/storageAccounts/networkAcls.ipRules",
"value": "[parameters('networkAclsIpRules')]"
},
{
"operation": "addOrReplace",
"field": "Microsoft.Storage/storageAccounts/networkAcls.defaultAction",
"value": "Deny"
}
]
}
}
},
"parameters": {
"effect": {
"type": "String",
"metadata": {
"displayName": "Effect",
"description": "Enable or disable the execution of the policy"
},
"allowedValues": [
"Modify",
"Disabled",
"Deny",
"Audit"
],
"defaultValue": "Modify"
},
"networkAclsIpRules": {
"type": "Array",
"metadata": {
"displayName": "networkAclsIpRules",
"description": "List of IP rules that need to be included for in the ipRules of the resource"
},
"defaultValue": [
{
"value": "xx.xxx.xxx.xxx",
"action": "Allow"
}
]
},
"allowedIpRules": {
"type": "Array",
"metadata": {
"displayName": "IP list of allowed values",
"description": "List of IP rules that need to be included for in the ipRules of the resource"
},
"defaultValue": [
"xx.xxx.xxx.xxx"
]
}
}
}

Our policy is broken into 3 main parts. The conditions we are looking to satisfy, the action we want to take if conditions are met, and our parameters.

In our conditions, we are setting that all of the fields we are including need to be found true in order for the policy to determine if the resource is compliant with the policy.

"allOf": [
{
"field": "type",
"equals": "Microsoft.Storage/storageAccounts"
},
{
"field": "Microsoft.Storage/storageAccounts/networkAcls.ipRules[*].value",
"notIn": "[parameters('allowedIpRules')]"
},
{
"count": {
"field": "Microsoft.Storage/storageAccounts/networkAcls.ipRules[*]"
},
"notEquals": "[length(parameters('networkAclsIpRules'))]"
}
]

For our action, we do the following:

"then": {
"effect": "[parameters('effect')]",
"details": {
"conflictEffect": "audit",
"roleDefinitionIds": [
"/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab"
],
"operations": [
{
"operation": "addOrReplace",
"field": "Microsoft.Storage/storageAccounts/networkAcls.ipRules",
"value": "[parameters('networkAclsIpRules')]"
},
{
"operation": "addOrReplace",
"field": "Microsoft.Storage/storageAccounts/networkAcls.defaultAction",
"value": "Deny"
}
]
}
}
}

If our conditions are met we are completing the operation of addOrReplace on the field Microsoft.Storage/storageAccounts/networkAcls.ipRules and setting it to our parameter networkAclsIpRules. This portion also creates the necessary permissions for the managed identity that will be performing our modify action on the resources that are non-compliant with the policy. The permissions required will be of at least a contributor role. Our example uses storage account contributor built-in azure role.

Our second action is to limit access to the select networks we are allowing to access the resource. Microsoft.Storage/storageAccounts/networkAcls.defaultAction is the field that is essential to add as excluding it will result in the resource continuing to be accessible to the public.

Lastly, our parameters:

"parameters": {
"effect": {
"type": "String",
"metadata": {
"displayName": "Effect",
"description": "Enable or disable the execution of the policy"
},
"allowedValues": [
"Modify",
"Disabled",
"Deny",
"Audit"
],
"defaultValue": "Modify"
},
"networkAclsIpRules": {
"type": "Array",
"metadata": {
"displayName": "networkAclsIpRules",
"description": "List of IP rules that need to be included for in the ipRules of the resource"
},
"defaultValue": [
{
"value": "xxx.xxx.xxx.xxx",
"action": "Allow"
}
]
},
"allowedIpRules": {
"type": "Array",
"metadata": {
"displayName": "IP list of allowed values",
"description": "List of IP rules that need to be included for in the ipRules of the resource"
},
"defaultValue": [
"xxx.xxx.xxx.xxx"
]
}
}

2. Policy definition assignment

With our policy created, we can move forward with the assignment through the Azure Portal.

  1. Under subscriptions select the desired subscriptions and navigate to policies.

2. Select assign policies from the policy blade.

3. Under settings we select the policy we created as the policy to assign and fill in the scope. We can enforce this policy down to a resource group. For our purposes, we used the subscription scope to assign the sergio-enforceWhiteListStorage policy.

  • Advanced
  • Kept defaults from Azure
  • Parameters
  • These are the same parameters defined in our policy definition

4. Remediation settings

For our remediation settings, we choose to use a managed identity so that we do not have to manage the credentials of the service principal used to complete the remediation tasks and select the appropriate permissions needed to make changes to the resource with the policy.
We are also able to set a custom message in the policy assignment creation to allow for the identification of issues with resources being created out of compliance.

3. Remediation to bring all resources within scope into compliance

This is accomplished through the use of built-in remediation tasks that will evaluate resources within scope and modify their configuration to bring them back within compliance and secure our PaaS resources.

Summary

To summarize, we can leverage Azure Policies to not only audit non-compliant resources but also modify them at deployment to bring them into compliance. We are also able to utilize remediation tasks to modify existing non-compliant resources ensuring that we can secure our PaaS resources and limit access to them.

Similar policies can be constructed and applied across different types of resources such as cosmos dbs and provide an improved security posture.

--

--