A doua parte din setul despre Azure Key Vault. În prima parte am integrat Key Vault cu IConfiguration. Aici ne ocupăm de rotația secretelor și de certificate.
Rotație automată de secrete
Rotația manuală a secretelor e un proces fragil: cineva trebuie să genereze noua valoare, să o actualizeze în Key Vault, să o propage în toate sistemele care o folosesc și să se asigure că nu există downtime în tranziție. Key Vault oferă mecanisme pentru a automatiza și simplifica acest proces.
Politici de expirare
# Seteaza o data de expirare pe un secret
az keyvault secret set-attributes \
--vault-name my-app-keyvault \
--name "Stripe--ApiKey" \
--expires "2026-01-01T00:00:00Z"
# Tag pentru notificare cu 30 zile inainte de expirare
az keyvault secret set-attributes \
--vault-name my-app-keyvault \
--name "Stripe--ApiKey" \
--tags "notify-days-before-expiry=30"
Reîncărcarea automată în ASP.NET Core
Implicit, AddAzureKeyVault încarcă secretele o singură dată la startup. Dacă rotești un secret în Key Vault, aplicația va folosi în continuare valoarea veche până la restart. Poți configura reîncărcarea periodică:
builder.Configuration.AddAzureKeyVault(
new Uri(kvUri),
new DefaultAzureCredential(),
new AzureKeyVaultConfigurationOptions
{
// Reincarca secretele la fiecare 30 de minute
// Permite rotatia fara restart de aplicatie
ReloadInterval = TimeSpan.FromMinutes(30)
});
Cu ReloadInterval setat, aplicația detectează automat noile versiuni. Dar contează cum consumi configurația în servicii:
// IOptions -- valoarea e fixata la startup, NU se actualizeaza
public StripeService(IOptions options) { ... }
// IOptionsSnapshot -- reincarcata la fiecare request scope
// Compatibil cu ReloadInterval, dar doar in servicii scoped/transient
public StripeService(IOptionsSnapshot options) { ... }
// IOptionsMonitor -- valoare mereu curenta, sigur si in singleton
public class StripeService
{
private readonly IOptionsMonitor _monitor;
public StripeService(IOptionsMonitor monitor)
{
_monitor = monitor;
_monitor.OnChange(opts =>
_logger.LogInformation("Stripe options actualizate"));
}
private string ApiKey => _monitor.CurrentValue.ApiKey;
}
Regula practică: IOptionsMonitor<T> când serviciul e singleton (cazul tipic pentru un client HTTP reutilizat), IOptionsSnapshot<T> când serviciul e scoped per request.
Strategia de rotație fără downtime
Rotația fără downtime necesită o perioadă de tranziție în care ambele versiuni ale secretului sunt valide. Patternul recomandat:
- Generezi noua cheie API la furnizor (Stripe, SendGrid etc.)
- Adaugi noua versiune ca secret nou în Key Vault (Key Vault păstrează toate versiunile)
- Aștepți ca
ReloadIntervalsă distribuie noua valoare în toate instanțele - Dezactivezi versiunea veche din Key Vault
- Revoci cheia veche la furnizor
# Adauga noua versiune (versiunea veche ramane accesibila)
az keyvault secret set \
--vault-name my-app-keyvault \
--name "Stripe--ApiKey" \
--value "sk_live_new_value..."
# Listeaza versiunile unui secret
az keyvault secret list-versions \
--vault-name my-app-keyvault \
--name "Stripe--ApiKey"
# Dezactiveaza o versiune veche dupa tranzitie
az keyvault secret set-attributes \
--vault-name my-app-keyvault \
--name "Stripe--ApiKey" \
--version "versiunea-veche-id" \
--enabled false
Certificate în Key Vault
Key Vault poate gestiona și certificate X.509 — import, generare, reînnoire automată și distribuire. Certificatele stocate în Key Vault sunt accesibile aplicațiilor fără a le copia pe disk.
Import certificat existent
az keyvault certificate import \
--vault-name my-app-keyvault \
--name "my-tls-cert" \
--file "./certificate.pfx" \
--password "pfx-password"
Generare certificat cu reînnoire automată
# Politica de certificat cu AutoRenew
cat > cert-policy.json << EOF
{
"issuerParameters": { "name": "Self" },
"keyProperties": { "keyType": "RSA", "keySize": 2048, "reuseKey": false },
"secretProperties": { "contentType": "application/x-pkcs12" },
"x509CertificateProperties": {
"subject": "CN=my-app.azurewebsites.net",
"validityInMonths": 12
},
"lifetimeActions": [{
"action": { "actionType": "AutoRenew" },
"trigger": { "daysBeforeExpiry": 30 }
}]
}
EOF
az keyvault certificate create \
--vault-name my-app-keyvault \
--name "my-tls-cert" \
--policy @cert-policy.json
Parametrul AutoRenew în lifetimeActions face ca Key Vault să reînnoiască automat certificatul cu 30 de zile înainte de expirare. Nu mai există certificate expirate surpriză.
Acces la certificate din cod .NET
dotnet add package Azure.Security.KeyVault.Certificates
public class CertificateService
{
private readonly Uri _kvUri;
public CertificateService(IConfiguration config)
=> _kvUri = new Uri(config["KeyVaultUri"]!);
public async Task GetCertificateAsync(string certName)
{
// Certificatele sunt stocate si ca secrete in Key Vault
// (formatul PFX e accesibil prin SecretClient)
var secretClient = new SecretClient(_kvUri, new DefaultAzureCredential());
var secret = await secretClient.GetSecretAsync(certName);
var certBytes = Convert.FromBase64String(secret.Value.Value);
return new X509Certificate2(certBytes, (string?)null,
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.Exportable);
}
}
Certificat pentru mTLS sau client credentials
// HttpClient cu certificat client din Key Vault
builder.Services.AddHttpClient("secure-api")
.ConfigurePrimaryHttpMessageHandler(sp =>
{
var certService = sp.GetRequiredService();
var cert = certService
.GetCertificateAsync("client-cert")
.GetAwaiter().GetResult();
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(cert);
return handler;
});
Ce urmează
În partea a treia acoperim accesul granular prin RBAC la nivel de secret individual (principiul least privilege), audit trail-ul complet în Log Analytics și Key Vault References în App Service — plus pattern-ul complet de configurație fără niciun secret hardcodat.
Întrebări? Scrie-mi la contact@ludoprogramming.com.