No XML encryptor configured - When using Key Vault
Asked Answered
T

2

6

I have an netcoreapp2.2 containerized application that uses azure key vault to store keys and also uses:

app.UseAuthentication(); 

And

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)

I am building/running a docker image in a hosted linux environment under App Services. I am using the azure container registry and dev ops pipe line to maintain my app. Azure controls the deployment process and the "docker run" command.

My app works great, however in the container logs I see:

2019-12-13T17:18:12.207394900Z [40m[1m[33mwarn[39m[22m[49m: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
2019-12-13T17:18:12.207436700Z       No XML encryptor configured. Key {...} may be persisted to storage in unencrypted form.
...
2019-12-13T17:18:14.540484659Z Application started. Press Ctrl+C to shut down.

I realize there are many other posts on this that allude to using other storage mechanisms, however I am using key vault to store my sensitive data. JWT is all handled by key vault. I have a few application settings that control static variables for DEV/QA/PROD but they are not sensitive data at all.

I am also not sure what key is being stored in memory as all my sensitive keys are completely outside of the application and are called by:

var azureServiceTokenProvider = new AzureServiceTokenProvider();
                var keyVaultClient = new KeyVaultClient(
                    new KeyVaultClient.AuthenticationCallback(
                        azureServiceTokenProvider.KeyVaultTokenCallback));

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

I am having a difficult time understanding why this warning is being thrown and also if I should take additional steps to mitigate the issue. I have not personally seen side effects, and app restarts do not seem to have any effect as I am using bearer tokens and other concerns such as token expiration, password resets and the like are not applicable.

So I am left with asking are there any additional steps I can take to avoid this warning? Do I need to ensure that there is a better data at rest mechanism for any configuration settings that may be in my linux environment? Can I safely ignore this warning?

Torsk answered 14/12, 2019 at 1:38 Comment(0)
T
5

It took me a while to find a way that suited the needs that I have for my application but I wanted to lend some clarity to a number of other stack answers that just did not make sense to me and how I finally understood the problem.

TLDR; Since I was already using key vault, I was confusing how .net core works. I didn't realize that config.AddAzureKeyVault() has nothing to do with how .net core decides to store data at rest on your app service.

When you see this warning:

No XML encryptor configured. Key {GUID} may be persisted to storage in unencrypted form.

it really doesn't matter what GUID was being set: that string of data was not being stored encrypted at rest.

For my risk analysis any information that is not being encrypted at rest is a bad idea as it could mean at anytime in the future some sort of sensitive data could leak and then be exposed to an attacker. In the end, I chose to classify my data at rest as sensitive and err on the side of caution with a potential attack surface.

I have been struggling to try and explain this in a clear and concise way and it is difficult to sum up in a few words. This is what I learned.

  • Access control (IAM) is your friend in this situation as you can declare a system assigned identity for your application and use role based accessed control. In my case I used my application identity to control access to both key vault and azure storage with RBAC. This makes it much easier to get access without SAS tokens or access keys.
  • Azure storage will be the final destination for the file you are creating, but it will be the vault that controls the encryption key. I created an RSA key in key vault, and that key is what encrypts the XML file that is throwing the original error.
  • One of the mistakes I was making in my head was that I wanted two write the encrypted XML to key vault. However, that is not really the use case Microsoft describes. There are two Mechanisms: PersistKeysTo and ProtectKeysWith. As soon as I got that through my thick head, it all made sense.

I used the following to remove the warning and create encrypted data at rest:

services.AddDataProtection()
    // Create a CloudBlockBlob with AzureServiceTokenProvider
    .PersistKeysToAzureBlobStorage(...)
    // Create a KeyVaultClient with AzureServiceTokenProvider
    // And point to the RSA key by id
    .ProtectKeysWithAzureKeyVault(...);

I had already used RBAC for my application with key vault (with wrap/unwrap permissions), but I also added Storage Blob Data Contributor to the storage account.

How you create your blob is up to you, but one gotcha is creating the access token synchronously:

    // GetStorageAccessToken()
    var token = new AzureServiceTokenProvider();
    return token.GetAccessTokenAsync("https://storage.azure.com/")
        .GetAwaiter()
        .GetResult();

Then I called it from a method:

    var uri = new Uri($"https://{storageAccount}.blob.core.windows.net/{containerName}/{blobName}");
    //Credentials.
    var tokenCredential = new TokenCredential(GetStorageAccessToken());
    var storageCredentials = new StorageCredentials(tokenCredential);
    return new CloudBlockBlob(uri, storageCredentials);

After this hurdle was overcame, putting the encryption in was straight forward. The Keyvault ID is the location of the encryption key you are using.

https://mykeyvaultname.vault.azure.net/keys/my-key-name/{VersionGuid}

And creating the client is

var token = new AzureServiceTokenProvider();
var client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(token.KeyVaultTokenCallback));

services.AddDataProtection()
    .ProtectKeysWithAzureKeyVault(client, keyVaultId);

I also have to give credit to this blog: https://joonasw.net/view/using-azure-key-vault-and-azure-storage-for-asp-net-core-data-protection-keys as this pointed me in the right direction.

https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/default-settings?view=aspnetcore-2.2 this also pointed out why keys are not encrypted

https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles - RBAC for apps

https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?view=aspnetcore-3.1 this was confusing at first but has a good warning about how to grant access and limit access in production.

Torsk answered 17/12, 2019 at 15:11 Comment(1)
I recently blogged about how you can store the Data Protection key ring in Azure Key Vayult here edument.se/en/blog/post/…Porkpie
C
1

Might be you have to configure your data protection policy to use CryptographicAlogrithms as follow:

 .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration()
                {
                    EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
                    ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
                });

Also, following are few warning which you get around Data protection policy

ASP.Net core DataProtection stores keys in the HOME directory (/root/.aspnet/DataProtection-Keys) so when container restart keys are lost and this might crash the service.

This can be resolve by persisting key at

  • Persist key at the persistent location (volume) and mount that volume to docker container
  • Persist key at the external key store like Azure or Redis

More details about ASP.NET DataProtection:

https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview?view=aspnetcore-3.1

https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/introduction?view=aspnetcore-3.1

To mount an external volume (C:/temp-kyes) to docker container volume (/root/.aspnet/DataProtection-Keys) using following command

docker run -d -v /c/temp-keys:/root/.aspnet/DataProtection-Keys container-name

Also, You need to update your Starup.cs - ConfigureServices to configure DataProtection policy

services.AddDataProtection().PersistKeysToFileSystem(new DirectoryInfo(@"C:\temp-keys\"))
                .UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration()
                {
                    EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
                    ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
                });
Chadbourne answered 19/6, 2020 at 2:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.