Служба приложений для проверки подлинности службы приложений в Azure с помощью управляемого удостоверения

Я настроил две службы приложений в Azure. «Родительский» и «Дочерний» предоставляют конечные точки API.

  • Ребенок имеет конечную точку "Получить".
  • У родителя есть конечные точки «Get» и «GetChild» (которые вызывают «Get» для дочернего элемента с помощью HttpClient).

Я хочу, чтобы все дочерние конечные точки требовали проверки подлинности с помощью управляемого удостоверения и AAD, и я хочу, чтобы все родительские конечные точки разрешали анонимность. Однако в Azure я хочу, чтобы у родительской службы приложений было разрешение на вызов дочерней службы приложений. Поэтому дочерние конечные точки доступны только при использовании родительских конечных точек (или если у вас есть разрешения для учетной записи пользователя на прямое использование дочерних).

На портале Azure:

Аутентификация/авторизация

  • Я включил «Аутентификацию службы приложений» в обеих службах приложений.
  • Для ребенка установлено значение «Войти с помощью AAD».
  • Для родительского элемента установлено значение «Разрешить анонимные запросы».
  • Оба имеют AAD, настроенный в разделе «Поставщики аутентификации».

Личность

  • Установите значение «Вкл.» для обеих служб приложений.

Контроль доступа (IAM)

  • У дочернего элемента есть родительский элемент в качестве назначения роли, тип = "Служба приложений или приложение-функция" и роль = "Участник"

Со всеми вышеуказанными настройками:

  • Вызов Child -> Get требует от меня входа в систему
  • Вызов Parent -> Get возвращает ожидаемый ответ 200 OK
  • Вызов Parent -> GetChild возвращает «401 — у вас нет разрешения на просмотр этого каталога или страницы»

Без использования идентификаторов клиентов/секретов/ключей/и т. д., как я думал, идея Managed Identity заключалась в том, чтобы выбросить все это из окна, учитывая все вышеизложенное, должен ли Parent вызывать Child? И если да, то что я настроил не так?

Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
Как установить LAMP Stack 1/2 на Azure Linux VM
Как установить LAMP Stack 1/2 на Azure Linux VM
В дополнение к нашему предыдущему сообщению о намерении Azure прекратить поддержку Azure Database для MySQL в качестве единого сервера после 16...
9
0
2 594
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий
  • Calling Parent -> GetChild, returns "401 - You do not have permission to view this directory or page"

Without the use of Client ids/Secrets/Keys/etc, as I thought the idea behind Managed Identity was to throw that all out the window, given all the above, should Parent be able to call Child? And if so, what have I setup wrong?

Есть две вещи, которые я замечаю в текущей настройке.

1. Получите токен с помощью управляемого удостоверения для вызова конечной точки «дочерней» службы из «родительской».

Управляемое удостоверение только предоставляет службе вашего приложения удостоверение (без хлопот, связанных с управлением/обслуживанием секретов или ключей приложения). Затем это удостоверение можно использовать для получения токенов для различных ресурсов Azure.

Но ваше приложение по-прежнему несет ответственность за использование этого удостоверения и получение токена для соответствующего ресурса. В этом случае соответствующим ресурсом будет ваш «Дочерний» API. Я думаю, что это, вероятно, та часть, которую вам сейчас не хватает.

Соответствующая документация по Microsoft Docs — Как использовать управляемые удостоверения для службы приложений и функций Azure > Получить токены для ресурсов Azure

using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Azure.KeyVault;
// ...
var azureServiceTokenProvider = new AzureServiceTokenProvider();
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://vault.azure.net");

// change this to use identifierUri for your child app service. 
// I have used the default value but in case you've used a different value, find it by going to Azure AD applications > your app registration > manifest
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://<yourchildappservice>.azurewebsites.net");

Этот пример C#/.NET использует пакет nuget Microsoft.Azure.Services.AppAuthentication и получает токен для Azure Key Vault. В вашем случае вы замените https://vault.azure.net идентификатором Uri для вашего «дочернего» сервиса. Обычно по умолчанию установлено значение https://<yourappservicename>.azurewebsites.net, но вы можете узнать его значение, перейдя в приложения Azure AD и найдя регистрацию соответствующего приложения > манифест. Вы также можете использовать applicationId для целевого приложения (т.е. «дочернего») для получения токена.

