Opinionated .NET library for resolving secrets in application configuration. By default, this library uses a locally-available certificate to access an Azure KeyVault from which it pulls the values of the secrets. It supports local overrides by writing the secrets to local files, or with dotnet user-secrets
.
Install NuGet package Lokad.Secrets and use:
using Lokad.Secrets;
SecretString secret = LokadSecret.Resolve("secret:mykeyvault/mysecretid");
Functions LokadSecret.Resolve
and LokadSecret.ResolveAsync
will resolve the provided secret by attempting all of the following cases, in this order:
-
If the provided value does not begin with
secret:
then it is considered to be a verbatim secret, and will be returned as-is. -
If the application has a .NET user secret with the name
"mykeyvault/mysecretid"
, the value of that user secret is returned. The user secret should be associated to the assembly returned by Assembly.GetEntryAssembly(). -
If a readable file at path
./secrets/mykeyvault/mysecretid
exists (relative to the working directory of the program), its contents are returned as the secret. -
If a readable file at absolute path
C:\LokadData\config\secrets\mykeyvault\mysecretid
(on Windows) or/etc/lokad/secrets/mykeyvault/mysecretid
(on Linux) exists, its contents are returned as the secret. -
The Azure KeyVault vault
https://mykeyvault.vault.azure.net
is opened, and the secret with keymysecretid
is accessed and returned. -
If all of the previous steps failed, throw a
SecretNotResolvedException
.
The SecretString
has four properties:
Value
is the value of secret that was resolved.Key
is the original key, in the form"secret:mykeyvault/mysecretid"
(if the original key was a verbatim secret, this will be redacted instead, usually as"[HIDDEN]"
). This property should always be safe to display, since it is not in itself a secret.Source
indicates how it was resolved:SecretSource.Verbatim
if the input was returned verbatim,SecretSource.UserSecrets
if the secret was found in the .NET user secrets,SecretSource.File
if it was loaded from the file system, andSecretSource.Vault
if it was loaded from Azure KeyVault.Identity
is an additional piece of information to help with debugging. The objective is to confirm whether the secret held by a given application is the one currently present in the source, or if it has been changed since it was resolved by the application. ForSecretSource.File
, this will the modification data of the file from which the secret was taken. ForSecretSource.Vault
, this will be the version identifier of the secret on Azure KeyVault.
In addition, calling SecretString.ToString()
, as well as serializing the object to JSON using JSON.NET or System.Text.Json, will never leak the secret value, and will instead display a combination of the key, source and identity to aid in debugging.
In order to access KeyVault, an application must be registered in Azure Active Directory, using a certificate for authentication. You can follow the detailed documentation on docs.microsoft.com. After following this procedure, you should create three files:
client_id.txt
should contain the Application (client) ID of the application that was created. It will be a GUID.certificate.pfx
should be the certificate that was registered for authentication. This is a password-protected file.certificate_pwd.txt
should contain the password required to open the certificate filecertificate.pfx
.
These files should be placed in C:\LokadData\config\principal
on Windows systems, and in /etc/lokad/principal
on Linux systems, and they should be made readable to the application.
The overloads LokadSecrets.Resolve(IConfiguration)
and LokadSecrets.Resolve(IConfigurationSection)
will produce an IConfiguration
(respectively IConfigurationSection
) that automatically resolves all secrets upon access.
We generally advise against doing this: the interfaces will only return string
instead of SecretString
(losing safety and ease of debugging), and will fail if a non-secret value starts with secret:
.
However, this can be helpful when working with a third-party library that expects its configuration to be provided in the form of an IConfiguration
or IConfigurationSection
.
This package is based on two major principles:
-
All configuration should be committed to the repository. That is, the settings and configuration of an application, in Production, should be entirely controlled by the
appsettings.json
andappsettings.Production.json
files present in version control. These files should not be modified by the build or deployment systems, nor should they be overridden by the execution environment. -
Secrets should never be committed to the repository. That is, neither
appsettings.json
norappsettings.Production.json
should contain secrets (nor should those secrets appear elsewhere in the code).
The configuration files (committed to repository) do not contain secrets, but instead contain secret identifiers. Loading the configuration does not yield the secret values, instead secrets are resolved from their identifiers after the configuration has been loaded.