28

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.

zola25
  • 1,774
  • 6
  • 24
  • 44
  • why dont you use azure sdk? – 4c74356b41 Mar 19 '19 at 19:41
  • 2
    Could you elaborate on that? – zola25 Mar 19 '19 at 23:05
  • well, like you normally would, without msi endpoint – 4c74356b41 Mar 20 '19 at 05:30
  • 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 work – E. Staal Apr 29 '20 at 09:20
  • 1
    If you're just coming here from google, [this](https://stackoverflow.com/a/67526309/398630), 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 May 13 '21 at 21:16

5 Answers5

4

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());
E. Staal
  • 525
  • 5
  • 18
  • Hi, could you elaborate more on getting the access token? I'm an azure novice so I'm not sure where or how to enter the "$Env" command. – ChasetopherB May 04 '21 at 17:07
  • You can fetch that by running (az account get-access-token --resource=https://vault.azure.net | ConvertFrom-Json).accessToken in a command prompt. That uses the Azure CLI (az) so you need to be logged in first using az login – E. Staal May 04 '21 at 17:13
  • I got that command working, I think. I don't actually see ACCESS_TOKEN in the environment variables list on my local machine. Does the command save the environment variable somewhere else? – ChasetopherB May 04 '21 at 19:09
  • If you use the command with '$Env:ACCESS_TOKEN=' before it, that will store it in an environment variable. However, when you set a value in the Visual Studio Debug window like I did in the image above, then Visual Studio will make it available for you while debugging your app. – E. Staal May 04 '21 at 20:13
  • I did use '$Env:ACCESS_TOKEN=' beforehand, and I still don't see it. But it sounds like I just need to get ahold of the access token and place it in my VS project settings under debug, correct? It doesn't HAVE to be assigned to a local environment variable? – ChasetopherB May 04 '21 at 20:18
  • If I run this command: Write-Output (az account get-access-token --resource=vault.azure.net | ConvertFrom-Json).accessToken I get this response: ERROR: AADSTS500011: The resource principal named vault.azure.net was not found in the tenant named xxxxxxxxxx. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant. I am logged in correctly. – ChasetopherB May 04 '21 at 20:33
  • I figured it out. I needed to include "https://" in "vault.azure.net". Thank you so much for your assistance! – ChasetopherB May 06 '21 at 17:06
  • I posted [a simplified version of this below](https://stackoverflow.com/a/67526309/398630), it's self-contained and automated as well, so that each time you perform a debug build, it just shoves the access token right in. – BrainSlugs83 May 13 '21 at 21:13
  • KeyVaultClient has been replaced with Azure.Security.KeyVault.Secrets, Azure.Security.KeyVault.Keys and Azure.Security.KeyVault.Certificiates. See https://www.domstamand.com/migrating-to-the-new-csharp-azure-keyvault-sdk-libraries/ and https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/keyvault/Azure.Security.KeyVault.Secrets I will attempt to update the answer if I can fix my issues. – Jeremiah Adams May 19 '21 at 19:48
  • 1
    When using docker don't forget mapping the variable in the docker configuration ACCESS_TOKEN=${ACCESS_TOKEN} – Javi Jul 18 '21 at 10:59
3

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)
fox918
  • 179
  • 3
  • 9
  • Could leveraging the environment variables (see the other answer by @E. Staal) make this solution more "productionable"? – usr-local-ΕΨΗΕΛΩΝ Nov 24 '20 at 09:50
  • What I ended up doing was passing my SP client ID and secret via Environment variables and then authenticating KeyVault using ServicePrincipalCredentials object. This way no secrets are exposed in the code. – Aman Gill Dec 31 '20 at 07:21
1

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.

BrainSlugs83
  • 6,214
  • 7
  • 50
  • 56
  • This is a perfectly good answer. I use an SPN to run my containers in Azure and it's just as easy to do this locally. In my launchSettings.json I have the following: { "profiles": { "Docker": { "commandName": "Docker", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "", "AZURE_CLIENT_SECRET": "", "AZURE_CLIENT_ID": "", "AZURE_TENANT_ID": "" } } } } – Keith Hodo May 30 '22 at 00:28
0

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.

SantiFdezMunoz
  • 501
  • 1
  • 6
  • 18
-3

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)

Abhi
  • 5,501
  • 17
  • 78
  • 133
  • 2
    How would they ever be the same user? One is a Microsoft account, the other is a Linux user – zola25 May 17 '19 at 15:13