Hello, everyone! How are you today? I hope you are doing fine. I'm so excited because this is my first post about HashiCorp Vault. In short, Vault is a secret manager, but it's not only that! Please check here for more information about Vault.
This page inspires me to create this post. If you have some questions, feel free to ask me. I expect you already understand Docker and .NET. If you are still unsure, feel free to comment.
Prerequisites
Preparing the Project
- We will use the project generator provided by .NET 8 SDK. Please generate the solution file.
dotnet new sln -o VaultExample
. - Change the directory to VaultExample.
cd VaultExample
. - Now, we will create the Minimal Web API.
dotnet new api -o VaultExampleAPI
- Add the
HashiCrop.Vault
to theVaultExampleAPI
project.dotnet add VaultExampleAPI package Hashicorp.Vault --version "0.1.0-beta"
- Add the
VaultExampleAPI
to the solution.dotnet sln add VaultExampleAPI
. - Now, it's ready to code!
Writing the Code to Read and Write Secrets into Vault
-
Create
VaultData.cs
insideVaultExampleAPI
directory.
namespace VaultExampleAPI { public class VaultData { public string Name { get; set; } public Dictionary<string, string> Data { get; set; } } }
-
Update
Program.cs
.
using Microsoft.AspNetCore.Mvc; using System.Text.Json; using Vault; using Vault.Client; using Vault.Model; using VaultExampleAPI; var builder = WebApplication.CreateSlimBuilder(args); builder.Logging.AddConsole(); builder.Services.AddSingleton((serviceProvider) => { string address = "http://127.0.0.1:8200"; VaultConfiguration config = new(address); VaultClient vaultClient = new(config); vaultClient.SetToken("dev-only-token"); return vaultClient; }); var app = builder.Build(); var sampleTodos = TodoGenerator.GenerateTodos().ToArray(); var todosApi = app.MapGroup("/todos"); todosApi.MapGet("/", () => sampleTodos); todosApi.MapGet("/{id}", (int id) => sampleTodos.FirstOrDefault(a => a.Id == id) is { } todo ? Results.Ok(todo) : Results.NotFound()); var secretsApi = app.MapGroup("/secrets"); secretsApi.MapPost("/", (VaultClient vaultClient, [FromBody] VaultData secretData) => { // Write a secret var kvRequestData = new KVv2WriteRequest(secretData.Data); vaultClient.Secrets.KVv2Write(secretData.Name, kvRequestData); return Results.Ok(); }); secretsApi.MapGet("/{secretKey}", (ILoggerFactory loggerFactory, VaultClient vaultClient, string secretKey) => { var logger = loggerFactory.CreateLogger("vault"); try { VaultResponse<object> resp = vaultClient.Secrets.KVv2Read(secretKey); var data = resp.Data.ToString(); return !string.IsNullOrEmpty(data) ? Results.Ok(JsonSerializer.Deserialize<object>(data)) : Results.NotFound(); } catch (VaultApiException ex) { logger.LogError(ex, "vault error"); return Results.NotFound(); } }); app.Run();
Break Down Each Added Code
-
Let's break down each added code. First, we add the Dependency Injection (DI) for Vault Client as a Singleton. So, we can get the Vault Client in our Minimal Web API through DI.
builder.Services.AddSingleton((serviceProvider) => { string address = "http://127.0.0.1:8200"; VaultConfiguration config = new(address); VaultClient vaultClient = new(config); vaultClient.SetToken("dev-only-token"); return vaultClient; });
We create an API group.
var secretsApi = app.MapGroup("/secrets");
-
We will use the
POST
method for writing a secret.
secretsApi.MapPost("/", (VaultClient vaultClient, [FromBody] VaultData secretData) => { // Write a secret var kvRequestData = new KVv2WriteRequest(secretData.Data); vaultClient.Secrets.KVv2Write(secretData.Name, kvRequestData); return Results.Ok(); });
-
We will use the
GET {secretKey}
for reading the secret.
secretsApi.MapGet("/{secretKey}", (ILoggerFactory loggerFactory, VaultClient vaultClient, string secretKey) => { var logger = loggerFactory.CreateLogger("vault"); try { VaultResponse<object> resp = vaultClient.Secrets.KVv2Read(secretKey); var data = resp.Data.ToString(); return !string.IsNullOrEmpty(data) ? Results.Ok(JsonSerializer.Deserialize<object>(data)) : Results.NotFound(); } catch (VaultApiException ex) { logger.LogError(ex, "vault error"); return Results.NotFound(); } });
More Information About The Codes
You can the repository here. My Pull Request:
Testing our API
-
Run the Vault server. We can use
docker-compose.yml
to help us and rundocker compose up -d
.
version: '3.9' services: vault: ports: - 8200:8200 environment: - VAULT_DEV_ROOT_TOKEN_ID=dev-only-token image: vault
-
Open a browser and type
localhost:8200
. Ensure the Vault page is shown. Run the API.
dotnet run VaultExampleAPI
Test use HTTP Client or Postman.
Testing Write Secret
Testing Read Secret
Read the secret from the Browser
Continue to open
localhost:8200
in the browser and input the tokendev-only-token
.-
Click on
secret
. -
Click on the created secret.
-
You can check the entry.
Thank you for reading
Thank you for reading. :) Feel free to give feedback in the comment section.
Top comments (1)
This tutorial is a valuable resource for developers interested in learning how to integrate HashiCorp Vault into their .NET applications using the Vault .NET Client Library. The clear explanations and practical examples make it a helpful guide for both beginners and experienced developers looking to enhance their understanding of secret management and secure data storage.