Are your application secrets being checked in the source control?
Let me introduce you to the way of safeguarding your application secrets that will make your application more secure, with no more secrets checked in the source control.
What will you learn in this article?
I have been trying to explore how to secure secrets of web application on Azure using Key Vault. I have been trying to do a small Proof of Concept to try "your secrets are safe with Key Vault in ASP.NET Core Web App" but was failing to implement it in a single shot. I searched various articles which were shown working as per the code but when I implemented them step by step, it never worked.
So finally, after lots of hit and trials, I was finally able to store my web application secrets in the Azure Key Vault.
I will try to cover each and every topic in this small proof of concept with all the small points to be demonstrated via GIF images so that it's easy to understand.
Problem Statement
We have been storing all our application related secrets in web.config in ASP.NET whether it has been Web Form or MVC. One of our projects which was handed over to us was hosted on Azure Cloud as Platform as a Service. We saw web.config had been checked in Source control. This is a bad practice to store web app secrets on source control. As we are moving more towards Continuous Integration and Continuous deployment DevOps, it is strongly always recommended to store the app secrets on Azure Key Vault or Application Settings configuration section of Web Apps in Azure.
While in ASP.NET Core, we should store all the application configuration in Environment variables if we are using Web app for containers. This is a standard that is being followed in Enterprise applications.
So, for local development, you can use appsettings.json while in production or QA environment, these configurations will be loaded from Environment variables or applicationSetting in the cloud.
Any new configuration added in appsetting.json should be added in the comment section of Pull Request which should just demonstrate the name of the new configuration and not the value and should be informed to the infrastructure team.
The same is the case with application secrets which we don't want to share with anyone. These kinds of application secrets should be stored in Azure Key vault for production and for local in secrets.json. secrets.json file isn't present in our project folder, it's stored in the OS user profile folder. So, there's no chance it'll get checked into source control.
Case Study
We have a web application that we want to deploy on Azure Platform as a service and store all application config to be stored in the Azure Key Vault. Just display the value of secrets on the web page to show that we are able to communicate with Key vault in our web application.
The requirement that will be covered in this blog post,
- Configure the web app to communicate with Key vault when deployed.
- How to use secrets.json for local dev environment.
- Communicate with Key vault in the local environment with Visual studio sign-in identity.
- Assign web app to access Azure Key Vault.
- Show the Configuration details in the table from Azure Key vault
Dependencies
- Azure subscription
- Azure Resources needed
- Web App
- App Service plan
- Azure Active Directory
- Key Vault
Breaking down the problem into small problems first is how we will be proceeding ahead,
- Setting up the local environment to use secrets.json and appsettings.json for configuration using ASP.NET core.
- Creating Azure Resources needed to for this Demo.
- Providing key vault access identity to the web app using power shell command and manual from the portal.
Setting up the local environment to use secrets.json and app settings for configuration using ASP.NET core web application
Create an ASP.NET Core project,
Select MVC Template,
Add some non-secret configuration in the appsettings.json file as shown below,
Adding an application secret in Secret Manager using Visual Studio,
Loading all the application secrets in the application
The default IConfiguration builder configures up to 5 configuration providers by default,
- Appsettings.json
- An environment-specific appsettings.ENVIRONMENT.json where ENVIRONMENT is the name of the current environment development, staging, etc.
- User Secrets manager
- Environment variables for Docker containers
In order to demonstrate the same let's run the application where we have configured our application secrets in appsettings.json and secrets.json.
Create a class named Configurations and use IConfiguration interface to load the secrets as shown below,
- using Microsoft.Extensions.Configuration;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
-
- namespace KeyVaultDemo.Models
- {
- public class Configurations
- {
- private readonly IConfiguration _configuration;
- public Configurations(IConfiguration configuration)
- {
- _configuration = configuration;
- }
-
- public string ApplicationName => _configuration["ApplicationName"];
- public string ApplicationSecret1 => _configuration["applicationSecret1"];
- public string ApplicationSecret2 => _configuration["applicationSecret2"];
-
- }
- }
Controller Code
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Threading.Tasks;
- using Microsoft.AspNetCore.Mvc;
- using KeyVaultDemo.Models;
- using Microsoft.Extensions.Configuration;
-
- namespace KeyVaultDemo.Controllers
- {
- public class HomeController : Controller
- {
- private readonly IConfiguration _configuration;
- public HomeController(IConfiguration configuration)
- {
- _configuration = configuration;
- }
-
- public IActionResult Index()
- {
- Configurations configurations = new Configurations(_configuration);
- return View(configurations);
- }
-
- public IActionResult About()
- {
- ViewData["Message"] = "Your application description page.";
-
- return View();
- }
-
- public IActionResult Contact()
- {
- ViewData["Message"] = "Your contact page.";
-
- return View();
- }
-
- public IActionResult Privacy()
- {
- return View();
- }
-
- [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
- public IActionResult Error()
- {
- return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
- }
- }
- }
UI Code
Let's run our application and check whether our application is showing the application secrets on the homepage or not.
We are able to successfully load the configuration from appsettings.json and secrets.json files in our web application. Let's now proceed ahead and start our pending task of loading user secrets from the Key Vault. In order to achieve the same, I will create the needed Azure resources.
To speeds things up, I have already created a resource group consisting of the web app which uses an instance of app service plan in another resource group and I already have Azure Active Directory that would be needed in future references.
Azure Active Directory
Let's now create a Key Vault resource in our resource group, as shown below.
Now we are done with resource creation. Let us create some secrets on Key vault manually from the portal.
Once the above steps are completed, let's try to configure and consume key vault on our ASP.NET Core application. Before moving ahead, install two NuGet dependencies we need to work with while working on the Key Vault.
- Microsoft.Azure.KeyVault Version:3.0.3
- Microsoft.Extensions.Configuration.AzureKeyVault Version:2.1.0
- Microsoft.Azure.Services.AppAuthentication Version: 1.0.3
Here is the code snippet to read configuration values from the Azure KeyVault.
- using Microsoft.AspNetCore;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.Azure.KeyVault;
- using Microsoft.Azure.Services.AppAuthentication;
- using Microsoft.Extensions.Configuration;
- using Microsoft.Extensions.Configuration.AzureKeyVault;
-
- namespace KeyVaultDemo
- {
- public class Program
- {
- public static void Main(string[] args)
- {
- CreateWebHostBuilder(args).Build().Run();
- }
-
- public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
- WebHost.CreateDefaultBuilder(args).ConfigureAppConfiguration((ctx, builder) =>
- {
-
-
- var keyVaultEndpoint = "https://appkeyvaultdemo.vault.azure.net/";
- if (!string.IsNullOrEmpty(keyVaultEndpoint))
- {
- var azureServiceTokenProvider = new AzureServiceTokenProvider();
- var keyVaultClient = new KeyVaultClient(
- new KeyVaultClient.AuthenticationCallback(
- azureServiceTokenProvider.KeyVaultTokenCallback));
- builder.AddAzureKeyVault(
- keyVaultEndpoint, keyVaultClient, new DefaultKeyVaultSecretManager());
- }
-
- }
- )
- .UseStartup<Startup>();
- }
- }
This code will add a provider to read configuration from the Azure Key Vault.
Communicate with Key vault in the local environment with Visual studio sign-in identity
Visual studio sign-in identity allows you to connect with Azure Services on the local environment; this is done with the help of dependency that we have installed priorly Microsoft.Azure.Services.AppAuthentication.
First sign in on Visual Studio to your Azure Account.
Once you are done with the Sign-in, all your Azure Subscription Services will come under Server Explorer, as shown below.
Now, let's run the application by setting the Environment as Production to test whether we are able to connect to the Azure services locally.
Congratulations, we are successfully able to authenticate to Azure services using the developer's Azure Active Directory/ Microsoft account during development.
Now let's try to complete our last and most important task of deploying the app on Azure and set read policy for Azure Key vault from our web application.
Using PowerShell command,
az webapp identity assign --name "usingSailleshPawarkeyVault" --resource-group "sailleshpawarRG"
This will enable the web app's managed service identity to On.
Response
Allowing Key Vault Access policy to the web app:
az keyvault set-policy --name usingSailleshPawarkeyVault--object-id PrincipalId --secret-permissions get
The response will be the set of permissions and modes of Azure Key Vault.
Using the Azure portal, allow Key vault access to the web app using the portal.
... Go to Key vault resource and click on Access policies,
Then add a new Access policy and search for your web app name,
Search for web app name i.e. usingSailleshPawarkeyVault,
Note
Make sure you do not enter any application name in Authorized application attribute...
... if you do the application will not be able to read secrets and will fail and throw Process Id failure exception.
Give permission to the application for Key vault and then click on the OK button.
Now, enable the managed identity for the web application as shown below:
Go to your app service and click on Identity and enable system assign,
Once the above steps are done now let's publish our application on Azure portal by following the below steps,
Publish the web application using the downloaded publish profile
Finally, you can see we are able to connect to Azure key vault and secure our app secrets from anyone. The Key vault secret will be available to the users or applications that have access, otherwise no one else can access them.
Congratulations, we have finally successfully completed the whole requirement. Happy Learning!!
References