Demo: restrict access to Azure key vault using service endpoints
Tags: azure, powershell, keyvault, security
Published: 2019-12-18 03:18:50

Azure Key Vault is a service which provides secure storage and management of the secrets, encryption keys, and certificates an application requires at run time. It improves upon legacy secret management approaches such as storing values in configuration or source code while providing a consistent access experience across Azure PaaS, IaaS, and serverless platforms.  

In this post I'll show how to lock down access to a key vault so that these secrets can only be accessed via the Microsoft Azure backbone network. To do so, I'll restrict access at the network level using a service endpoint bound to a subnet in a virtual network, and apply firewall settings to deny Internet traffic. The provisioned Azure function will retrieve secrets at run time from the key vault via the service endpoint. 

The entire solution is scripted in Powershell and can be found in the [linked github repository](https://github.com/jkewley/KeyVaultServiceEndpoint).

## Azure Function
I'll be using an Azure function hosted on a dedicated App Service plan as the client application which will be accessing the key vault. [At the time of this writing service endpoint access from consumption-based functions is not available](https://docs.microsoft.com/en-us/azure/azure-functions/functions-networking-options#matrix-of-networking-features) which is why I'm using an app service plan.

Like just about every other Azure service offering available, App Service plans have continued to grow and evolve over time. One of the recent additions is [regional vnet integration](https://docs.microsoft.com/en-us/azure/app-service/web-sites-integrate-with-vnet#regional-vnet-integration) which allows you to connect your app service plan to a non-internet routable virtual network. This is an evolution of the previous implementation of vnet connectivity which used a point-to-site VPN and virtual gateway to establish this network flow. As of this writing, both of the approaches are still supported. The new approach makes sense when your Azure assets are in the same region, whereas the old approach is required if you're trying to connect to services in another region.

## Key Vault
When working with key vault in Azure you interact with two [resource endpoints](https://docs.microsoft.com/en-us/azure/key-vault/key-vault-secure-your-key-vault#resource-endpoints) with distinct access planes
 * The **data plane** is where the secured data is stored. This plane can only be reached when an access policy is in place which explicitly provides the appropriate permission (read, list, update, etc.) for the specified resource (secret, key, certificate) for the identified caller. This is the plane which applications access when they require keys, secrets, and certificates.
 * The **management plane** is where you perform administrative activites on the key vault including configuration of role based access control (access to the management plane) and access policies (access to the data plane)
Requests to either plane require proof of authentication by a trusted Azure Active Directory. This is a very common approach when securing access to Azure platforms and APIs, and provides for strong governance. 

Our team typically provisions a vault early in the development life cycle for each application or integration we deploy to Azure. This helps us reinforce the practice of not including secure data in code which might get committed to source control.

## Architecture
The Powershell script will produce the following architecture:

![Key vault architecture](https://thepracticaldev.s3.amazonaws.com/i/0bgektxnsdn010fupaxw.png)

## Script settings

The script has a few variables which you'll want to update

```powershell
# used to create uniquely named assets to avoid naming collisions
$yourUniquePrefix = "myPrefix"          

# storage account name. append prefix manually due to storage naming constraints (lowercase, alpha, max 24)
$storageName = "myprefixtestpaasstorage"   

# the name value from 'az account list' for the subscription where you want to deploy the demo
$subscriptionName = "YOUR SUBSCRIPTION NAME"
```

The script leverages the [latest Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) (version 2.0.77) which introduced support for `az functionapp vnet-integration add`. 

## What the script does

This script will deploy the following assets in the East US2 region:
 * a resource group
 * a vnet and subnet
 * a key vault
 * a storage account
 * an app service plan (Premium!) and function app with a managed identity
 * a deployed Powershell function app which retrieves and returns the key vault secret

It connects the app service plan to the vnet and sets up a service endpoint for the subnet. Finally, it provides the function app's identity `get` rights to the key vault's secrets.

The function uses Powershell and the local managed identity endpoint to demonstrate that it is able to retrieve the secrets. As of the time that this was written, [key vault configuration references are not supported with service endpoints](https://github.com/Azure/azure-webjobs-sdk/issues/746).

The script walks through four validation tests.
 * First it shows that it can read the secret using the Azure CLI over the Internet with a firewall rule in place which allows the caller's IP address. 
 * Next it invokes the function, which returns the secret in the http response payload
 * It then removes the firewall rule and demonstrates that it can no longer read the secret from the Internet
 * It makes a final call to the function app to show that it is still able to access the vault through the subnet's service endpoint

[The code is heavily commented to make it easy to follow along](https://github.com/jkewley/KeyVaultServiceEndpoint). Enjoy!