Skip to content

Using Azure Key Vault in your Applications

Introduction:

When developing cloud applications, it can be important to abstract your keys and secrets out of your environment for security. Failing to do so can result in compromised security issues escalating to other environments and services. In addition this can reduce the steps required for environmental setup, as well as provide a single point of management for multiple environments.

When using Azure Key Vault for this purpose in an “Azure Active Directory App” we built, we found that the documentation for using Key Vault was lacking at the time of writing this article. Apps must be registered with Active Directory in Azure if you’d like to Authenticate and Authorize with Azure Active Directory.

This will be a quick overview of the process we used to set up Azure Key Vault for in our application. We will be including code examples from Python 3.6 and Node.

Dependencies:

  • The Node version requires the use of the library ‘azure-keyvault’ (https://www.npmjs.com/package/azure-keyvault)
  • The Python version requires the use of the library ‘azure’ (https://pypi.org/project/azure/)
  • This assumes you have an Azure Portal account, and an Azure Active Directory application set up, as well as the required permissions for your account and subscription.

The following documentation provided by Microsoft should assist in the previously mentioned setup if needed. (https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal)

Setup:

1.  Directory ID

The first piece of information you need to gather is the “Directory ID” or “Tennant ID”. Various pieces of documentation refer to these interchangeably, but for the purpose of this guide, we will refer to it as the Directory ID. The Directory ID is a GUID located in the properties section of your Azure Active Directory setup.

2.  Application ID

The next step is to gather the Application ID from the App Registration. The Application ID is a GUID located on the app registration for your app. Azure Active Directory App Registrations are located under Azure Active Directory -> App Registrations.

3.  Azure Key Vault

The next step is to create an Azure Key Vault. This guide presumes this has been set up, and you’ve added a “secret”. In order to set up an Azure Key Vault, the following documentation can assist you.  (https://docs.microsoft.com/en-us/azure-stack/user/azure-stack-key-vault-manage-portal)  In addition, it can be done programmatically.  (https://docs.microsoft.com/en-us/azure/key-vault/quick-create-node#deploy-the-node-app-to-azure-and-retrieve-the-secret-value).

4.  Access Policy

The next step is to set up an access policy that allows your Active Directory App to access its values. On the access policies page, add a new policy. Under “Select principle” enter your “Application ID” into the search. This should bring up your active directory application. Select the app, then hit the select button.

Once you’ve selected the principle, you’ll have to configure the access settings. This demo uses the “secret list” and “secret get” permissions. Once you’ve selected your permission and hit OK, you’ll be back at the access policies for your key vault. Please note you need to hit save on this page, simply creating the policy doesn’t save it.

5.  DNS Name

The last piece of information you’ll need to gather is the DNS Name for the key vault URL. This can be gathered from the properties page of the key vault instance.

Additionally, this demo uses the key / secret authentication method, however there are other methods of authentication. At the time of writing I was unable to find documentation on setting them up properly at the time of writing this article with the given libraries. As such, if you’re using this form of authentication you’ll need a key / secret combination from the Azure Active Directory Application.

For Node:

1.  Client Object

The first step is to create the client object. This presumes you’ve set up environment variables for the Azure Active Directory key and secret.

var keyVault = require('azure-keyvault');

let client = new keyVault.KeyVaultClient(new keyVault.KeyVaultCredentials((challenge, callback) =>
{

	return (new authenticationContext(challenge.authorization)).acquireTokenWithClientCredentials(challenge.resource, process.env.AD_CLIENT_KEY, process.env.AD_CLIENT_SECRET, (err, tokenResponse) =>
	{

		if (err)
		{

			// console.log('error getting authorization', err);

			callback(err);
		}

		else
		{

			callback(null, tokenResponse.tokenType + ' ' + tokenResponse.accessToken);
		}
	});
}));

Once this client object is created, you’ll use it to interact with the Key Vault. Below is an example of how to pull a secret.

2.  Pull a Secret

There’s no (documented) way to pull the current version of a secret, which poses a problem for most use cases. As such, we iterate over the secrets until we find one that’s enabled, and hope the first one is the most relevant one. Once we have the secret version, the version ID is only sent in URL format, but we need the version number alone. As such, we have to split the ID and send that as the version ID.

let getSecret = function(secretName, callback)
{

	client.client.getSecretVersions(process.env.KV_URL, secretName, (err, res) =>
	{

		if (err)
		{

			callback(err);
		}

		else
		{

			let foundValidValue = false;

			for (let index in res)
			{

				let obj = res[index];

				if (obj && obj.attributes && obj.attributes.enabled)
				{
					// Only way to get id from returned id, which is in url format.
					let id_split = obj.id.split('/');

					this.client.getSecret(process.env.KV_URL, secretName, id_split[(id_split.length || 1) - 1], (err, res) =>
					{

						if (err)
						{

							callback(err);
						}

						else
						{

						callback(null, res['value']);
						}
					});

					foundValidValue = true;

					break;
				}
			}

			if (!foundValidValue)
			{

				callback(new Error('no valid value ' + JSON.stringify(secretName)));
			}
		}
	});
};

For Python, the example is similar:

First we create the client:

1.  Callback

The first step is to create the callback function. This could also be accomplished with a lambda.

# Environment variable manager
from os import environ
from azure.keyvault import KeyVaultClient, KeyVaultAuthentication
from azure.common.credentials import ServicePrincipalCredentials

def auth_callback(server, resource, scope, *args):

	credentials = ServicePrincipalCredentials(client_id=environ.get('AD_CLIENT_KEY'), secret=environ.get('AD_CLIENT_SECRET'), tenant=environ.get('AD_TENANT_ID'), resource='https://vault.azure.net')

	return credentials.token['token_type'], credentials.token['access_token']

2.  Create the Client

Then we create the client.

keyvault_client = KeyVaultClient(KeyVaultAuthentication(authorization_callback=auth_callback))

3.  Define the Creation Method

Once the keyvault client has been created, we define the creation method as the following:

def get_secret(var):

	res = ''

	for version in keyvault_client.get_secret_versions(environ.get('KV_URL'), var):

		if version and version.attributes and version.attributes.enabled:

			version_id_split = version.id.split('/')

			version_id = version_id_split[(len(version_id_split) if len(version_id_split) else 1) - 1]

			response = self.keyvault_client.get_secret(self.get_env_var('KV_URL'), var, version_id)

			self.secrets[var] = response.value

			res = response.value

			break

		else:
			# print(version.attributes, version.attributes.enabled)
			pass
	# else on loop in python triggers if loop was not broken
	else:

		raise EnvironmentError('unable to find env error', var, res)
		
	return res

This follows the same steps of iterating over versions and getting the text from the first valid result. This implementation is simpler due to the fact that it uses a synchronous workflow.

It may be worth considering caching the results in an object, assuming the Azure KeyVault doesn’t do this natively, but this could be a security concern.

Description of the image