Secret Management in ASP.NET 5.0 Web APIs
Secret management is essential for every application that have secret values such as encryption passwords, BasicAuth credentials, DB connection strings, and so forth. Focusing on secret management in APIs in this note, we will cover how to secret values in your APIs, how to configure them, and finally how to read them.
By the way, this topic belongs to the series to set up an Asp.NET API for production use.
- API Route Versioning
- Configuration Management
- Secret Management
- Request Validation
- Global Exception Handling
- URL Rewriting
- Deploy .NET API to Azure App Service
Configurations vs Secrets
Secrets are different from configurations. Secrets are values that should be protected and never be stored as plain text sitting in your repositories along with the rest of your code. You must have different values for each secret per environment, otherwise you are risking the safety and security of your systems. Configurations on the other hand can be stored in plain text in configuration files and loaded based on the environment. They can also be injected through environment variables. Refer to Asp.Net 5.0 Web API Configuration Management
Accessing Secrets within Your Web ASP.Net 5.0 API
Starting from the end, you would like to have a convenient way to access your secrets from your code. Ideally you should have intellisense when accessing your secrets (and not using string literals as keys), and hence you will need to define a class that would be filled with all your secrets when your methods are run.
As you can see, this class will give you a nice structure on how to access your API secrets. Next, you will need to define your secret key/values somewhere, and then at start up time, fill this class with all those values, and access it within your classes.
Hence looking at how you use this class, ideally you’d like to inject an instance of this class anywhere you would need to access your secrets. We will be using IConfigurations as an injectable carrier of secrets.
Now anywhere in your EmailSender class, you can access a well defined secret structure.
Storing Secrets Locally and in Azure KeyVault
1. Development Machine
Following .Net 5.0 practices, developing on your local machine, you can access the secrets of each solution by right clicking on your solution file,
This would lead you to a json file containing all the secrets you can have on your development machine, which also matches the structure of your AppSecret class.
However in production, you should be storing your secrets in a safe storage such as Azure KeyVault.
Using Azure KeyVault is pretty easy, however there are some points which could cause you waste a few hours there. First, talking about best practices, ideally you should have a KeyVault per microservice. This will give the isolation you need when dealing with your resources, and your microservices won’t necessarily access secrets beyond their own remit.
Once you created your KeyVault, go to the secrets section and generate/import your secrets there.
Define Secret Keys in a Way to Match Your AppSecret Structure
Because we want to load all our secrets in an instance of AppSecrets class, make sure you follow the naming convention you see in the below photo. Every json parent element needs to have — to indicate a . in json structure. Hence AppSecrets.AuthSecrets.Secret needs to have a key as AppSecrets — AuthSecrets — Secret in the KeyVault.
Loading Secrets Locally/From KeyVault into your Asp.Net 5.0 Web API
The last step, which is pretty much the most important one, is to connect/authenticate your API to the KeyVault in production, so you can read the secret at run time.
Loading the Secret in your Local Dev Machine
As far as you are concerned from the local dev point of view, at this stage, you already are about to load the local secrets, inject IConfiguration in any class, and access to those secrets like I explained above. From this point onward, we are just talking about loading and accessing your secrets in production through your KeyVault.
The steps we need to follow to access your secrets in Azure KeyVault in production are as follows:
- 1. Register your API in Azure Active Directory
- 2. Associate a certificate to your app registration
- 3. Set the right access policies in the KeyVault to allow access to any API that represents the app registration in step 2.
- 4. Associate the same certificate with your API, so you prove your API matches the same app registration.
- 5. Configure Your API so it can access TLS/SSL certificates.
- 6. Loading Secrets at API start up time .
1. Register Your API in Azure Active Directory
This process is pretty straightforward, you can go to Azure Active Directory > App Registration > New Registration.
2. Associate a Certificate to your App Registration
Once registered, click on your registered app > Certificates and Secrets, and upload a certificate there.
3. Set KeyVault Access Policies
Go to your KeyVault > Access Policies > +Add Access Policies, and then find your app registration from step 1, and give it required permissions.
This basically means if an API was associated with that app registration, it can access the resources (secrets, certificates, etc.) from KeyVault here.
4. Associate the same certificate with your API
Go to your API > TLS/SSL settings > Private Key Certificates (.pfx), and upload the same certificate so that your API is associated with your previous app registration and can access resources in your key vault.
Configuration, plumbing and troubleshooting your software foundation take a considerable amount of time in your product development. Consider using Pellerex which is a complete foundation for your enterprise software products, providing source-included Identity and Payment functions across UI (React), API (.NET), Pipeline (Azure DevOps) and Infrastructure (Kubernetes).
5. Configure Your ASP.Net 5.0 API to Access TLS/SSL Certificates
You now need to set some settings on your Azure API, so that your API can access the certificate we just uploaded. Go to your API in Azure > Configurations and set WEBSITE_LOAD_CERTIFICATES and WEBSITE_LOAD_USER_PROFILE with appropriate values. Without these, you will face errors reading your certificates and access your KeyVault. Again I’d recommend to automate this step as part of your pipeline, so every time you deploy to a new API, these two settings will be created as well.
6. Loading secrets at API start up time
Now what’s left is to load the secret values from your secret store (either on local machine, or a KeyVault) at startup time, write them to an instance of AppSecrets class, and inject it within our dependency injection space. In our program.cs file, within CreateHostBuilder method:
This code is self-explanatory, it basically says, if I was in Production, read the certificate from API TLS/SSL certificates, and use it to login the Azure KeyVault using those variables such as Vault Url, etc.
And your are good to go.
How are you building your current software today? Build everything from scratch or use a foundation to save on development time, budget and resources? For an enterprise software MVP, which might take 8–12 months with a small team, you might indeed spend 6 months on your foundation. Things like Identity, Payment, Infrastructure, DevOps, etc. they all take time, while contributing not much to your actual product. These features are needed, but they are not your differentiators.
Pellerex does just that. It provides a foundation that save you a lot development time and effort at a fraction of the cost. It gives you source-included Identity, Payment, Infrastructure, and DevOps to build Web, Api and Mobile apps all-integrated and ready-to-go on day 1.
Check out Pellerex and talk to our team today to start building your next enterprise software fast.