22
votes

I have a docker image containing an ASP.NET Core app that uses Azure Key vault to access things like connection strings. When I run the image locally, I get this error:

Unhandled Exception: Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProviderException: Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/[guid]. Exception Message: Tried the following 3 methods to get an access token, but none of them worked.
Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/[guid]. Exception Message: Tried to get token using Managed Service Identity. Unable to connect to the Managed Service Identity (MSI) endpoint. Please check that you are running on an Azure resource that has MSI setup.
Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/[guid]. Exception Message: Tried to get token using Visual Studio. Access token could not be acquired. Environment variable LOCALAPPDATA not set.
Parameters: Connection String: [No connection string specified], Resource: https://vault.azure.net, Authority: https://login.windows.net/[guid]. Exception Message: Tried to get token using Azure CLI. Access token could not be acquired. /bin/bash: az: No such file or directory

From what I understand, it first tries to get the access token as a managed service identity. As it's not running in the Azure cloud, it can't do this and tries to get it through visual studio connected service. As this won't be on the docker image, it tries using the Azure CLI, but this isn't installed on the docker image.

So I need to install the Azure CLI into the docker image. How is this done, given that the base image of the Dockerfile is FROM microsoft/dotnet:2.1-aspnetcore-runtime?

Is this base image an Alpine OS image, so do I need to look at installing Azure CLI with Alpine?

Assuming I have Azure CLI installed, is there a way to access Key vault without storing any credentials in Dockerfile source code or passing them to the container through plain text?

More generally, what is the best approach here.

6
why dont you use azure sdk?4c74356b41
Could you elaborate on that?zola25
well, like you normally would, without msi endpoint4c74356b41
Did you find a solution to this? I rather not create a SP like @fox918 suggest because of shared accounts and security reasons. What I currently do is get the token from the cli, put that in an environment variable and use that token in code. That way each dev still uses hid own identity but it's a bit manual workE. Staal
If you're just coming here from google, this, is what I ended up doing to to solve the issue, it's an automated version of @E.Staal's answer; and it will work on your co-workers machines without them having to muck around with environment variables.BrainSlugs83

6 Answers

3
votes

Solution (not for production use)

A possible Solution to your Problem is to generate a Service Principal (SP) and grant this Service Principal access to the key vault (via RBAC or IAM). Microsoft Documentation on creating a SP

Using the credentials of the SP as client-id and client-secret (Random example) you can then log into the vault and retrieve the secrets.

Concerns

  • with this approach, you will introduce secrets into the code (propably the exact reason why you use the key vault). I suppose the local docker image is for development use only. Therefore I would recommend creating a Keyvault just for development (and access it using SP) while using a separate Kevault for Production where one of the established, secret-less authentication schemes is used.
  • You must make sure that the key vault allows access from outside the azure cloud (see the access policies on portal.azure.com)
3
votes

My current solution is to use an environment variable with the access token.

Get the key and store in environment variable (after you did an az login and set the correct subscription):

$Env:ACCESS_TOKEN=(az account get-access-token  --resource=https://vault.azure.net | ConvertFrom-Json).accessToken

The we add that environment variable in Visual Studio: enter image description here

Change the code to:

                config.AddEnvironmentVariables();

                KeyVaultClient keyVaultClient;
                var accessToken = Environment.GetEnvironmentVariable("ACCESS_TOKEN");

                if (accessToken != null)
                {
                    keyVaultClient = new KeyVaultClient(
                        async (string a, string r, string s) => accessToken);
                }
                else
                {
                    var azureServiceTokenProvider = new AzureServiceTokenProvider();
                    keyVaultClient = new KeyVaultClient(
                       new KeyVaultClient.AuthenticationCallback(
                           azureServiceTokenProvider.KeyVaultTokenCallback));
                }

                config.AddAzureKeyVault(
                    $"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
                    keyVaultClient,
                    new DefaultKeyVaultSecretManager());
0
votes

Although there's some time since you make this question, another option, suitable for production environments, would be using an x509 certificate.

Microsoft has this article explaining how to do this. You can use self-signed certificates or any other valid SSL certificate. That depends on your needs.

-1
votes

In an attempt to simplify and automate E. Staal's answer, I came up with this:

  1. Update your .gitignore file, by adding the following line to the bottom of it:

    appsettings.local.json
    
  2. Right click on the project in Solution Explorer, and click on Properties; in the Build Events tab, find the Pre-build event command line text box and add the following code:

    cd /d "$(ProjectDir)"
    if exist "appsettings.local.json" del "appsettings.local.json"
    if "$(ConfigurationName)" == "Debug" (
    az account get-access-token  --resource=https://vault.azure.net > appsettings.local.json
    )
    
  3. In your launchSettings.json (or using the Visual Editor under project settings) configure the following values:

    {
      "profiles": {
        // ...
        "Docker": {
          "commandName": "Docker",
          "environmentVariables": {
            "DOTNET_ENVIRONMENT": "Development",
            "AZURE_TENANT_ID": "<YOUR-AZURE-TENANT-ID-HERE>"
          }
        }
      }
    }
    
  4. In your Program.cs file find the CreateHostBuilder method and update the ConfigureAppConfiguration block accordingly -- here is mine as an example:

    Host.CreateDefaultBuilder(args).ConfigureAppConfiguration
    (
        (ctx, cfg) =>
        {
            if (ctx.HostingEnvironment.IsDevelopment())
            {
                cfg.AddJsonFile("appsettings.local.json", true);
            }
    
            var builtConfig = cfg.Build();
            var keyVault = builtConfig["KeyVault"];
            if (!string.IsNullOrWhiteSpace(keyVault))
            {
                var accessToken = builtConfig["accessToken"];
                cfg.AddAzureKeyVault
                (
                    $"https://{keyVault}.vault.azure.net/",
                    new KeyVaultClient
                    (
                        string.IsNullOrWhiteSpace(accessToken)
                        ? new KeyVaultClient.AuthenticationCallback
                        (
                            new AzureServiceTokenProvider().KeyVaultTokenCallback
                        )
                        : (x, y, z) => Task.FromResult(accessToken)
                    ),
                    new DefaultKeyVaultSecretManager()
                );
            }
        }
    )
    

If this still doesn't work, verify that az login has been performed and that az account get-access-token --resource=https://vault.azure.net works correctly for you.

-2
votes

This is because your docker container is running as root user and the user registered in key vault is some other user (yourusername@yourcmpany.com)

-2
votes

Try the methods in this link to install: https://github.com/Azure/azure-cli/issues/8863

Once installed:

  1. Login/Authenticate: az login --service-principal --username "ServicePrincipalName-or-ID" --password "ServicePrincipalPassword" --tenant "Azure_Tenant"
  2. Then fetch az keyvault secret show --name "SecretName" --vault-name "KeyVaultName" --query value

See here for Quickstart, here for a better flow and here once you're comfortable with the process for more advanced usage