Using HashiCorp Vault Agent with .NET Core
If your .NET application needs some secrets (e.g. database credentials), your organization might offer HashiCorp Vault to store and manage them for you. As a developer, you need a way to retrieve secrets from Vault for your application to use.
While you can use a C# client library to authenticate to Vault and retrieve secrets with application code, you can better scale your application's interaction with Vault by using Vault Agent. Vault Agent will handle the authentication and retrieval of secrets from Vault, enabling the scalability of the distribution and rotation of short-lived secrets.
This tutorial demonstrates how to use Vault Agent and Consul template to authenticate to Vault, retrieve database usernames and passwords, generate a configuration file, and reload an application each time the database secrets expire.
Prerequisites
This lab was tested on macOS using an x86_64 based processor. If you are running macOS on an Apple silicon-based processor, use a x86_64 based Linux virtual machine in your preferred cloud provider.
Retrieve the demo application
Retrieve the configuration by cloning or downloading the hashicorp/vault-guides repository from GitHub.
Clone the repository.
Or download the repository.
This repository contains supporting content for all of the Vault learn
tutorials. The content specific to this tutorial can be found under the
secrets/dotnet-vault/ProjectApi/
directory.
Switch your working directory to secrets/dotnet-vault/
.
You should find the ProjectApi/
sub-directory.
Vault and database setup
Your application, called project-api
needs to reference a Vault deployment and
Microsoft SQL Server (MSSQL). Create the dependencies by running the setup
script, which will configure and populate data for both Vault and MSSQL.
In addition, the operations team already configured Vault with a database secrets engine that will create a new username and password on-demand for the Microsoft SQL Server database.
To examine this workflow, run get_db_username.sh
to make an API call to Vault
and generate a new username and password for use.
Copy the generated username and store it in the DB_USERNAME
environment
variable.
Example:
Similarly, copy the generated password and store it in the DB_PASSWORD
environment variable.
Example:
The MSSQL contains the HashiCorp
database with a table called Projects
.
It contains information about HashiCorp's projects.
Connect to the MSSQL running in the dotnet-vault_db_1
container with the
username stored in the DB_USERNAME
and the password stored in the
DB_PASSWORD
environment variable.
Now, select the Projects
table.
Execute the GO
command to execute the select command and view the table
entries.
Enter exit
to quit the docker exec
command.
Your operations team has given you a Vault role and secret to log into
Vault using the approle
auth method.
Note
Your Vault administrator may use a different authentication method for you get a Vault token.
You can find the role identifier at ProjectApi/vault-agent/role-id
.
You can find the secret ID at ProjectApi/vault-agent/secret-id
.
The secret ID can only be used five times before it expires.
Configure Vault Agent to create application settings files
Rather than write code within the example application to authenticate and read
secrets from Vault, you can run Vault Agent as a separate process that watches
for changes to the secrets and creates a new ProjectApi/appsettings.json
file.
Open the Vault Agent configuration file,
ProjectApi/vault-agent/config-vault-agent-template.hcl
in your preferred text
editor to review. It automatically authenticates to Vault using the auto_auth
configuration and a template
stanza to read from a template for
appsettings.json
and write out the values.
When Vault Agent starts, it will authenticate to Vault, and write the Vault
token to its sink location which is /ProjectApi/vault-agent/token
. It
generates an appsettings.json
file based on
ProjectApi/vault-agent/appsettings.ctmpl
.
Open the ProjectApi/vault-agent/appsettings.ctmpl
in a text editor to review.
This template file reads database username and password from Vault, and generate
the appsettings.json
file for the ASP.NET Core application. It contains most
of the default configuration for an ASP.NET Core application but creates a
ConnectionString
based on the database username and password from Vault.
Run the script vault_agent_template.sh
to start Vault Agent as a background
process.
Open the resulting ProjectApi/appsettings.json
file. The database connection
string contains the username and password generated by Vault.
The operations team configured Vault to rotate the database credentials after
two minutes. If you wait for a few minutes and check the
ProjectApi/appsettings.json
again, the username and password will not be the
same!
Vault Agent will update ProjectApi/appsettings.json
each time Vault rotates
the database username and password. The example application uses ASP.NET Core,
which allows a live reload of the application each time
ProjectApi/appsettings.json
changes. This makes your database credentials to
be short-lived and significantly reduces its vulnerability.
Open the ProjectApi/Program.cs
file in a text editor to view how the
configuration reloads on changes. The CreateHostBuilder
method configures an
application configuration by adding the appsettings.json
file. You must
configure the file to reloadOnChange: true
(at line 2) in order for the
application to reload based on changes to the file.
In addition to reloading the application upon a file change, you need to reload the database
connection string in the application. In ProjectApi/Models/ProjectContext.cs
, override the
OnConfiguring
method of the DbContext
.
After you added code to handle a live reload of your application, run the run_app.sh
script.
This will restore the .NET packages, retrieve the Vault secret ID and set it as environment
variable, and run the application.
Open your browser to https://localhost:5001/api/Projects
. It will return a JSON
list of HashiCorp projects, the year of their first commit, and GitHub Links.
If you wait 5-10 minutes and refresh the browser with the API call, you will still
be able to access the Project API. Vault Agent continues to write the new
database username and password into ProjectApi/appsettings.json
and the demo application
retrieves the new database connection string. By using Vault Agent to create
the application settings file and adding reload capabilities within application code,
you maintain the availability of your application while reducing the lifetime of your
database username and password.
Enter CTRL-C
to exit the running application.
Clean up the Vault Agent you deployed.
Add Consul template to reload applications on changes
The demo application uses ASP.NET Core, which does support live reload capability. However, some .NET applications do not support application reload. You can add Consul template to trigger an application reload when Vault updates database usernames and passwords.
To perform the steps in this section, make sure you have the Consul template binary installed where your application runs. In this type of configuration, you must run Consul template on the same machine as your application process.
Open the ProjectApi/Program.cs
file in a text editor, and comment out the line
for config.AddJsonFile
(at line 22).
In ProjectApi/Models/ProjectContext.cs
, comment out the method override
OnConfiguring
(line 11 through 14).
When you use a combination of Vault Agent and Consul template, you do not need application code to handle reloading.
Open the Vault Agent configuration file,
ProjectApi/vault-agent/config-vault-agent-token.hcl
to review. The difference
between the config-vault-agent-token.hcl
and config-vault-agent-template.hcl
is that the config-vault-agent-token.hcl
file does not define the template
block.
Open the ProjectApi/vault-agent/config-consul-template.hcl
file to review.
This file configures the Consul template. The configuration will use the token
generated by Vault Agent and generates the ProjectApi/appsettings.json
file
based on the template. Consul template's configuration includes the exec
stanza, which executes a dotnet run
as a child process each time the template
changes. When the database username and password changes, Consul template will
issue a SIGTERM to the application and wait for it to exit in 15 seconds before
restarting the application.
In terminal, execute the vault_agent_token.sh
script to start Vault Agent,
write a token to a file, and start Consul template. The Project API will start.
You can access the API in your browser at https://localhost:5001/api/Projects
.
As the vault_agent_token.sh
script execute, the Consul template logs
record the rotation of the database credentials and restarts the application
for the changes to the credentials.
Summary
In this tutorial, you used Vault Agent and Consul template to reload the application when secrets change values in HashiCorp Vault. This pattern reduces the need to add application code to authenticate to Vault and retrieve its secrets.
Help and Reference
- The Vault Agent with AWS tutorial walks through Vault Agent using AWS auth method
- The Vault Agent with Kubernetes tutorial walks through Vault Agent using Kubernetes auth method
- Vault Agent Caching provides step-by-step instruction of using Vault Agent Caching