Manage data exfiltration to Azure Storage accounts with virtual network service endpoint policies using the Azure CLI

Virtual network service endpoint policies enable you to apply access control on Azure Storage accounts from within a virtual network over service endpoints. This is a key to securing your workloads, managing what storage accounts are allowed and where data exfiltration is allowed. In this article, you learn how to:

  • Create a virtual network and add a subnet.
  • Enable service endpoint for Azure Storage.
  • Create two Azure Storage accounts and allow network access to it from the subnet created above.
  • Create a service endpoint policy to allow access only to one of the storage accounts.
  • Deploy a virtual machine (VM) to the subnet.
  • Confirm access to the allowed storage account from the subnet.
  • Confirm access is denied to the non-allowed storage account from the subnet.

If you don't have an Azure subscription, create an Azure free account before you begin.

Prerequisites

  • This article requires version 2.0.28 or later of the Azure CLI. If using Azure Cloud Shell, the latest version is already installed.

Create a virtual network

Before creating a virtual network, you have to create a resource group for the virtual network, and all other resources created in this article. Create a resource group with az group create. The following example creates a resource group named myResourceGroup in the eastus location.

az group create \
  --name myResourceGroup \
  --location eastus

Create a virtual network with one subnet with az network vnet create.

az network vnet create \
  --name myVirtualNetwork \
  --resource-group myResourceGroup \
  --address-prefix 10.0.0.0/16 \
  --subnet-name Private \
  --subnet-prefix 10.0.0.0/24

Enable a service endpoint

In this example, a service endpoint for Microsoft.Storage is created for the subnet Private:

az network vnet subnet create \
  --vnet-name myVirtualNetwork \
  --resource-group myResourceGroup \
  --name Private \
  --address-prefix 10.0.0.0/24 \
  --service-endpoints Microsoft.Storage

Restrict network access for a subnet

Create a network security group with az network nsg create. The following example creates a network security group named myNsgPrivate.

az network nsg create \
  --resource-group myResourceGroup \
  --name myNsgPrivate

Associate the network security group to the Private subnet with az network vnet subnet update. The following example associates the myNsgPrivate network security group to the Private subnet:

az network vnet subnet update \
  --vnet-name myVirtualNetwork \
  --name Private \
  --resource-group myResourceGroup \
  --network-security-group myNsgPrivate

Create security rules with az network nsg rule create. The rule that follows allows outbound access to the public IP addresses assigned to the Azure Storage service:

az network nsg rule create \
  --resource-group myResourceGroup \
  --nsg-name myNsgPrivate \
  --name Allow-Storage-All \
  --access Allow \
  --protocol "*" \
  --direction Outbound \
  --priority 100 \
  --source-address-prefix "VirtualNetwork" \
  --source-port-range "*" \
  --destination-address-prefix "Storage" \
  --destination-port-range "*"

Each network security group contains several default security rules. The rule that follows overrides a default security rule that allows outbound access to all public IP addresses. The destination-address-prefix "Internet" option denies outbound access to all public IP addresses. The previous rule overrides this rule, due to its higher priority, which allows access to the public IP addresses of Azure Storage.

az network nsg rule create \
  --resource-group myResourceGroup \
  --nsg-name myNsgPrivate \
  --name Deny-Internet-All \
  --access Deny \
  --protocol "*" \
  --direction Outbound \
  --priority 110 \
  --source-address-prefix "VirtualNetwork" \
  --source-port-range "*" \
  --destination-address-prefix "Internet" \
  --destination-port-range "*"

The following rule allows SSH traffic inbound to the subnet from anywhere. The rule overrides a default security rule that denies all inbound traffic from the internet. SSH is allowed to the subnet so that connectivity can be tested in a later step.

az network nsg rule create \
  --resource-group myResourceGroup \
  --nsg-name myNsgPrivate \
  --name Allow-SSH-All \
  --access Allow \
  --protocol Tcp \
  --direction Inbound \
  --priority 120 \
  --source-address-prefix "*" \
  --source-port-range "*" \
  --destination-address-prefix "VirtualNetwork" \
  --destination-port-range "22"

Restrict network access to Azure Storage accounts

This section lists steps to restrict network access for an Azure Storage account from the given subnet in a Virtual network via service endpoint.

Create a storage account

Create two Azure storage accounts with az storage account create.

storageAcctName1="allowedstorageacc"

az storage account create \
  --name $storageAcctName1 \
  --resource-group myResourceGroup \
  --sku Standard_LRS \
  --kind StorageV2

storageAcctName2="notallowedstorageacc"

az storage account create \
  --name $storageAcctName2 \
  --resource-group myResourceGroup \
  --sku Standard_LRS \
  --kind StorageV2

After the storage accounts are created, retrieve the connection string for the storage accounts into a variable with az storage account show-connection-string. The connection string is used to create a file share in a later step.

saConnectionString1=$(az storage account show-connection-string \
  --name $storageAcctName1 \
  --resource-group myResourceGroup \
  --query 'connectionString' \
  --out tsv)

saConnectionString2=$(az storage account show-connection-string \
  --name $storageAcctName2 \
  --resource-group myResourceGroup \
  --query 'connectionString' \
  --out tsv)

View the contents of the variable and note the value for AccountKey returned in the output, because it's used in a later step.

echo $saConnectionString1

echo $saConnectionString2

