没有列表权限 Azure Key Vault 'Forbidden' 错误
Without List permission Azure Key Vault 'Forbidden' error
TL;DR: 没有列表权限 Asp.Net Azure 中部署的核心 2.2 Web 应用程序失败并出现错误 - Microsoft.Azure.KeyVault.Models.KeyVaultErrorException: Operation returned an invalid status code 'Forbidden'
在启动时使用 [=16] =]
我正在研究 Asp.Net 核心 2.2 Web 应用程序,我知道 Azure Key Vault 的工作原理以及部署在 Azure 中的 Web 应用程序如何从密钥值访问密钥、机密和证书。
以下是我当前的配置:
我创建了一个 Azure Key Vault 来存储我所有客户的订阅信息:
然后我创建了 Azure Web App 并为其创建了身份:
稍后在 Azure Key Vault 访问策略中,我授予了此 App Get 和 List 秘密权限。
我不想在我的代码中硬编码任何秘密,因此我使用 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 访问 Secrets 的代码:
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());
});
}
}
}
只要我授予 "List" 权限,一切正常。但是,通过授予 "List" 权限,我注意到可以访问整个 Secret 列表。这暴露了我不太高兴的所有其他客户端订阅信息。我可以为每个客户端创建一个 Key Vault,但这似乎有些矫枉过正。
可能我犯了一个愚蠢的错误但我没有看到它,或者您很可能无法删除 "List" 权限。无论哪种方式,如果有更多知识的人可以阐明 我是否可以在不授予 List 权限的情况下使用 AzureServiceTokenProvider
,我将不胜感激?
更新:1
发现 GitHub 中已经记录了此问题:
Handle No List Permission for Secrets and Azure Key Vault with no List permissions on Secrets fails
更新:2
基于 答案,这是最终的工作代码:
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());
});
}
}
}
正如 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();
TL;DR: 没有列表权限 Asp.Net Azure 中部署的核心 2.2 Web 应用程序失败并出现错误 - Microsoft.Azure.KeyVault.Models.KeyVaultErrorException: Operation returned an invalid status code 'Forbidden'
在启动时使用 [=16] =]
我正在研究 Asp.Net 核心 2.2 Web 应用程序,我知道 Azure Key Vault 的工作原理以及部署在 Azure 中的 Web 应用程序如何从密钥值访问密钥、机密和证书。
以下是我当前的配置:
我创建了一个 Azure Key Vault 来存储我所有客户的订阅信息:
然后我创建了 Azure Web App 并为其创建了身份:
稍后在 Azure Key Vault 访问策略中,我授予了此 App Get 和 List 秘密权限。
我不想在我的代码中硬编码任何秘密,因此我使用 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 访问 Secrets 的代码:
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());
});
}
}
}
只要我授予 "List" 权限,一切正常。但是,通过授予 "List" 权限,我注意到可以访问整个 Secret 列表。这暴露了我不太高兴的所有其他客户端订阅信息。我可以为每个客户端创建一个 Key Vault,但这似乎有些矫枉过正。
可能我犯了一个愚蠢的错误但我没有看到它,或者您很可能无法删除 "List" 权限。无论哪种方式,如果有更多知识的人可以阐明 我是否可以在不授予 List 权限的情况下使用 AzureServiceTokenProvider
,我将不胜感激?
更新:1
发现 GitHub 中已经记录了此问题: Handle No List Permission for Secrets and Azure Key Vault with no List permissions on Secrets fails
更新:2
基于
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());
});
}
}
}
正如 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();