Azure

Połączenie Azure Function z Azure Key Vault

Ostatnio wpadłem na problem. Przechowywanie credentiali w Azure Function. W aplikacji ASP.NET Core można użyć do tego secrets.json. A w Azure Functions? Okazuje się, że istnieje usługa Azure stworzona do tego typu zadań: Key Vault. Jednak połączenie Azure Key Vault z Azure Functions nie jest takie oczywiste (przynajmniej dla mnie) a wiele tutoriali, które znalazłem na ten temat nie działa. Postanowiłem opisać więc jak wykonać takie połączenie w działający sposób. Przejdźmy zatem do rzeczy.

Na początku musimy utworzyć sobie Azure Function. Następnie musimy wstępnie ją skonfigurować do korzystania z Azure Key Vault. W funkcji włączamy Azure Managed Service Identity. Już pokazuje jak to zrobić.

MSI w funkcji

Wybieramy funkcje którą chcemy skonfigurować. Przechodzimy na kartę Platform features i wybieramy Managed service identity:

W otwartym oknie przestawiamy opcję Register with AAD na On i zapisujemy zmiany:

Teraz możemy już przejść do konfiguracji Key Vault.

Azure Key Vault

Po pierwsze musimy utworzyć Azure Key Vault. Następnie, po wejściu do utworzonego przed chwilą Key Vault, z listy po lewej stronie wybieramy Access policies i klikamy Add new:

Wynikiem tego działania powinno być takie okno:

Następnie klikamy na pole Select principal i wybieramy funkcje która utworzyliśmy na początku:

Z dropdowna Secret permissions wybieramy opcję Get:

I klikamy przycisk ok, aby potwierdzić dodawanie. Zostaniemy przekierowani do strony naszego Key Vault. Teraz musimy zapisać ustawienia przyciskiem Save:

Teraz przechodzimy do zakładki Secrets i naciskamy Generate/Import:

Po wyświetleniu okna wypełniamy pola Name – nazwa przechowywanej tajnej wartości (hasło, token itp.) i Value – sama wartość hasła, tokena itp.:

Gdy wypełnimy te pola klikamy przycisk Create.

Ostatnią rzeczą jaką musimy zrobić w Key Vault to skopiowanie adresu pod którym tenże rezyduje. Zrobimy to w karcie Overview w polu DNS Name:

Konfiguracja Azure Function

Aby skonfigurować naszą funkcję do działania z Key Vault w zasadzie nie musimy robić nic. Wystarczy tylko użyć odpowiedniego kodu. Natomiast to co przedstawię za chwilkę jest dobra praktyką. Przechodzimy więc do naszej funkcji i wybieramy link Application settings:

Dzięki temu otwarta zostanie karta w Azure Portal o tej samej nazwie. Zjeżdżamy trochę niżej aż dotrzemy do Application settings Klikamy opcję + Add new setting. Jako nazwę wpisujemy np. KeyVaultUri. Jako wartość wklejamy wcześniej skopiowany adres Key Vault. Rezultat powinien wyglądać następująco:

 

Wjeżdżamy na górę i klikamy przycisk Save .

Dostęp do Key Vault z kodu

Teraz zostało już tylko pobrać w odpowiedni sposób wpis z secretem z Key Vault z poziomu kodu funkcji. Przedstawię, jak wygląda taki kod w C#.

To co ważne w kontekście kodu to metoda GetSecretAsync(String keyVaultUrl, String secretName) (link do opisu na MSDNie). Podajemy w niej jako pierwszy argument URL do Key Vaulta. Natomiast jako drugi argument podajemy nazwę naszego secreta który chcemy uzyskać. W naszym przypadku URL do Key Vaulta został zapisany w App Settings (zmienna środowiskowa). Jej pobranie wygląda tak:

Environment.GetEnvironmentVariable("KeyVaultUri")

Gdzie KeyVaultUri to nazwa zmiennej środowiskowej w App Settings którą zapisaliśmy w poprzednim „rozdziale”. Tak więc pobranie faktycznego hasła/tokenu/co tam trzymamy w secret w Key Vault wygląda następująca:

Cały kod służący do pobrania wartości z wpisu typu Credential w Key Vault wyglada tak:

private static HttpClient _client = new HttpClient();

var azureServiceTokenProvider = new AzureServiceTokenProvider();
 var kvClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback), _client);

//poniższa linijka pobiera nasze hasło które zapisaliśmy w Key Vault jako secret
string password = (await kvClient.GetSecretAsync(Environment.GetEnvironmentVariable("KeyVaultUri"), "secret-name")).Value;

Tutaj warto też znznaczyć, że Azure Functions nie zawsze startowane są od zera. Co mam na myśli to to, że niektóre obiekty deklarowac jako static. Tak jak zrobiłem to z HttpClient-em. Warto zastosować też ta strategie przy pobieraniu Credentiali. Czyli obiekt przechowujący/potrzebujący tajne dane też utworzyć jako static. I dopiero, gdy jest on nullem (funkcja startuje od zera) pobierać wartości z Azure Key Vault. Spowoduje to mniejszy czas wywołania funkcji oraz mniejsze koszta związane z odczytem wartości z Azure Key Vault.

Jeśli pobieram hasło, aby utworzyć EmailCredentials zrobię to tak:

private static NetworkCredential _emailCredentials;
private static HttpClient _client = new HttpClient();

if (_emailCredentials == null)
{
         var azureServiceTokenProvider = new AzureServiceTokenProvider();
         var kvClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback), _client);

          var emailPassword = (await kvClient.GetSecretAsync(Environment.GetEnvironmentVariable("KeyVaultUri"), "secret-name")).Value;

          _emailCredentials = new NetworkCredential("mail@mail.com", emailPassword);
}

Jest to całkiem proste, aczkolwiek dojście do poprawnie działającego kodu zajęło mi trochę czasu.

Zostaw odpowiedź

Twój adres email nie zostanie upubliczniony.* Pola wymagane *