Если вы не используете C#/.NET, по той же ссылке Microsoft Docs выше также есть руководство по получению токена с помощью управляемого удостоверения и Вызовы на основе REST с любой платформы. Использование протокола REST

Вот сообщение в блоге, которое также дает хороший обзор - Вызов веб-сайта, защищенного Azure AD, с помощью управляемого удостоверения службы (MSI).

2. Назначение ролей Azure RBAC отличается от ролей Azure AD, которые вы можете использовать.

Я вижу, что вы назначили роль участника удостоверению родительской службы приложений из IAM. Это назначение ролей работает для Azure RBAC и помогает в предоставлении разрешений на управление ресурсами, но утверждения роли Azure AD работают по-другому.

Если то, что вы хотели сделать, это назначить роль родительскому приложению, которое можно проверить в дочернем приложении, и только затем разрешать вызовы, есть другой способ настроить это.

Я должен сначала упомянуть, что эта настройка на основе ролей предназначена для небольшого продвинутого сценария и не является обязательной. Вы сможете вызывать «Дочернюю» службу из «Родительской» после того, как выполните шаги, описанные в пункте 1 выше.

Теперь, когда вызов от родительского к дочернему работает, вы можете ограничить доступ к службе дочернего приложения только «родительским» или несколькими допустимыми приложениями. Вот два подхода к достижению этого.

Оба подхода объясняются в Microsoft Docs здесь — Платформа Microsoft Identity и поток учетных данных клиента OAuth 2.0.

Связанные сообщения SO и блог

Подход 1. Используйте списки контроля доступа

Когда ваш «дочерний» API получает токен, он может декодировать токен и извлекать идентификатор клиентского приложения из утверждений appid и iss. Затем он сравнивает приложение со списком управления доступом (ACL), который оно поддерживает.

В зависимости от ваших требований API может предоставить только часть полных разрешений или все разрешения конкретному клиенту.

Подход 2 — использование разрешений или ролей приложения

Настройте дочернее приложение API для предоставления набора разрешений (или ролей) приложения.

Этот подход немного более декларативен, так как вы определяете разрешение приложения, которое должно быть назначено любому приложению, которое может вызывать ваш child-api.

Перейдите в Azure Active Directory > Регистрация приложений > Регистрация приложения для вашего приложения child-api > Манифест.

Добавьте новую роль приложения.. используя json следующим образом:

"appRoles": [
{
  "allowedMemberTypes": [
    "Application"
  ],
  "displayName": "Can invoke my API",
  "id": "fc803414-3c61-4ebc-a5e5-cd1675c14bbb",
  "isEnabled": true,
  "description": "Apps that have this role have the ability to invoke my child API",
  "value": "MyAPIValidClient"
}]

Назначьте разрешение приложения вашему внешнему приложению

New-AzureADServiceAppRoleAssignment -ObjectId <parentApp.ObjectId> -PrincipalId <parentApp.ObjectId> -Id "fc803414-3c61-4ebc-a5e5-cd1675c14bbb" -ResourceId <childApp.ObjectId>

Теперь в токене аутентификации, полученном вашим дочерним API, вы можете проверить, что коллекция утверждений ролей должна содержать роль с именем «MyAPIValidClient», в противном случае вы можете отклонить вызов с неавторизованным исключением.

