Проблемы с использованием MSI для аутентификации в Azure BlobStorage - PullRequest
0 голосов
/ 05 декабря 2018

Я пытаюсь подключить консольное приложение C # (.net core 2.1) к хранилищу BLOB-объектов.Я инициализирую клиент хранилища BLOB-объектов несколькими способами.Это:

  1. Строка подключения - полезна при разработке
  2. Принцип обслуживания - возможность развертывания на производстве
  3. Аутентификация MSI - более безопасная, ключи автоматически зацикливаются для нас

В моем коде, если строка соединения не установлена ​​явно, я генерирую ее с использованием принципа обслуживания или MSI в зависимости отопределены настройки приложения (пример кода инициализации ниже).Независимо от того, какой из трех способов я использую, я в конечном итоге инициализирую клиента, используя строку подключения (либо ее явно устанавливают в случае 1, либо генерируют с моим кодом в случае 2 и 3).

Приведенный ниже код работает на 100% нормально для 1 (строка подключения) и 2 (принцип обслуживания), НО я получаю ошибку при попытке достичь 3 (MSI).

При локальном запуске я получаю эту ошибку:

Маркер доступа от неправильного эмитента 'https://sts.windows.net/f8cdef31-a31e-4b4a-93e4-5f571e91255a/'. Он должен соответствовать арендатору' https://sts.windows.net/{my-subscription-id}/', связанному с этой подпиской.Пожалуйста, используйте полномочия (URL) 'https://login.windows.net/{my-subscription-id}', чтобы получить токен.Обратите внимание, что если подписка передается другому арендатору, это не влияет на услуги, но распространение информации о новом арендаторе может занять некоторое время (до часа).Если вы только что перенесли свою подписку и видите это сообщение об ошибке, повторите попытку позже.

При этом я понятия не имею, откуда появляется «f8cdef31-a31e-4b4a-93e4-5f571e91255a», возможно,быть глобальным экземпляром Microsoft.Я попытался смягчить это, запустив мой код в веб-задании в Azure, в котором включен MSI, чтобы проверить, работает ли это, и я получаю:

System.AggregateException: произошла одна или несколько ошибок.(Исключение произошло во время подключения к службе, более подробно см. Внутреннее исключение) ---> System.Exception: Исключение произошло при подключении к службе, подробнее см. Внутреннее исключение ---> Microsoft.Rest.Azure.CloudException: клиент«{my-subscription-id}» с идентификатором объекта «{my-subscription-id}» не имеет полномочий для выполнения действия «Microsoft.Storage/storageAccounts/read« over scope »/ subscription / {my-subscription-id}'.

( note У меня установлена ​​учетная запись MSI как «владелец» и «Оператор ключа учетной записи хранения» хранилища больших двоичных объектов)

Я инициализирую CloudStorageAccountклиент следующим образом:

public void InitializeClient()
{
    // Always using the connection string, no matter how it's generated.
    if (ConnectionString.IsNullOrEmpty()) // if not already set, then build.
        ConnectionString = BuildStorageConnection().GetAwaiter().GetResult();

    CloudStorageAccount.TryParse(ConnectionString, out var storageAccount);

    if (storageAccount == null)
        throw new InvalidOperationException("Cannot find storage account");

    // CloudBlobClient that represents the Blob storage endpoint.
    _cloudBlobClient = storageAccount.CreateCloudBlobClient();
}

И создайте строку подключения следующим образом:

internal async Task<string> BuildStorageConnection()
{
    try
    {
        string token = null;

        if (Config.UseMsi)
        {
            // Managed Service Identity (MSI) authentication.
            var provider = new AzureServiceTokenProvider();
            token = provider.GetAccessTokenAsync("https://management.azure.com/").GetAwaiter().GetResult();

            if (string.IsNullOrEmpty(token))
                throw new InvalidOperationException("Could not authenticate using Managed Service Identity");

            _expiryTime = DateTime.Now.AddDays(1);
        }
        else
        {
            // Service Principle authentication
            // Grab an authentication token from Azure.
            var context = new AuthenticationContext("https://login.windows.net/" + Config.TenantId);

            var credential = new ClientCredential(Config.AppId, Config.AppSecret);
            var tokenResult = context.AcquireTokenAsync("https://management.azure.com/", credential).GetAwaiter().GetResult();

            if (tokenResult == null || tokenResult.AccessToken == null)
                throw new InvalidOperationException($"Could not authenticate using Service Principle");

            _expiryTime = tokenResult.ExpiresOn;
            token = tokenResult.AccessToken;
        }

        // Set credentials and grab the authenticated REST client.
        var tokenCredentials = new TokenCredentials(token);

        var client = RestClient.Configure()
            .WithEnvironment(AzureEnvironment.AzureGlobalCloud)
            .WithLogLevel(HttpLoggingDelegatingHandler.Level.BodyAndHeaders)
            .WithCredentials(new AzureCredentials(tokenCredentials, tokenCredentials, string.Empty, AzureEnvironment.AzureGlobalCloud))
            .WithRetryPolicy(new RetryPolicy(new HttpStatusCodeErrorDetectionStrategy(), new FixedIntervalRetryStrategy(3, TimeSpan.FromMilliseconds(500))))
            .Build();

        // Authenticate against the management layer.
        var azureManagement = Azure.Authenticate(client, string.Empty).WithSubscription(Config.SubscriptionId);

        // Get the storage namespace for the passed in instance name.
        var storageNamespace = azureManagement.StorageAccounts.List().FirstOrDefault(n => n.Name == Config.StorageInstanceName);

        // If we cant find that name, throw an exception.
        if (storageNamespace == null)
        {
            throw new InvalidOperationException($"Could not find the storage instance {Config.StorageInstanceName} in the subscription with ID {Config.SubscriptionId}");
        }

        // Storage accounts use access keys - this will be used to build a connection string.
        var accessKeys = await storageNamespace.GetKeysAsync();

        // If the access keys are not found (not configured for some reason), throw an exception.
        if (accessKeys == null)
        {
            throw new InvalidOperationException($"Could not find access keys for the storage instance {Config.StorageInstanceName}");
        }

        // We just default to the first key.
        var key = accessKeys[0].Value;

        // Build and return the connection string.
        return $"DefaultEndpointsProtocol=https;AccountName={Config.StorageInstanceName};AccountKey={key};EndpointSuffix=core.windows.net";
    }
    catch (Exception e)
    {
        Logger?.LogError(e, "An exception occured during connection to blob storage");
        throw new Exception("An exception occurred during service connection, see inner exception for more detail", e);
    }
}

Основное отличие в том, как я получаю токен доступа, состоит в том, что с использованием принципа обслуживания IУ меня есть контекст аутентификации и я не использую MSI.Влияет ли это на объем аутентификации?Любая помощь и советы с этим очень ценятся!

1 Ответ

0 голосов
/ 06 декабря 2018

Только что понял, как решить проблему выше - изменение GetTokenAsync для второго параметра TenantId дает контекст для вызова аутентификации.

Вот код, который вам понадобится:

token = await provider.GetAccessTokenAsync("https://management.azure.com/", Config.TenantId);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...