How to give MSI enabled Function App access to Key Vault? - azure

I have a Function App with Managed service identity (MSI) enabled.
I'm trying to use this Function App to access a secret from my Key Vault.
I have added the code into my Function App to retrieve the secret.
AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
var secret = await keyVaultClient.GetSecretAsync("https://test-prototype-vault.vault.azure.net/secrets/batman/guidhere").ConfigureAwait(false);
I receive the following error:
Microsoft.Azure.WebJobs.Script: One or more errors occurred. Microsoft.Azure.KeyVault: Access denied.
I think this is because I need to (as described in the link above).
You may need to configure the target resource to allow access from
your application. For example, if you request a token to Key Vault,
you need to make sure you have added an access policy that includes
your application's identity.
I don't know how to do that. I have gone to my Key Vault and tried to add an Access Policy - I can't find the application to Select Principle option.
Setup of Azure Function.
What happens when I try to add the principal.

This blog has details but you need to go into key vault and give the function app access to secrets in a new access policy
https://medium.com/#jeffhollan/getting-key-vault-secrets-in-azure-functions-37620fd20a0b
The name of your function app should show in list of users

Related

Azure Key Vault: The user, group, or app does not have secrets set permission on key vault

I am creating a script using Azure CLI that will automatically generate an App Registration (service principal), and then use that App Registration to create a secret that will be stored in Azure Key Vault.
However, I am getting the following error:
The user, group or application 'appid=04b07795-8ddb-461a-bbee-02f9e1bf7b46;oid=0ec2b0e8-daeb-46a8-b627-0d4f61f87157;numgroups=134;iss=https://sts.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/' does not have secrets set permission on key vault 'asakeyabcfelaqpgsfnxcy;location=eastus'. For help resolving this issue, please see https://go.microsoft.com/fwlink/?linkid=2125287
Can anyone provide guidance on what this ID is and how to resolve this error? This is not my App Registration Object ID or App ID.
I think there're 2 points you're now concerning, one is you failed to add secret, another is the app id in the error message is not the one you registered.
I think you've followed the document to execute the cli command, so I just want to explain the second point. Pls allow me show you a code sample for a better explanation.
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
public async Task<IActionResult> PrivacyAsync()
{
var kvUri = "https://your_vault_name.vault.azure.net/";
var client = new SecretClient(new Uri(kvUri), new DefaultAzureCredential());
_ = await client.SetSecretAsync("test0311", "hello");
return View();
}
When we want to add key vault secret to azure, we need to provide a credential so that our operations are authenticated. This is the DefaultAzureCredential() here, and it has several sources to get the authentication like screenshot below.
That means if someone sets the environment variables for authentication, then it will cover the information you entered when executing cli command, this may usually cause the issue that the app is different from what you set. I think you may follow this document to check all your configurations and try again, or you can directly add environment variables with the app you registered on your computer.
By the way, pls don't forget to add access policy in azure portal for the azure ad app you registered.

keyVaultClient - get a secret

I am using Azure function to do an action, in this action I need to get a secret from a keyvault.
I am using this code in order to get the secret
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient((authority, resource, scope) => azureServiceTokenProvider.GetAccessTokenAsync(resource));
var secret= await keyVaultClient.GetSecretAsync($"https://{KeyVaultName}.vault.azure.net/", "SecretName");
When I run it locally it's work but when I run the function in azure I am getting an error "Forbidden"
How can I get the secret from a keyVault inside my azure function?
Thanks!
Forbidden might indicate that the identity assumed by the Azure Function does not have access rights over the specific Azure Key Vault.
From the Azure Portal or via CLI/API, head into the relevant Azure Key Vault resource -> Access Policies -> Add Access Policy -> Assign the Azure Function identity with the following permissions:
Secret List
Secret Get
Instead of using the KeyVault client inside your Function, you can have it much more simple if you can use KeyVault-referenced AppSettings (depends a bit of course on your scenario if thats an option). https://learn.microsoft.com/en-us/azure/app-service/app-service-key-vault-references
Then you can simple use the secret like any setting and read it as an Env variable.

Is the KeyVaultClient method GetSecret() safe?

I'm working with Azure Key Vault and I'm testing the "Managed Identities" for Azure Resource. Long story short: with this feature we can easily access to a KeyVault secrets from (e.g.) VM, App Service... I wrote this simple code:
private void GetKeyVaultSecrets()
{
AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
var akvCallback = new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback);
var keyVaultClient = new KeyVaultClient(akvCallback);
var secret = keyVaultClient.GetSecretAsync("https://mykeyvault.vault.azure.net/secrets/mySecret").GetAwaiter().GetResult();
string superSecret = secret.Value;
}
And I'm able to access my "Secret" without any kind of Authorization. Is it safe? Am I missing something? It looks so strange that with those 7 lines of code you have access to all your secrets. Thanks for your attention.
If you are running this on a service with a system-assigned Managed Identity, here's what actually happens (example for App Service, VM is slightly different):
Your app reads IDENTITY_ENDPOINT and IDENTITY_HEADER environment variables
HTTP call to IDENTITY_ENDPOINT using the IDENTITY_HEADER as authentication
This endpoint cannot be called from the outside, only from within the instance.
Its port is also random
In the call, your app specifies it wants a token for Key Vault (resource https://vault.azure.net)
The Managed Identity endpoint uses the certificate it has created to authenticate to Azure Active Directory with the Client Credentials flow
Azure AD verifies the request and issues a token
Managed Identity endpoint returns the token to your app
KeyVaultClient uses the token to authorize the call to Key Vault
On Virtual Machines, the Instance Metadata Service is used to get tokens.
The main thing to understand is that any process running on the instance itself is capable of getting tokens from the Managed Identity.
Though if you were to get malicious code running on your instance,
other approaches could be in trouble as well :)
If you run that code locally, it can work as well.
AzureServiceTokenProvider also attempts Visual Studio authentication as well as Azure CLI authentication.
So if you are logged in to e.g. AZ CLI, it is able to get an access token for your user to Azure Key Vault and access it.
Reference: https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity?context=azure%2Factive-directory%2Fmanaged-identities-azure-resources%2Fcontext%2Fmsi-context&tabs=dotnet#rest-protocol-examples
Example request done in VMs: https://learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/tutorial-windows-vm-access-nonaad#access-data

