Jak zabezpieczyć i uzyskiwać dostęp do aplikacji w Azure za pomocą AAD?
Internet milczał na ten temat. Postanowiłem więc napisać poradnik wraz z przykładowym projektem, jak nadawać uprawnienia dostępu do zasobów (aplikacji webowych) w Azure za pomocą AAD.
Problem
Posiadamy 2 aplikacje Web/Function App na Azure. Chcemy, aby aplikacja 1
(MVC) miała dostęp do aplikacji 2
(API). Nie chcemy implementować własnego uwierzytelniania i autoryzacji. Możemy więc skorzystać z AAD do zabezpieczenia dostępu do aplikacji 2
.
Rozwiązanie
Zamin przejdę do opisu rozwiązania, nadmienię, że wszystko co robimy ręcznie w tym poradniku zostało przeze mnie zautomatyzowane skryptami. Link do nich znajduje się pod koniec tego wpisu.
Scenariusz autoryzacji i uwierzytelniania
Scenariusz uzyskiwania tokenu do autoryzacji w aplikacji 2
z pozoru wygląda bardzo prosto.
Najpierw aplikacja 1
wysyła zapytanie do Azure Active Directory w celu uzyskania tokenu umożliwiającego uwierzytelnienie w aplikacji 2
. Po uzyskaniu odpowiedniego tokenu JWT, z AppId aplikacji 2
w polu aud
, aplikacja 1
dołącza ten token do żądania HTTP wysyłanego do aplikacji 2
. Następnie ten token sprawdzany jest przez mechanizm Azure, który włączyliśmy w rozdziale Zabezpieczenie aplikacji.
Diagram jest dość prosty i oczywisty dla znających flow OAuth2. Problemy przysparza odpowiednia konfiguracjia aplikacji AAD. Szczególnie dlatego, że niektórych rzeczy nie można (w momencie pisania artykułu) wyklikać z Azure portal. Trzeba zatem posiłkowac się az cli lub modułem Az PowerShell. Ale po kolei.
Konfiguracja Azure i AAD
- Tworzymy dwie aplikacje Azure WebApp (jedna dla aplikacji MVC (
aplikacja 1
), druga dla API (aplikacja 2
)); - Aplikację, która udostępniać będzie API (
aplikacja 2
) należy zabezpieczyć przed nieautoryzowanym dostępem.W celu zabezpieczenia aplikacji należy włączyć w Azure App Service opcję
Authentication / Authorization
. Bardzo dobry poradnik znajduje się na stronie Microsoftu: https://docs.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad. - W aplikacji MVC (
aplikacja 1
) należy włączyć Managed Identity, czyli zarządzaną przez Microsoft tożsamość aplikacji. Tożsamość ta jest reprezentowana w Azure Active Directory (AAD) przez Service Principal’a. Managed Identity pozwala na dostęp do zasobów przez aplikację w jej kontekście, bez dodatkowych haseł, connection stringów, czy innych credentiali. To Azure (a dokładnie AAD) odpowiada za przypięcie odpowiednich uprawnień do App Service i używanie ich przy wysyłaniu zapytań do innych serwisów np. Key Vault, Blob Storage, czy Azure SQL. Programista musi uzyć odpowiedniej formy łączenia do zasobu, tak, aby biblioteka wiedziała, że należy uzyć Managed Identity. W przypadku wysyłania zapytań HTTP między dwoma serwisami napisanymi przez nas to do nas należy pobranie odpowiedniego tokena i dodanie go do zapytania wysyłanego do aplikacji zabezpieczonej za pośrednictwem AAD. Więcej na temat Managed Identity przeczytasz w dokumentacji. - Do aplikacji AAD „stojącej na straży” App Service z API (
aplikacja 2
), należy dodać odpowiednią rolę. Można to zrobić na dwa sposoby:- Edytując Manifest: Azure Active Directory → App registrations → {nazwa aplikacji} → Manifest
- Używając poniższego skryptu:
- Do service principala odpowiadającego App Service z API, należy w dodanej przed chwilą roli przypiąć aplikację MVC (
aplikacja 1
). Podobną operację wykonywałem we wpisie dotyczącym Dostępu do aplikacji w Azure za pomocą AAD w kontekście użytkownika. W tym przypadku różnica polega na tym, że w momencie pisania artykułu, nie da się tego zrobić z Azure Portal. Należy do tego użyć poleceń Azure CLI lub AzureAD PowerShell. Ja uzyję tego drugiego rozwiązania. Poniższe polecenie wykona rządaną przez nas akcję:
New-AzureADServiceAppRoleAssignment -ObjectId $mvcAppObjectId `
-Id $roleId `
-PrincipalId $mvcAppObjectId `
-ResourceId $apiAppObjectId
Jednak jak widać musimy znać tutaj 3 guidy:
- id roli w jakiej przypinamy aplikacje;
- Object Id Service Principala – aplikacji AAD zabezpieczającej App Service z API (
aplikacja 2
). Jest to aplikacja DO której przypinamy dostęp; - Object Id Managed Identity (Service Principala) skojarzonego z App Service aplikacji MVC (
aplikacja 1
). Jest to aplikacja której nadajemy dostęp.
Id roli uzyskamy z manifestu: Azure Active Directory → App registrations → {nazwa aplikacji} → Manifest
. Znajduje sie ono w treści json-a:
Natomiast Object Id w przypadku obydwu aplikacji uzyskamy tak samo. W tym celu przechodzimy pod: Azure Active Directory -> App registrations -> {nazwa aplikacji}
. ObjectId znajduje się na górze w sekcji properties:
Jednak nie musimy tego robić ręcznie. Opisałem to, żebyś wiedział gdzie co jest i skąd pochodzi. Jednak jak wspomniałem, wszystkie skrypty automatyzujące opisze na końcu tego artykułu.
Konfiguracja w kodzie
Przykładowy serwis, wysyłający zapytania z aplikacji MVC (aplikacja 1
) do API (aplikacja 2
) zabezpieczonego aplikacją AAD możesz zobaczyć w moim projekcie AadAccess na GitHubie. Pobieranie tokenu do tego zapytania realizowane jest w klasie ManagedIdentityRequestToken.cs:
namespace AadAccess.MVC.Services
{
using System;
using System.Threading.Tasks;
using Microsoft.Azure.Services.AppAuthentication;
public class ManagedIdentityRequestToken
{
public async Task<string> GetToken(string appId)
{
var azureServiceTokenProvider = new AzureServiceTokenProvider();
return await azureServiceTokenProvider.GetAccessTokenAsync(appId);
}
}
}
Żeby uzysakć taki token musimy mieć ApplicationId (AppId) aplikacji AAD. W przykładowym serwisie, do którego link jewst wyżej, wartość ta jest pobierana z App Settings. Lecz skąd wziąć to id? Przechodzimy w portalu do: Azure Active Directory → Enterprise Applications → {nazwa aplikacji}
. Application Id znajduje się na górze w sekcji properties:
Skrypty
Skrypty które automatyzują setup wszystkich aplikacji w AAD znajduą się w tym folderze.
- Włączenie opcji
Authentication / Authorization
w Azure App Service wykonamy za pomocą skryptu SetWebAppAuth.ps1; - Przypięcie aplikacji AAD do siebie w odpowiedniej roli wykonamy za pomocą skryptu AddAppToServicePrincipalInRole.ps1;
- Wstawianie ApplicationId do AppSettings – potrzebne do uzyskania tokenu z kodu (akapit Konfiguracja w kodzie): AddAppIdToAppSettings.ps1
Przykładowy projekt
W celu zaprezentowania tej funkcjonalności Azure stworzyłem prosty projekt, który zawiera:
- ARM templates – tworzący całą infrastrukturę na Azure;
- Skrypty przedstawione w rozdziale wyżej;
- Trzy projekty:
- ASP.NET Core API (
aplikacja 2
/API z powyższego artykułu) - ASP.NET Core MVC (
aplikacja 1
/MVC z powyższego artykułu) - Azure Functions
- ASP.NET Core API (
Architektura całego rozwiązania wygląda następująco:
Aplikacja MVC (aplikacja 1
) wykonuje zapytania nie tylko do Azure Functions czy API ale również do Key Vault.
Oprócz tego w głównym folderze projektu znajduje się skrypt DeploymentScript.ps1. Tworzy on infrastrukturę na Azure (wywołując ARM template), zabezpiecza aplikację za pomocą AAD, oraz ustawia dostępy między aplikacjami w AAD. Tym sposobem przygotowuje całe środowisko w Azure na przyjęcie kodu i działanie. Jedyne co należy zrobić ręcznie to wrzucić kod 3 aplikacji (MVC, API i Funkcji) na odpowiednio założone App Service i Function App. Tym samym jest to demo, które możesz uruchomić we własnym zakresie.
Podsumowanie
W tym artykule pokazałem jak wyglada uzyskiwanie dostępu między dwoma aplikacjami za pomocą AAD. W tym celu skorzystaliśmy z zabezpieczenia App Service za pomocą Service Principal AAD. W celu uzyskania tokenu dostępowego skorzystaliśmy z Managed Identity – tożsamości aplikacji zarządzanej przez AAD. Co wiecej nie tylko pokazałem jak wygląda flow takiego dostępu, ale również przedstawiłem jak wygląda nadawanie uprawnień aplikacjom do dostępu do siebie. Na koniec zamieściłem opis projektu, który stworzyłem w celu zobrazowania wszystkich kwestii przedstawionych w tym artykule oraz kilku innych. Opisane one zostaną w kolejnych wpisach.
To taki tekst, który najbardziej lubię – zwięzły, z przykładami, grafiką, naprawdę dobrze wszystko wytłumaczone. A swoją drogą według mnie jeśli już mowa o azure, to migracja azure jest naprawdę rewelacyjnym rozwiązaniem.