In this post, I share three ways of gaining a Windows virtual machine access to a key vault. The machine can be an azure virtual machine or a non-azure machine such as your personal computer or a on premise server.
This approach only works if the VM is an azure VM. Essentially, this approach uses the identity of an azure resource which needs access to the azure key vault. Examples of such resource are azure virtual machines, app services, and azure functions. Developers neither have to store any credentials in the codes nor maintain the identity. Azure manages the identity for us as well as abstract away much of the complexity. It only takes a few clicks to turn on identity for a resource. Once the identity setup is in place, the codes are also fairly straightforward.
There are two types of managed identity: system assigned identity and user assigned identity. The main difference between the two types is that a system assigned identity can only associate with the resource on which it is enabled, whereas a user assigned identity can associate with multiple resources.
For more details on setting up a managed identity using azure portal, checkout this documentation. For an example of using managed identity in an ASP.NET core application, checkout this documentation.
Azure managed identity is the recommended approach for authenticating an azure resource against AAD. The setup is also simple and straightforward. I got it working in just a few steps and few lines of codes. Below are the summary of the steps:
var azureServiceTokenProvider = new AzureServiceTokenProvider(); var keyVaultClient = new KeyVaultClient( new KeyVaultClient.AuthenticationCallback( azureServiceTokenProvider.KeyVaultTokenCallback)); builder.AddAzureKeyVault(keyVaultURL, keyVaultClient, new DefaultKeyVaultSecretManager());
Pay attention to the version of the Microsoft.Azure.Services.AppAuthentication library. It costed me several hours of sleep trying to debug the error with connecting to the Managed Service Endpoint (MSI) because of using an out of date version of the library. For more information, checkout this thread.
Azure managed identity is great for authenticating an azure resources. However, it’s not applicable for non-azure resources. For instance, you cannot use managed identity to authenticate your on premise VMs. For non azure resources, the alternative is to authenticate using X.509 certificates.
This approach authenticates a resource using a X.509 certificate which consists of a public/private key pair. You upload the public key (the certificate’s thumbprint) to azure active directory and keep the private portion accessible to the app. The certificate needs to contain the private key. A self-signed certificate would be sufficient as the only purpose is to identify the server when authenticating against azure active directory. Below I show a sample Powershell script to generate a self-signed .PFX certificate file and install to the Personal certificate store.
$date_now = Get-Date
# Set the number of years before the certificate expires. $extended_date = $date_now.AddYears(20)
# DNS name of the host/machine. Ex: MyTestServer $DnsName = "My test server"
# owner of the certificate. $Subject = "CN=myapp,CN=mycompany,DC=com"
# friendly name of the certificate $FriendlyName = "KeyVaultAccess" $cert = New-SelfSignedCertificate -CertStoreLocation Cert:\LocalMachine\my -DnsName $DnsName -NotAfter $extended_date -Subject $Subject -FriendlyName $FriendlyName
This sample project from Microsoft shows codes that retrieve the certificate at the root of the source code, but for security, you can and should keep the certificate in the certificate store and let the OS manages the certificate. . You can share a certificate among multiple servers, or use a separate certificate for each server you want to grant access. I find using a different certificate for each server provides better security because I don’t need to export the certificate out of the store, which means I don’t have to set the password to protect the certificate.
If you use different certificates for multiple servers, be sure those certificates have a common value such as same distinguished subject or issuer from which you can base the search.
Below I give a summary of the steps to access azure key vault using certificates:
// key vault access using certificate (private/public key) using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine)) { store.Open(OpenFlags.ReadOnly); var certs = store.Certificates; var distinguishedName = new X500DistinguishedName(configuration["KeyVault:SubjectDistinguishedName"]); var certFound = certs.Find(X509FindType.FindBySubjectDistinguishedName, distinguishedName.Name, false) .OfType<X509Certificate2>(); logger.LogDebug("Certificate found: " + certFound.Count()); if (certFound.Count() > 0) { builder.AddAzureKeyVault( keyVaultURL, appId, certFound.Single()); store.Close(); } else { logger.LogWarning("Unable to find the certificate for authenticating to key vault."); } }
This approach is less secure than using certificate or azure managed identity. For this approach, you just register the application in azure active directory and generate a secret. You should only consider this option for testing, or if you absolutely can’t use certificate and/or managed identity. Even then, you should store the secret outside of the source code, such as in the environment variables. See my post on secure app settings.
What is managed identities for Azure resources?
Service-to-service authentication to Azure Key Vault using .NET
Adding a system-assigned identity
Azure Key Vault Configuration Provider
Configure managed identities for Azure resources on a VM using the Azure portal
Web scraping in C# using HtmlAgilityPack
Building multitenant application – Part 2: Storing value into database session context from ASP.NET core web API
Common frameworks, libraries and design patterns I use
Build and deploy a WebJob alongside web app using azure pipelines
Authenticate against azure ad using certificate in a client credentials flow
Notes on The Clean Architecture
Migrating from Microsoft.AspNetCore.Authentication.AzureAD to Microsoft Identity Web authentication library to integrate with Azure AD.
Three essential libraries for unit testing a .NET project