Сохранение секретов производства в ASP.NET Core
Я пытаюсь выяснить, где лучше хранить секреты создания приложений для приложения ASP.NET Core. Есть два похожих вопроса
Где я должен хранить строку подключения для рабочей среды моего приложения asp.net-5?
а также
Как развернуть ASP.NET Core UserSecrets для производства
которые оба рекомендуют использовать переменные среды.
Моя проблема в том, что я хочу запустить несколько экземпляров своего веб-приложения с различными базами данных и разными учетными данными. Поэтому должна быть некоторая конфигурация каждого экземпляра, включая секреты.
Как это можно было бы сделать безопасным способом?
Обратите внимание, что приложение должно иметь возможность самообслуживания и находиться под управлением IIS! (Позже мы также планируем запустить его на Linux, если это имеет значение для вопроса)
Обновление
этот вопрос не о попытке использования секретов пользователя ASP.NET в производстве! UserSecrets исключены для производства.
Ответы
Ответ 1
Как говорится, пользовательские секреты только для разработки (чтобы избежать случайного ввода учетных данных в SCM) и не предназначены для производства. Вы должны использовать одну строку соединения для каждой базы данных, т.е. ConnectionStrings:CmsDatabaseProduction
, ConnectionStrings:CmsDatabaseDevelopment
и т.д.
Или используйте контейнеры докеров (если вы не используете Azure App Service), вы можете установить его на основе контейнера.
В качестве альтернативы вы также можете использовать файлы appsetting на основе среды. appsettings.production.json
, но они не должны включаться в управление контролем источника (Git, CSV, TFS)!
В старте просто выполните:
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
Таким образом, вы можете загружать определенные вещи из appsettings.production.json
и все равно переопределять его через переменную среды.
Ответ 2
Существует простой способ безопасного хранения секретов приложений в рабочей среде без использования Azure Vault или сторонних поставщиков:
https://github.com/aspnet/AspNetCore.Docs/issues/12428
Ответ 3
Существует простой способ безопасного хранения секретов приложений в рабочей среде без использования Azure Vault или сторонних поставщиков:
- Секреты приложения вводятся с помощью переключателя -config приложения, например:
dotnet helloworld -config
; В Program.Main
обнаруживает этот переключатель, чтобы позволить пользователю вводить секреты и сохранять их в отдельном файле .json в зашифрованном виде:
public class Program
{
private const string APP_NAME = "5E71EE95-49BD-40A9-81CD-B1DFD873EEA8";
private const string SECRET_CONFIG_FILE_NAME = "appsettings.secret.json";
public static void Main(string[] args)
{
if (args != null && args.Length == 1 && args[0].ToLowerInvariant() == "-config")
{
ConfigAppSettingsSecret();
return;
}
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((builder, options) =>
{
options.AddJsonFile(ConfigFileFullPath, optional: true, reloadOnChange: false);
})
.UseStartup<Startup>();
private static void ConfigAppSettingsSecret()
{
var serviceCollection = new ServiceCollection();
AddDataProtection(serviceCollection);
var services = serviceCollection.BuildServiceProvider();
var dataProtectionProvider = services.GetService<IDataProtectionProvider>();
var protector = CreateProtector(dataProtectionProvider);
string dbPassword = protector.Protect("DbPassword", ReadPasswordFromConsole());
... // other secrets
string json = ...; // Serialize encrypted secrets to JSON
var path = ConfigFileFullPath;
File.WriteAllText(path, json);
Console.WriteLine($"Writing app settings secret to '${path}' completed successfully.");
}
private static string CurrentDirectory
{
get { return Directory.GetParent(typeof(Program).Assembly.Location).FullName; }
}
private static string ConfigFileFullPath
{
get { return Path.Combine(CurrentDirectory, SECRET_CONFIG_FILE_NAME); }
}
internal static void AddDataProtection(IServiceCollection serviceCollection)
{
serviceCollection.AddDataProtection()
.SetApplicationName(APP_NAME)
.DisableAutomaticKeyGeneration();
}
internal static IDataProtector CreateProtector(IDataProtectionProvider dataProtectionProvider)
{
return dataProtectionProvider.CreateProtector(APP_NAME);
}
}
- В Startup.cs прочитайте и расшифруйте секрет:
public void ConfigureServices(IServiceCollection services)
{
Program.AddDataProtection(services);
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
if (env.IsProduction())
{
var dataProtectionProvider = app.ApplicationServices.GetService<IDataProtectionProvider>();
var protector = Program.CreateProtector(dataProtectionProvider);
var builder = new SqlConnectionStringBuilder();
builder.Password = protector.Unprotect(configuration["DbPassword"]);
...
}
}
Это!