Спасибо, Рохит, за такой исчерпывающий ответ, я реализовал ваш фрагмент кода (я использую C#.NET) и получаю токен обратно. Я добавляю этот токен в качестве токена Bearer в запрос HttpClient в Child -> Get, но все равно получаю ошибку 401. Я воссоздал свои службы приложений с нуля, чтобы убедиться, что я не установил какие-либо настройки непреднамеренно. У вас есть еще что-нибудь, что вы можете дать?

user10238915 12.06.2019 09:53

На самом деле мне удалось заставить это работать, используя ApplicationId (ClientId) вместо идентификатора Uri. Что, несмотря на то, что у меня есть работающий сквозной код, я бы предпочел что-то более понятное для человека, если у вас есть какие-либо предложения?

user10238915 12.06.2019 10:20

@user10238915, пожалуйста. Что касается приобретения токена, в идеале вы должны иметь возможность сделать это любым из них, например, applicationId или идентификаторUri. Я не уверен, почему у вас работает только applicationId. Я могу предложить одну вещь: убедитесь, что идентификатор Uri присутствует в разрешенных аудиториях токенов. См. Шаг 4 ссылки, которой я уже поделился в своем ответе. blogs.msdn.microsoft.com/waws/2018/10/23/…

Rohit Saigal 13.06.2019 06:37

Якорь в 1-й ссылке неправильный — он должен начинаться с «#получить-», а не с «#получение-». (Я предложил редактирование, но оно было отклонено).

BillH 16.09.2020 19:22

имеет смысл @BillH, не уверен, почему редактирование было отклонено, но похоже, что вы правы, и ссылка на MS Docs изменилась с тех пор, как я изначально опубликовал ответ. Спасибо за информацию, обновлю.

Rohit Saigal 16.09.2020 19:26

См. раздел «API и другие зарегистрированные приложения Azure AD» в этой статье, в котором раскрывается описанный выше подход 2 blog.yannickreekmans.be/….

Dasith Wijes 17.11.2021 06:32

@ user10238915 здесь вы можете использовать следующий фрагмент кода, в котором используется теперь рекомендуемая библиотека «Azure.Identity» для управляемого удостоверения gist.github.com/dasiths/80a5a8b56c1bb33dcb940d8a3ae39f37 var token = await azureAdTokenRetriever.GetTokenAsync («идентификатор или uri приложения ресурса», «требуемые области»); var autheHeader = new AuthenticationHeaderValue («Носитель», токен);

Dasith Wijes 17.11.2021 06:40

Чтобы расширить принятый ответ.

  1. Вам необходимо определить «Роль приложения» в манифесте регистрации целевого приложения. Это регистрация приложения, которая используется для представления ресурса (служба приложений API).

  2. Затем вы используете Azure CLI, чтобы предоставить разрешение для этой «роли приложения» корпоративному приложению (которое создается при настройке управляемого удостоверения для клиентского приложения). Подробные инструкции см. в разделе «API и другие зарегистрированные приложения Azure AD» в этой статье https://blog.yannickreekmans.be/secretless-applications-add-permissions-to-a-managed-identity/.

Вы можете получить токен, используя следующее, как только разрешения будут предоставлены. В приведенном ниже фрагменте кода используется библиотека Azure.Identity, которая теперь является рекомендуемой библиотекой для управляемого удостоверения в Azure.

public class AzureAdTokenRetriever : IAzureAdTokenRetriever
{
    private readonly ILogger<AzureAdTokenRetriever> logger;
    private readonly IMemoryCache inMemoryCache;

    public AzureAdTokenRetriever(
        ILogger<AzureAdTokenRetriever> logger,
        IMemoryCache inMemoryCache)
    {
        this.logger = logger;
        this.inMemoryCache = inMemoryCache;
    }

    public async Task<string> GetTokenAsync(string resourceId, string scope = "/.default")
    {
        var resourceIdentifier = resourceId + scope;
        if (inMemoryCache.TryGetValue(resourceIdentifier, out var token))
        {
            this.logger.LogDebug("Token for {ResourceId} and {Scope} were fetched from cache", resourceId, scope);
            return (string)token;
        }

        var tokenCredential = new DefaultAzureCredential();
        var accessToken = await tokenCredential.GetTokenAsync(
            new TokenRequestContext(new [] { resourceIdentifier }), CancellationToken.None)
            .ConfigureAwait(false);

        // Set cache options with expiration 5 minutes before the token expires
        var cacheEntryOptions = new MemoryCacheEntryOptions().SetAbsoluteExpiration(accessToken.ExpiresOn.AddMinutes(-5));
        inMemoryCache.Set(resourceIdentifier, accessToken.Token, cacheEntryOptions);
        this.logger.LogDebug("Token for {ResourceId} and {Scope} saved in cache with expiration of {TokenExpiry}",
            resourceId, scope, cacheEntryOptions.AbsoluteExpiration);

        return accessToken.Token;
    }
}

Другие вопросы по теме