没有列表权限 Azure Key Vault 'Forbidden' 错误

Posted

技术标签:

【中文标题】没有列表权限 Azure Key Vault \'Forbidden\' 错误【英文标题】:Without List permission Azure Key Vault 'Forbidden' error没有列表权限 Azure Key Vault 'Forbidden' 错误 【发布时间】:2019-05-21 07:39:23 【问题描述】:

TL;DR: 没有列表权限,部署在 Azure 中的 Asp.Net Core 2.2 Web 应用程序失败,并在使用 AzureServiceTokenProvider 时在启动时出现错误 - Microsoft.Azure.KeyVault.Models.KeyVaultErrorException: Operation returned an invalid status code 'Forbidden'

我正在开发 Asp.Net Core 2.2 Web 应用程序,我知道 Azure Key Vault 的工作原理以及部署在 Azure 中的 Web 应用程序如何从密钥值访问密钥、秘密和证书。

以下是我目前的配置:

我创建了一个 Azure Key Vault 来存储我所有客户的订阅信息:

然后我创建了 Azure Web App 并为其创建了 Identity:

后来在 Azure Key Vault 访问策略中,我授予了此应用获取和列出机密权限。

我不想在我的代码中硬编码任何秘密,因此我使用AzureServiceTokenProvider 连接并获取秘密,下面是我的 Program.cs 文件代码:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.AzureKeyVault;
using Microsoft.Extensions.Logging;
using NLog.Common;
using NLog.Web;

namespace AzureSecretsTest

    public class Program
    
        public static void Main(string[] args)
        
            var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
            try
            
                InternalLogger.LogFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "nlog-internals.txt");
                var host = CreateWebHostBuilder(args).Build();
                host.Run();
            
            catch (Exception ex)
            
                logger.Error(ex, "Stopped program because of exception");
                throw;
            
            finally
            
                // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
                NLog.LogManager.Shutdown();
            
        

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((ctx, builder) =>
                
                    //https://anthonychu.ca/post/secrets-aspnet-core-key-vault-msi/
                    var keyVaultEndpoint = Environment.GetEnvironmentVariable("KEYVAULT_ENDPOINT");
                    if (!string.IsNullOrEmpty(keyVaultEndpoint))
                    
                        var azureServiceTokenProvider = new AzureServiceTokenProvider();
                        var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
                        builder.AddAzureKeyVault(keyVaultEndpoint, keyVaultClient, new DefaultKeyVaultSecretManager());
                    
                )
                .UseStartup<Startup>();
    

下面是我的简单 Startup.cs 文件,其中包含从 Azure Key Vault 访问机密的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace AzureSecretsTest

    public class Startup
    
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        
        

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, IConfiguration configuration)
        
            if (env.IsDevelopment())
            
                app.UseDeveloperExceptionPage();
            

            app.Run(async (context) =>
            
                string configValue = configuration["OneOfTheSecretKey"];
                StringBuilder sb = new StringBuilder();
                var children = configuration.GetChildren();
                sb.AppendLine($"Is null or whitespace: string.IsNullOrWhiteSpace(configValue), Value: 'configValue'");
                foreach (IConfigurationSection item in children)
                
                    sb.AppendLine($"Key: item.Key, Value: item.Value");
                
                await context.Response.WriteAsync(sb.ToString());
            );
        
    

只要我授予“列表”权限,一切正常。但是,通过授予“列表”权限,我注意到可以访问整个秘密列表。这暴露了我不太满意的所有其他客户订阅信息。我可以为每个客户端创建一个 Key Vault,但这似乎有点过头了。

我可能犯了一个愚蠢的错误而我没有看到它,或者您很可能无法删除“列表”权限。无论哪种方式,如果有更多知识的人能够阐明我是否可以在不授予列表权限的情况下使用AzureServiceTokenProvider,我将不胜感激?

更新:1

发现已经在 GitHub 中记录了这个问题: Handle No List Permission for Secrets 和 Azure Key Vault with no List permissions on Secrets fails

