Сохранение секретов производства в 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 или сторонних поставщиков:

  1. Секреты приложения вводятся с помощью переключателя -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);
        }
    }
  1. В 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"]);
        ...
    }
}

Это!