Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Not able to connect to Azure Key Vault when using Service Identity

I am trying to retrieve secrets from Azure Key Vault using Service Identity in an ASPNet 4.6.2 web application. I am using the code as outlined in this article. Locally, things are working fine, though this is because it is using my identity. When I deploy the application to Azure I get an exception when keyVaultClient.GetSecretAsync(keyUrl) is called.

As best as I can tell everything is configured correctly. I created a User assigned identity so it could be reused and made sure that identity had get access to secrets and keys in the KeyVault policy.

The exception is an AzureServiceTokenProviderException. It is verbose and outlines how it tried four methods to authenticate. The information I'm concerned about is when it tries to use Managed Service Identity:

Tried to get token using Managed Service Identity. Access token could not be acquired. MSI ResponseCode: BadRequest, Response:

I checked application insights and saw that it tried to make the following connection with a 400 result error:

http://127.0.0.1:41340/MSI/token/?resource=https://vault.azure.net&api-version=2017-09-01

There are two things interesting about this:

  1. Why is it trying to connect to a localhost address? This seems wrong.
  2. Could this be getting a 400 back because the resource parameter isn't escaped?
  3. In the MsiAccessTokenProvider source, it only uses that form of an address when the environment variables MSI_ENDPOINT and MSI_SECRET are set. They are not set in application settings, but I can see them in the debug console when I output environment variables.

At this point I don't know what to do. The examples online all make it seem like magic, but if I'm right about the source of the problem then there's some obscure automated setting that needs fixing.

For completeness here is all of my relevant code:

public class ServiceIdentityKeyVaultUtil : IDisposable
{
    private readonly AzureServiceTokenProvider azureServiceTokenProvider;
    private readonly Uri baseSecretsUri;
    private readonly KeyVaultClient keyVaultClient;


    public ServiceIdentityKeyVaultUtil(string baseKeyVaultUrl)
    {
        baseSecretsUri = new Uri(new Uri(baseKeyVaultUrl, UriKind.Absolute), "secrets/");
        azureServiceTokenProvider = new AzureServiceTokenProvider();
        keyVaultClient = new KeyVaultClient(
            new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
    }

    public async Task<string> GetSecretAsync(string key, CancellationToken cancellationToken = new CancellationToken())
    {
        var keyUrl = new Uri(baseSecretsUri, key).ToString();
        try
        {
            var secret = await keyVaultClient.GetSecretAsync(keyUrl, cancellationToken);
            return secret.Value;
        }
        catch (Exception ex)
        {
            /** rethrows error with extra details */
        }
    }

    /** IDisposable support */
}

UPDATE #2 (I erased update #1)

I created a completely new app or a new service instance and was able to recreate the error. However, in all instances I was using a User Assigned Identity. If I remove that and use a System Assigned Identity then it works just fine.

I don't know why these would be any different. Anybody have an insight as I would prefer the user assigned one.

like image 544
Daniel Gimenez Avatar asked Jan 25 '19 16:01

Daniel Gimenez


People also ask

How does Azure key vault authenticate the identity of the user or app?

Authentication with Key Vault works in conjunction with Azure Active Directory (Azure AD), which is responsible for authenticating the identity of any given security principal. A security principal is an object that represents a user, group, service, or application that's requesting access to Azure resources.


2 Answers

One of the key differences of a user assigned identity is that you can assign it to multiple services. It exists as a separate asset in azure whereas a system identity is bound to the life cycle of the service to which it is paired.

From the docs:

A system-assigned managed identity is enabled directly on an Azure service instance. When the identity is enabled, Azure creates an identity for the instance in the Azure AD tenant that's trusted by the subscription of the instance. After the identity is created, the credentials are provisioned onto the instance. The lifecycle of a system-assigned identity is directly tied to the Azure service instance that it's enabled on. If the instance is deleted, Azure automatically cleans up the credentials and the identity in Azure AD.

A user-assigned managed identity is created as a standalone Azure resource. Through a create process, Azure creates an identity in the Azure AD tenant that's trusted by the subscription in use. After the identity is created, the identity can be assigned to one or more Azure service instances. The lifecycle of a user-assigned identity is managed separately from the lifecycle of the Azure service instances to which it's assigned.

User assigned identities are still in preview for App Services. See the documentation here. It may still be in private preview (i.e. Microsoft has to explicitly enable it on your subscription), it may not be available in the region you have selected, or it could be a defect.

enter image description here

like image 96
Murray Foxcroft Avatar answered Oct 23 '22 13:10

Murray Foxcroft


To use a user-assigned identity, the HTTP call to get a token must include the identity's id. Otherwise it will attempt to use a system-assigned identity.

Why is it trying to connect to a localhost address? This seems wrong.

Because the MSI endpoint is local to App Service, only accessible from within the instance.

Could this be getting a 400 back because the resource parameter isn't escaped?

Yes, but I don't think that was the reason here.

In the MsiAccessTokenProvider source, it only uses that form of an address when the environment variables MSI_ENDPOINT and MSI_SECRET are set. They are not set in application settings, but I can see them in the debug console when I output environment variables.

These are added by App Service invisibly, not added to app settings.

As for how to use the user-assigned identity, I couldn't see a way to do that with the AppAuthentication library. You could make the HTTP call manually in Azure: https://learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http. Then you gotta take care of caching yourself though! Managed identity endpoints can't handle a lot of queries at one time :)

like image 22
juunas Avatar answered Oct 23 '22 14:10

juunas