在Public Cloud中运行 ASP.NET Core 应用的一个重要方面是如何保护应用所需的机密(如连接字符串和 API 密钥等等)。在这篇文章中,我将介绍一个开源包来连接AWS的密钥管理。
保护 ASP.NET 核心应用中的敏感信息
包括连接字符串、API 密钥和证书等内容。根据经验,切勿将这些值写入 appsettings.json 文件或签入到源代码管理存储库的任何文件中。理想情况下,它们应存储在源代码之外。
为了本地开发,User Secrets(Safe storage of app secrets in development in ASP.NET Core | Microsoft Docs是存储敏感值的首选方法。此工具管理在项目的工作目录 (在用户配置文件目录中).这适用于本地开发,但重要的是要注意用户机密不会加密机密,它只是将它们移动到不太可能意外暴露的位置。
默认情况下,如果您使用的是默认构建器,则用户机密将添加到 ASP.NET 核心配置中:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args) // Adds User Secrets to configuration
.UseStartup<Startup>();
User Secrets仅用于开发时存储敏感配置,那么在生产环境中呢?
很大程度上取决于运行环境。环境变量也可行,不过也优缺点。
也可以CI/CD的时候,将配置信息写到Docker镜像中,但也不是最好的方式。
更好的方法,也是Microsoft提倡的方式(Azure Key Vault configuration provider in ASP.NET Core | Microsoft Docs)用于将敏感信息存储在专用的"敏感信息库"(如 Azure的Key Vault服务)中。在运行时,应用会从 Azure Key Vault服务请求对机密的访问权限。
将密钥添加到 AWS 密钥管理器
与大多数 AWS 功能一样,有几种方法可以将密钥添加到 AWS Secrets Manager。我这里就简单使用Portal添加,过程省略(比如secret name叫mySecret)。
{ "A:B": "A-B-Value" }
使用 ASP.NET 核心从 AWS 密钥管理器加载密钥
现在我们已经存储了一个秘密,我们现在准备在ASP.NET Core应用程序中加载他。这个使用了一个名为Kralizek.Extensions.Configuration.AWSSecretsManager的nuget包。也是开源项目:https://github.com/Kralizek/AWSSecretsManagerConfigurationExtensions
使用将包添加到项目中:
dotnet add package Kralizek.Extensions.Configuration.AWSSecretsManager
然后在程序启动的时候加载:
public class Program
{
public static void Main(string[] args) => BuildWebHost(args).Run();
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
var configuration = config.Build();
var accessKey = configuration.GetValue<string>("AWS:Credentials:AccessKey");
var secretKey = configuration.GetValue<string>("AWS:Credentials:SecretKey");
var region = configuration.GetValue<string>("AWS:Region");
var secretName = configuration.GetValue<string>("AWS:SecretName");
var credentials = new BasicAWSCredentials(accessKey, secretKey);
config.AddSecretsManager(credentials,
RegionEndpoint.GetBySystemName(region),
options =>
{
//因为使用accessKey和secretKey可能会拿到不需要的配置。所以可以用secretName作为前缀来过滤一下。
// 格式为:secretename:configRootKey:configChildKey
options.SecretFilter = entry => entry.Name.Contains(secretName);
// 这里把"secretname:"前缀去掉,代码里就统一了。
options.KeyGenerator = (secret, name) => name.Replace($"{secretName}:", string.Empty);
});
})
.UseStartup<Startup>()
.Build();
// 使用的时候即可通过Configuration["A:B"]获取到
}
在本地开发时不要添加密钥
首先,我不想在本地开发应用程序时使用AWS SecretsManager - .NET User Secrets是解决这个问题的更好解决方案。当本地开发数据库在本地处理应用程序时,
在本地开发时忽略 AWS Secrets Manager.我们可以有条件地检查我们是否在环境中,并且仅在适当的情况下调用:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
// 开发环境不加载
if(!hostingContext.HostingEnvironment.IsDevelopment())
{
config.AddSecretsManager();
}
})
.UseStartup<Startup>()
.Build();
就是这样 -现在可以将密钥安全地存储在密钥管理器中,根据运行应用程序的 AWS IAM 角色在运行时加载密钥!可以根据实际情况调整加载配置的代码。