【问题标题】:Azure - authenticate a console app using alternative to MSIAzure - 使用 MSI 的替代方法对控制台应用程序进行身份验证
【发布时间】:2018-08-29 18:14:59
【问题描述】:

我正在开发一个当前在 Azure Functions 中运行代码的解决方案。这些功能使用 MSI 身份验证向我们的 PaaS 服务(Key Vault/Service Bus/Blob Storage 等)进行身份验证。

验证的代码如下:

// Connect to KeyVault in the context of the running code.
var tokenProvider = new AzureServiceTokenProvider();

var config = new ConfigurationBuilder().AddAzureKeyVault(
     keyvaultUri,
     new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(tokenProvider.KeyVaultTokenCallback)),
     new DefaultKeyVaultSecretManager())
  .Build();

这让我可以使用如下代码安全地连接到服务总线:

// Get ServiceBus connection settings.
config.GetSection("Messaging").Bind(ConfigSettings);    
var namespaceName = Regex.Match(ConfigSettings.ConnectionString, @"Endpoint=sb:\/\/([^.]*)", RegexOptions.IgnoreCase).Groups[1].Value;


var token = tokenProvider.GetAccessTokenAsync("https://management.core.windows.net/", string.Empty).Result;
var tokenCredentials = new TokenCredentials(token);

var client = RestClient.Configure()
        .WithEnvironment(AzureEnvironment.AzureGlobalCloud)
        .WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
        .WithCredentials(new AzureCredentials(tokenCredentials, tokenCredentials, string.Empty, AzureEnvironment.AzureGlobalCloud))
    .Build();

// Authenticate against Service Bus.
ServiceBusNamespace = Azure.Authenticate(client, string.Empty)
    .WithSubscription(ConfigSettings.SubscriptionId)
    .ServiceBusNamespaces.List()
    .SingleOrDefault(n => n.Name == namespaceName);

我们正在更改我们的解决方案,以便我们将在控制台应用程序(在 Linux 上运行)中执行相同的操作,而不是在 ASE 上运行函数。显然 MSI auth 在这种情况下不起作用,所以我希望使用服务原则 AppId 和 AppSercret。我在 AAD 中设置的原理如下:

利用本服务原则AppId和AppSecret的代码如下:

    public async static Task<string> GetAccessToken(string tenantId, string appId, string appSecret)
    {
        var authenticationContext = new AuthenticationContext($"https://login.windows.net/{tenantId}");
        var credential = new ClientCredential(clientId: appId, clientSecret: appSecret);
        var result = await authenticationContext.AcquireTokenAsync(resource: "https://management.core.windows.net/", clientCredential: credential);

        if (result == null) {
            throw new InvalidOperationException("Failed to obtain the JWT token");
        }

        return result.AccessToken;    
     }

但它要求我准备好 AppId、AppSecret 和 TenantId 之类的内容以供应用使用。出于明显的安全原因,我不想使用 AppSettings。

我现在可以使用与之前类似的代码运行服务总线身份验证:

    var namespaceName = Regex.Match(connectionString, @"Endpoint=sb:\/\/([^.]*)", RegexOptions.IgnoreCase).Groups[1].Value;

    var tokenCredentials = new TokenCredentials(token);

    var client = RestClient.Configure()
        .WithEnvironment(AzureEnvironment.AzureGlobalCloud)
        .WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
        .WithCredentials(new AzureCredentials(tokenCredentials, tokenCredentials, string.Empty, AzureEnvironment.AzureGlobalCloud))
        .Build();

    var serviceBusNamespace = Azure.Authenticate(client, string.Empty)
        .WithSubscription(subscriptionId)
        .ServiceBusNamespaces.List()
        .SingleOrDefault(n => n.Name == namespaceName);

    if (serviceBusNamespace == null)
    {
        throw new InvalidOperationException($"Couldn't find the service bus namespace {namespaceName} in the subscription with ID {subscriptionId}");
    }

我的问题 - 担心安全性然后将所需的配置字段(AppId、AppSecret、TenantId)存储在 config 或 Env Vars 中似乎适得其反。我还有其他选择吗?除非经过身份验证,否则我无法使用 KeyVault,但我需要再次验证我的服务原则才能访问它。

以前有人做过这种方法吗?也许服务原则不是正确的方法?

提前感谢任何指点!

【问题讨论】:

  • 您确定不能使用 MSI 吗?最终,它使用了一个可从运行时环境访问的 HTTP 端点(你也应该能够从 Linux 中使用它:))
  • 其实不是,我不是!我只是习惯于通过 ASE 而不是在代码中配置它。有什么好的例子吗?不幸的是,我无法从控制台应用程序中看到使用 msi 进行身份验证的具体代码示例
  • 您也可以查看我关于 MSI 的文章:joonasw.net/view/azure-ad-managed-service-identity。我提到了它在后台使用的两个环境变量。当然问题是您需要获取这些环境变量。我不确定您的控制台应用程序是否可以获取它们。
  • 感谢 Joonas,所以如果我设置 MSI_ENDPOINT 和 MSI_SECRET 这两个环境变量,并运行这几行代码,它仍然适用于控制台应用程序吗?整洁的!我从哪里获得 MSI_ENDPOINT 和 MSI_SECRET? (我最初没有设置) - 谢谢!!!

标签: c# azure .net-core console-application azure-keyvault


【解决方案1】:

根据您的描述,您不想将AppId 存储在配置文件中,而是希望使用Service Principle 进行身份验证。

如果是这样,您可以参考 juunas 文章,您可以使用AzureServiceTokenProvider 进行身份验证并继续您想要的。

您可以使用以下代码获取密钥库机密,而无需使用 AppIdAppSecret

var azureServiceTokenProvider = new AzureServiceTokenProvider();

var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));

var scret = keyVaultClient.GetSecretAsync("https://xxxx.vault.azure.net", "xxxx").GetAwaiter().GetResult();

【讨论】:

  • 应用程序将在 Linux 上运行,而不是在 ASE 上,我们可以将应用程序设置为在服务原则上下文下运行。因此,为了获得该上下文,我需要服务原则 AppID/AppSecret 在代码执行时作为该原则进行身份验证 - 还是我误解了?
  • 是的,你需要获取AppId和AppSecret才能进行认证,你怀疑什么?
  • 我怀疑您显示的代码 - 因为您没有在代码中设置 AppId/AppSecret,所以它将依赖于它运行的上下文 (AzureServiceTokenProvider())。当我们将应用程序从 ASE 中取出时,这将不起作用。
  • 是的,所以你想找到一种在 linux 上进行身份验证的方法吗?
  • 是的,就是这样@joey-cai 我在想服务原则是要走的路。我已将 Azure WebJob 从 ASE 中移出并放入我将部署到 Linux 的控制台应用程序(.net 核心)中,但不想失去 ASE 中的安全性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-09
  • 2018-11-14
  • 2021-12-23
  • 1970-01-01
  • 1970-01-01
  • 2019-05-29
相关资源
最近更新 更多