更新:2 基于Joey's 的回答,这是最终的工作代码:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.AzureKeyVault;
using Microsoft.Extensions.Logging;
using NLog.Common;
using NLog.Web;

namespace AzureSecretsTest

    public class Program
    
        public static void Main(string[] args)
        
            var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
            try
            
                InternalLogger.LogFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "nlog-internals.txt");
                var host = CreateWebHostBuilder(args).Build();
                host.Run();
            
            catch (Exception ex)
            
                logger.Error(ex, "Stopped program because of exception");
                throw;
            
            finally
            
                // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
                NLog.LogManager.Shutdown();
            
        

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((ctx, builder) =>
                
                    //https://anthonychu.ca/post/secrets-aspnet-core-key-vault-msi/
                    //var keyVaultEndpoint = Environment.GetEnvironmentVariable("KEYVAULT_ENDPOINT");
                    //if (!string.IsNullOrEmpty(keyVaultEndpoint))
                    //
                    //    var azureServiceTokenProvider = new AzureServiceTokenProvider();
                    //    var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
                    //    keyVaultClient.GetSecretAsync(keyVaultEndpoint, "").GetAwaiter().GetResult();
                    //    //builder.AddAzureKeyVault(keyVaultEndpoint, keyVaultClient, new DefaultKeyVaultSecretManager());
                    //
                )
                .UseStartup<Startup>();
    

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace AzureSecretsTest

    public class Startup
    
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        
        

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, IConfiguration configuration)
        
            if (env.IsDevelopment())
            
                app.UseDeveloperExceptionPage();
            

            app.Run(async (context) =>
            
                var keyVaultEndpoint = Environment.GetEnvironmentVariable("KEYVAULT_ENDPOINT");
                StringBuilder sb = new StringBuilder();
                if (!string.IsNullOrEmpty(keyVaultEndpoint))
                
                    var azureServiceTokenProvider = new AzureServiceTokenProvider();
                    var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
                    var secret = await keyVaultClient.GetSecretAsync(keyVaultEndpoint, "OneOfTheSecretKey");
                    sb.AppendLine($"Is null or whitespace: string.IsNullOrWhiteSpace(secret.Value), Value: 'secret.Value'");
                

                //string configValue = configuration["OneOfTheSecretKey"];
                //var children = configuration.GetChildren();

                //sb.AppendLine($"Is null or whitespace: string.IsNullOrWhiteSpace(configValue), Value: 'configValue'");

                //foreach (IConfigurationSection item in children)
                //
                //    sb.AppendLine($"Key: item.Key, Value: item.Value");
                //
                await context.Response.WriteAsync(sb.ToString());
            );
        
    

【问题讨论】:

这里的问题是,使用AddAzureKeyVault 扩展从密钥保管库加载所有机密,使其在您的应用程序中可用。您可以手动检索密钥,也可以为每个客户设置一个密钥库 【参考方案1】:

正如 Thomas 所说,当您使用 AddAzureKeyVault 扩展名添加 KeyVaultProvider 时。此时,中间件有足够的信息去提取所有 KeyVault 数据。我们可以立即开始使用配置 API 提取秘密值。

所以,如果你想得到特定的秘密来保证安全,你可以使用下面的代码。

var azureServiceTokenProvider = new AzureServiceTokenProvider();

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

var scret = keyVaultClient.GetSecretAsync(keyvaultEndpoint, SecretName).GetAwaiter().GetResult();

【讨论】:

以上是关于没有列表权限 Azure Key Vault 'Forbidden' 错误的主要内容,如果未能解决你的问题,请参考以下文章

Python Azure 函数 - 使用 Key Vault 进行 MSI 身份验证

Angular - Azure Key Vault 管理 Vault 访问机密

Azure Key Vault 使用Azure Portal创建和查看Azure Key Vault

无法从 Azure Key Vault 获取令牌

无法使用 azure JavaScript 函数和 Key Vault 机密检索 cosmosDB 数据

在 Azure Key Vault 支持的 Databricks 中创建秘密范围失败