Create a file share in the storage account

Create a file share in the storage account with az storage share create. In a later step, this file share is mounted to confirm network access to it.

az storage share create \
  --name my-file-share \
  --quota 2048 \
  --connection-string $saConnectionString1 > /dev/null

az storage share create \
  --name my-file-share \
  --quota 2048 \
  --connection-string $saConnectionString2 > /dev/null

Deny all network access to the storage account

By default, storage accounts accept network connections from clients in any network. To limit access to selected networks, change the default action to Deny with az storage account update. Once network access is denied, the storage account is not accessible from any network.

az storage account update \
  --name $storageAcctName1 \
  --resource-group myResourceGroup \
  --default-action Deny

az storage account update \
  --name $storageAcctName2 \
  --resource-group myResourceGroup \
  --default-action Deny

Enable network access from virtual network subnet

Allow network access to the storage account from the Private subnet with az storage account network-rule add.

az storage account network-rule add \
  --resource-group myResourceGroup \
  --account-name $storageAcctName1 \
  --vnet-name myVirtualNetwork \
  --subnet Private

az storage account network-rule add \
  --resource-group myResourceGroup \
  --account-name $storageAcctName2 \
  --vnet-name myVirtualNetwork \
  --subnet Private

Apply policy to allow access to valid storage account

Azure Service Endpoint policies are only available for Azure Storage. So, we'll be enabling Service Endpoint for Microsoft.Storage on this subnet for this example setup.

Service endpoint policies are applied over service endpoints. We will start by creating a service endpoint policy. We will then create the policy definitions under this policy for Azure Storage accounts to be approved for this subnet

Create a service endpoint policy

az network service-endpoint policy create \
  --resource-group myResourceGroup \
  --name mysepolicy \
  --location eastus

Save the resource URI for the allowed storage account in a variable. Before executing the command below, replace <your-subscription-id> with actual value of your subscription ID.

$serviceResourceId="/subscriptions/<your-subscription-id>/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/allowedstorageacc"

Create & add a policy definition for allowing the above Azure Storage account to the service endpoint policy

az network service-endpoint policy-definition create \
  --resource-group myResourceGroup \
  --policy-name mysepolicy \
  --name mypolicydefinition \
  --service "Microsoft.Storage" \
  --service-resources $serviceResourceId

And update the virtual network subnet to associate with it the service endpoint policy created in the previous step

az network vnet subnet update \
  --vnet-name myVirtualNetwork \
  --resource-group myResourceGroup \
  --name Private \
  --service-endpoints Microsoft.Storage \
  --service-endpoint-policy mysepolicy

Validate access restriction to Azure Storage accounts

Create the virtual machine

To test network access to a storage account, deploy a VM to the subnet.

Create a VM in the Private subnet with az vm create. If SSH keys do not already exist in a default key location, the command creates them. To use a specific set of keys, use the --ssh-key-value option.

az vm create \
  --resource-group myResourceGroup \
  --name myVmPrivate \
  --image UbuntuLTS \
  --vnet-name myVirtualNetwork \
  --subnet Private \
  --generate-ssh-keys

The VM takes a few minutes to create. After creation, take note of the publicIpAddress in the output returned. This address is used to access the VM from the internet in a later step.

Confirm access to storage account

SSH into the myVmPrivate VM. Replace <publicIpAddress> with the public IP address of your myVmPrivate VM.

ssh <publicIpAddress>

Create a folder for a mount point:

sudo mkdir /mnt/MyAzureFileShare1

Mount the Azure file share to the directory you created. Before executing the command below, replace <storage-account-key> with value of AccountKey from $saConnectionString1.

sudo mount --types cifs //allowedstorageacc.file.core.windows.net/my-file-share /mnt/MyAzureFileShare1 --options vers=3.0,username=allowedstorageacc,password=<storage-account-key>,dir_mode=0777,file_mode=0777,serverino

You receive the user@myVmPrivate:~$ prompt. The Azure file share successfully mounted to /mnt/MyAzureFileShare.

Confirm access is denied to storage account

From the same VM myVmPrivate, create a directory for a mount point:

sudo mkdir /mnt/MyAzureFileShare2

Attempt to mount the Azure file share from storage account notallowedstorageacc to the directory you created. This article assumes you deployed the latest version of Ubuntu. If you are using earlier versions of Ubuntu, see Mount on Linux for additional instructions about mounting file shares.

Before executing the command below, replace <storage-account-key> with value of AccountKey from $saConnectionString2.

sudo mount --types cifs //notallowedstorageacc.file.core.windows.net/my-file-share /mnt/MyAzureFileShare2 --options vers=3.0,username=notallowedstorageacc,password=<storage-account-key>,dir_mode=0777,file_mode=0777,serverino

Access is denied, and you receive a mount error(13): Permission denied error, because this storage account is not in the allow list of the service endpoint policy we applied to the subnet.

Exit the SSH session to the myVmPublic VM.

Clean up resources

When no longer needed, use az group delete to remove the resource group and all of the resources it contains.

az group delete --name myResourceGroup --yes

Next steps

In this article, you applied a service endpoint policy over an Azure virtual network service endpoint to Azure Storage. You created Azure Storage accounts and limited network access to only certain storage accounts (and thus denied others) from a virtual network subnet. To learn more about service endpoint policies, see Service endpoints policies overview.