Use a certificate in the keyvault to access multi-tenant application in other tenant

We have a multi-tenant application in our Azure AD tenant. It is authorized in some other tenants (we know which ones). And it has multiple certificates registered to it to be used as client credentials.
We want to remove the certificates from the local stores and use a certificate in the key vault to request a token for one of the external tenant. According to the documentation this is one of the use cases.
Our tenant (id: xxxx):
Has app registration (app id: abcd-xxx-xxxx-xxx)
has keyvault
has managed service principal (with access to the key vault)
other tenant (id: yyyy):
Executed Admin consent for our application.
Question 1:
How do I create a certificate in the Key vault that is connected to an existing application (app id: abcd-xxx-xxxx-xxx)? It is important to note that since the application is already approved by several third party admins, it cannot be recreated. Same counts for creating a new certificate after it would be expired.
Question 2:
How to I setup the Microsoft.Azure.Services.AppAuthentication library to:
Use the managed identity to access the key vault in our tenant (xxxx).
Use the certificate in the key vault to request a token for our app (abcd-xxx-xxxx-xxx) in other companies tenant (yyyy)
Answer 1:
You could use az ad sp credential reset command like below. If you don't want to overwrite the existing certificate of the App, please pass the --append parameter.
az ad sp credential reset --name '<application-id>' --keyvault joykeyvault --cert cer136 --create-cert --append
Answer 2:
1.To use the MSI access the keyvault in your tenant, just use the code below.
No code changes are required, when you run your code on an Azure App Service or an Azure VM with a managed identity enabled, the library automatically uses the managed identity, see this link.
The environment variable AzureServicesAuthConnectionString has to be set to any credential with access to the keyvault. RunAs=Developer; DeveloperTool=AzureCli for dev or RunAs=App; for managed service identity (automatically in azure).
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Azure.KeyVault;
// Instantiate a new KeyVaultClient object, with an access token to Key Vault
var azureServiceTokenProvider1 = new AzureServiceTokenProvider();
var kv = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider1.KeyVaultTokenCallback));
2.If you want to use the service principal along with its certificate stored in the keyvault to get the token for the resources in another tenant, the connection string on the AzureServiceTokenProvider has to be set to RunAs=App;AppId={TestAppId};KeyVaultCertificateSecretIdentifier={KeyVaultCertificateSecretIdentifier} then you can get tokens for other tenants like.
const string appWithCertConnection = "RunAs=App;AppId={TestAppId};KeyVaultCertificateSecretIdentifier=https://myKeyVault.vault.azure.net/secrets/myCert";
Then use the code to get the token, e.g. for the resource https://management.azure.com/.
var azureServiceTokenProvider2 = new AzureServiceTokenProvider(appWithCertConnection);
string accessToken = await azureServiceTokenProvider2.GetAccessTokenAsync("https://management.azure.com/", "tenant-id-of-thridh-party-tenant").ConfigureAwait(false);

Read secret stored in Key Vault from an App Service running a container

I've created an App Service that is running a container running Identity Server. This container needs a certificate that I'm loading from Key Vault. To get the content of the certificate what I've done is:
Upload the certificate into Key Vault
Access the content accessing the secret endpoint of the Key Vault (https://mykeyvault.vault.azure.net/secrets/IdentityCert)
In my first attempt, I was storing just the URI of the secret in the App Settings and try to get the value using the following code:
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
var cert = keyVaultClient
.GetSecretAsync(
Env.GetString("CERTIFICATE_KEY_VAULT_KEY"))
.ConfigureAwait(false).GetAwaiter().GetResult();
identityServerBuilder.AddSigningCredential(new X509Certificate2(Convert.FromBase64String(cert.Value)));
This works if I deploy the code into a VM. But it doesn't if I deploy the code into an App Service running a container. So I decided to try another option which is to use the Key Vault reference thing. So, I've created a new App Settings like this:
CERTIFICATE_CONTENT = #Microsoft.KeyVault(SecretUri=https://mykeyvault.vault.azure.net/secrets/IdentityCert/5221036c6b734d5fa69cba29976a8592)
And then just use this value inside my code:
var certificateContent = Env.GetString("CERTIFICATE_CONTENT");
identityServerBuilder.AddSigningCredential(new X509Certificate2(Convert.FromBase64String(certificateContent)));
But this doesn't work either.
I've enabled the managed identity in the App Service and added it to the Access Policies in the Key Vault.
How can I get the value from Key Vault? Is there anything I'm missing?
So, the error was the way I was adding the new access policy. I was selecting the principal Id and the Authorized application. It turns out that I only need to select the principal, leaving the Authorized application as "None selected".

Resources