Background
Azure role-based access control (Azure RBAC) provides fine grained control over access to Azure resources. Azure RBAC is founded on top of the Azure Resource Manager which allows us to provide access authorisation at differing scope levels ranging from the Management Group through to individual resources.
With RBAC enabled key vaults we can manage access to the resource and data stored in the vault. We can also manage access for individual keys, secrets, and certificates.
Scenario
In this scenario we have an application that has stored all its secrets in its own application specific key vault. Part of this application makes use of an Azure Function that we would like to place into Azure API Management (APIM).
An Application can be a single or group of related services.
When deploying the Azure Function we are placing the Function Authorisation Key into the Application Specific Key Vault. As part of the APIM API Backend we would like to add the Function Authorisation Key as a header and obtain this key from the application specific key vault.
If we provide APIM access to the entire application key vault we would be compromising our security boundary.
Security Boundary - We do not want to provide complete access to the application Key Vault. If there is a breach of security we do not want to compromise the application and widen the blast radius.
To mitigate this, we can specify granular access to the specific secret in the key vault that APIM requires. In this case, if there were to be a security breach, we have limited the reach and access to which damage could spread.
Solution
To setup the RBAC permission between APIM and the application specific Key Vault, we will need to have the following configured:
- For a deployment principle to add role assignments, you must have Microsoft.Authorization/roleAssignments/write and Microsoft.Authorization/roleAssignments/delete permissions setup, such as one of the following:
- Make sure your Application Key Vault is RBAC Enabled.
resource symbolicname 'Microsoft.KeyVault/vaults@2022-07-01' = {
name: 'string'
location: 'string'
properties: {
...
enableRbacAuthorization: true
...
}
}
- Azure API Management is setup to use System Assigned Managed Identity.
resource symbolicname 'Microsoft.ApiManagement/service@2023-03-01-preview' = {
name: 'string'
location: 'string'
sku: {
...
}
identity: {
type: 'SystemAssigned'
}
properties: {
...
}
}
- The Azure Function Auth Key added into Key Vault as a secret.
Note: The function default host key is created and only accessible after the function code is provisioned. The following IaC would need to be conducted as a secondary deployment.
@description('Retrieve the Application Key Vault instance to store secrets')
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
name: keyVaultName
}
@description('Retrieve the Function App for linking as a backend')
resource functionApp 'Microsoft.Web/sites@2022-09-01' existing = {
name: functionName
...
}
@description('Vault the Function App Key as a secret')
resource vaultFunctionAppKey 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = {
name: 'functionAppKey'
parent: keyVault
properties: {
contentType: 'string'
value: listkeys('${functionApp.id}/host/default/', '2021-02-01').functionkeys.default
}
}
- Grant APIM Identity Role permissions to access the Key Vault Secret
- Granting Key Vault Reader Role.
- Property Scope is pointing at the Key Vault Secret so access is confined to only that secret.
@description('APIM Instance')
resource apimInstance 'Microsoft.ApiManagement/service@2022-08-01' = {
name: apimInstanceName
...
}
@description('Grant APIM Key Vault Reader for the function API key secret')
resource grantAPIMPermissionsToSecret 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(keyVault.id)
scope: vaultFunctionAppKey
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')
principalId: apimInstance.identity.principalId
principalType: 'ServicePrincipal'
}
}
- Setup a APIM Backend with a Named Value to make use of the Key Vault Secret
@description('Create the backend for the Function API')
resource functionBackend 'Microsoft.ApiManagement/service/backends@2022-08-01' = {
name: apiName
parent: apimInstance
properties: {
protocol: 'http'
url: 'https://${functionApp.properties.defaultHostName}/api'
resourceId: 'https://management.azure.com${functionApp.id}'
tls: {
validateCertificateChain: true
validateCertificateName: true
}
credentials: {
header: {
'x-functions-key': [
'{{${apiName}-key}}'
]
}
}
}
dependsOn: [
functionBackendNamedValues
]
}
@description('Create the named value for the function API backend')
resource functionBackendNamedValues 'Microsoft.ApiManagement/service/namedValues@2022-08-01' = {
name: '${apiName}-key'
parent: apimInstance
properties: {
displayName: '${apiName}-key'
tags: [
'key'
'function'
]
secret: true
keyVault: {
identityClientId: null
secretIdentifier: '${keyVault.properties.vaultUri}secrets/${vaultFunctionAppKey.name}'
}
}
dependsOn: [
grantAPIMPermissionsToSecret
]
}
Summary
And there we have it, APIM now has granular access to the secrets it requires without compromising our security boundaries that have been put in place to protect the backing application.
Have a play and have fun.