Azure-使用MSI的替代方案对控制台应用进行身份验证

时间:2018-08-28 15:49:00

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

我正在研究一种当前在Azure Functions中运行代码的解决方案。这些功能使用MSI身份验证已验证到我们的PaaS服务(Key Vault /服务总线/ Blob存储等)。

然后进行身份验证的代码如下:

// 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();

然后,我可以使用以下代码安全地连接到Service Bus:

// 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);

我们正在更改解决方案,以便代替在ASE上运行Functions,而要在Console App(在Linux上运行)中执行相同的操作。显然,MSI身份验证在这种情况下不起作用,因此我一直在使用Service Priniple AppId和AppSercret。我在AAD中设置了“原则”,如下所示:

enter image description here

使用此服务原则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,但再次需要经过身份验证的服务原则才能访问它。

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

在此先感谢任何指针!

1 个答案:

答案 0 :(得分: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();