Det eksterne Star Wars API, som vi benytter, kan kaldes direkte, men de fleste eksterne afhængigheder kræver, at man identificerer sig selv med en eller flere hemmelige værdier. Vi kan smide dem ind som al anden konfiguration, men det vil resultere i, at alle der har adgang til konfigurationen, f.eks. gennem versionskontrol eller Azure-portalen, også får adgang til hemmelighederne, noget der langt fra er ideelt.
Du kan evt. læse mere om problematikken i min kollega Esbens blogindlæg om
nøglerulning.
Hvad vi har brug for er en mekanisme som Azure KeyVault, hvor hemmeligheder kan opbevares og styres sikkert. Det vil dog også kræve, at forskellige udviklere skal gives adgang, og at de kan forbinde til Key Vaulten, hvilket gør, at det kan være praktisk at have et sikkert alternativ til lokal udvikling. Det findes der heldigvis allerede i form af User Secrets!
For at tilføje hemmelige konfigurationsværdier fra en Key Vault og bruge User Secrets lokalt skal der heldigvis kun laves mindre ændringer.
1. Installer NuGet-pakken Azure.Extensions.AspNetCore.Configuration.Secrets (og Azure.Identity hvis du ikke allerede har den installeret) for Key Vault adgang med Azure Managed Identity. Opdater ConfigureAppConfiguration-metoden i Startup.cs til at bygge en midlertidig konfiguration, der ser efter, om vi ønsker at bruge en Key Vault. Hvis det er tilfældet, tilføjer vi en Key Vault reference, der benytter DefaultAzureCredentials (der bl.a. inkluderer Azure Managed Identity) til at tilgå hemmelighederne.
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
var context = builder.GetContext();
var environmentName = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT");
var configBuilder = builder.ConfigurationBuilder
.AddJsonFile(Path.Combine(context.ApplicationRootPath, "appsettings.json"), optional: false)
.AddJsonFile(Path.Combine(context.ApplicationRootPath, $"appsettings.{environmentName}.json"), optional: true)
.AddEnvironmentVariables();
// nyt herunder
var temporaryConfig = configBuilder.Build();
if (!temporaryConfig.GetValue("KeyVault:UseKeyVault", true)) return;
var keyVaultUrl = temporaryConfig.GetValue<Uri>("KeyVault:Url");
configBuilder.AddAzureKeyVault(keyVaultUrl, new DefaultAzureCredential());
}
2. Installer NuGet-pakken Microsoft.Extensions.Configuration.UserSecrets og kør dotnet user-secrets init for at få et lokalt udvikler alternativ til Key Vault-hemmeligheder.
Opdater Startup.cs' ConfigureAppConfiguration til også valgfrit at tilføje User Secrets baseret på en fil der ligger væk fra resten af applikationskoden (og versionskontrol).
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
var context = builder.GetContext();
var environmentName = Environment.GetEnvironmentVariable("AZURE_FUNCTIONS_ENVIRONMENT");
var configBuilder = builder.ConfigurationBuilder
.AddJsonFile(Path.Combine(context.ApplicationRootPath, "appsettings.json"), optional: false)
.AddJsonFile(Path.Combine(context.ApplicationRootPath, $"appsettings.{environmentName}.json"), optional: true)
.AddEnvironmentVariables()
.AddUserSecrets<Startup>(true); // <-- ny
var temporaryConfig = configBuilder.Build();
if (!temporaryConfig.GetValue("KeyVault:UseKeyVault", true)) return;
var keyVaultUrl = temporaryConfig.GetValue<Uri>("KeyVault:Url");
configBuilder.AddAzureKeyVault(keyVaultUrl, new DefaultAzureCredential());
}
3. Tilføj den refererede key vault konfiguration og en placeholder for en hemmelig værdi til appsettings-filerne. appsettings.json:
{
"KeyVault": {
"UseKeyVault": true,
"Url": null // overskriv i appsettings.{miljø}.json
},
"StarWarsApi": {
"BaseUrl": "https://swapi.dev",
"ImaginarySecretApiKey": null // overskriv i key vault / user secrets
}
}
appsettings.Production.json:
{
"KeyVault": {
"Url": "https://produktion-mit-starwarsapi.vault.azure.net/"
},
"StarWarsApi": {
"BaseUrl": "https://swapi.prod" // Note: findes ikke
}
}
4. Slå Key Vault brug fra lokalt. Dette gøres bedst ved at sætte værdien i "Values"-sektionen af local.settings.json (bemærk at denne eksplicit kræver "sti"-formatet og ikke supporterer JSON strukturer).
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"KeyVault:UseKeyVault": false
}
}
5. Tilføj din hemmelighed lokalt til User Secrets: dotnet user-secrets set "StarWarsApi:ImaginarySecretApiKey" "secret-123".
Efter disse trin vil hemmeligheder kunne tilgås på lige fod med al anden konfiguration men baseret på forskellige konfigurationskilder alt efter, hvor applikationen kører henne. I Azure vil det dog yderligere kræve, at applikationen har fået opsat en Managed Identiity, at denne identitet har fået lov til at liste og læse hemmeligheder fra Key Vaulten (der også skal eksistere), og at hemmeligheden er opsat i et Key Vault kompatibelt format (StarWarsApi--ImaginarySecretApiKey i stedet for StarWarsApi:ImaginarySecretApiKey). Og det konkluderer tips og tricks for denne gang! Al den refererede kode kan findes i en lidt mere struktureret opsætning på GitHub.