
Наверное, в любых проектах есть необходимость использования различных секретных данных — строки подключения к БД, АПИ-ключи внешних сервисов и т.д.. К сожалению, до сих пор далеко не всегда разработчики заботятся о соответствующей защите этих данных: на прошлой неделе Гитхаб даже выкатил функцию push protection, чтобы у пользователя был ещё один рубеж защиты от проникновения этих данных в репозиторий.
Несмотря на то, что на рынке довольно много облачных сервисов для хранения и управления секретами, ввиду их зарубежного происхождения с недавних пор их использование стало затруднительно. Но мы не унываем, потому что на наших просторах появился Яндекс.Облако Локбокс.
Давайте добавим работу с секретами из Яндекс.Облака в .Net Core приложение в виде одного из источников конфигурации.
Когда разработчик думает о развёртывании приложения и его секретах, то первыми приходят на ум переменные окружения (сегодня мы не рассматриваем Кубернетис, а будем говорить о классическом развёртывании на виртуальные машины или облачные сервисы). Действительно, у такого решения много плюсов — отсутствие внешней зависимости, простота конфигурирования и так далее. Но они перекрываются одним жирным минусом — это небезопасно. Переменные окружения хранятся в незашифрованном виде и доступны любому процессу в системе. К тому же любая библиотека вашего приложения сможет получить доступ к этим данным, поэтому вы должны быть очень уверены в поставщиках своих зависимостей.
Вынос секретов вовне предполагает внедрение дополнительной зависимости, но добавляет удобства уравления и однозначно добавляет очков безопасности вашему решению: малваря, которая может распространиться по среде вашего приложения, уже не сможет просто так забрать всё в укромный уголок.
Ввиду этого, в переменных окружения есть смысл хранить только те данные, с помощью которых нужно каким-либо образом авторизоваться во внешнем поставщике секретов.
Держа в уме это сокровенное знание, переходим к практике.
Создание секрета в Yandex.Cloud Lockbox
Не так давно в Яндекс.Облаке был анонсирован сервис Lockbox, который представляет из себя хранилище секретов в виде набора пар ключ-значение (до 32-х пар в одном секрете). На данный момент сервис всё ещё находится в предварительном виде и не тарифицируется, но вы можете запросить к нему доступ и уже сейчас воспользоваться этим функционалом.
Для начала откроем консоль Яндекс.Облака и создадим сервисную учётную запись с ролью «lockbox.payloadViewer». Её идентификатор нам понадобится в будущем.

Следующим шагом создадим новый авторизованный ключ для этой сервисной учётки. Нам необходимо записать идентификатор этого ключа, а также приватный ключ. Приватный ключ нам понадобится позже, когда наше приложение будет аутентифицироваться в Облаке с помощью JWT-токена.

Теперь можно создать первый секрет

Обратите внимание на ключ секрета. Стандартный построитель конфигурации дотнетного приложения IConfigurationBuilder (Microsoft.Extensions.Configuration) умеет работать с иерархией, поэтому мы в .Net можем строить сложные конфигурационные объекты. Разбор древовидной структуры происходит с помощью разделителя, по-умолчанию это символ «:», но, к сожалению, этот символ не поддерживается в поле Ключ, поэтому приходится заменить его другим.
Теперь мы создали всё на стороне Локбокса и можно приступать к настройке своего приложения.
Добавление секрета в .Net Core приложение
Для начала добавим нужные для работы и не секретные параметры в настройки приложения (appsettings.json):
{ "YC": { "ConfigurationSecretId": "e6q3a82d6m2bkltjar6q", "ServiceAccountId": "ajk2cdb9jq2mk4unqq13", "ServiceAccountAuthorizedKeyId": "ak228rm0obcn5o90ij43" } }
Также добавьте приватный ключ авторизованного ключа в переменную окружения YC_PRIVATE_KEY.
Для того, чтобы ваше приложение могло взять секреты из Локбокса, ему необходимо аутентифицироваться в Облаке. Для аутентификации есть несколько вариантов:
-
OAuth-токен — не самый лучший вариант так как привязан непосредственно к вашему пользователю и имеет срок жизни 1 год.
-
JWT-токен — отлично подходит для аутентификации приложений. Привязан к сервисной учётке и аутентифицирует приложение с помощью короткоживущего IAM-токена.
-
API-ключ и Статический ключ — простая аутентификация бессрочным ключом, но поддерживаются не все сервисы Облака.
Из предложенного мы воспользуемся JWT-токеном как наиболее безопасным и универсальным вариантом.
Не так давно команда Яндекса начала работу над SDK для платформы .Net (спасибо им!), но, к сожалению, в нём пока не реализован провайдер авторизации с помощью JWT-токена, поэтому пришлось реализовать его самому.
Добавьте в ваше приложение (.Net 6) пакет Delobytes.Extensions.Configuration:
dotnet add package Delobytes.Extensions.Configuration
И добавьте вызов расширения AddYandexCloudLockboxConfiguration в его настройки конфигурации:
WebApplicationBuilder? builder = WebApplication.CreateBuilder(args); builder.Configuration.AddYandexCloudLockboxConfiguration(config => { IConfigurationRoot tempConfig = new ConfigurationBuilder() .AddEnvironmentVariables() .AddJsonFile("appsettings.json") .Build(); config.PrivateKey = Environment.GetEnvironmentVariable("YC_PRIVATE_KEY"); config.ServiceAccountId = tempConfig.GetValue<string>("YC:ServiceAccountId"); config.ServiceAccountAuthorizedKeyId = tempConfig.GetValue<string>("YC:ServiceAccountAuthorizedKeyId"); config.SecretId = tempConfig.GetValue<string>("YC:ConfigurationSecretId"); config.Path = "MyPath"; config.PathSeparator = '-'; config.Optional = false; config.ReloadPeriod = TimeSpan.FromDays(7); config.LoadTimeout = TimeSpan.FromSeconds(20); config.OnLoadException += exceptionContext => { //log }; }); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); WebApplication? app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
Расширение создаст JWT-токен, обменяет его на IAM-токен и загрузит указанный SecretId. В вашу конфигурацию будут загружены все пары «ключ-значение» и они будут подгружаться снова через указанный промежуток времени.
В результате во всём приложении вы сможете получать секреты с помощью стандартных механизмов работы с конфигурацией. Например, вы можете создать соответствующий объект и замапить на него ваш секрет:
public class AppSecrets { public string SecretServiceToken { get; set; } }
[Route("/")] [ApiController] public class HomeController : ControllerBase { public HomeController(IConfiguration config) { _config = config; } private readonly IConfiguration _config; [HttpGet("")] public IActionResult Get() { AppSecrets secrets = _config.GetSection(nameof(AppSecrets)).Get<AppSecrets>(); return Ok(); } }
Итогo
В результате проделанного опыта можно в очередной раз порадоваться гибкости .Net — простота интеграции в существующую Asp Net Core среду (спасибо Майкрософт за наше счастливое детство) позволяет производить миграции с одной платформы на другую в рекордные сроки.
Надеюсь, что Яндекс.Облако и дальше продолжит радовать разработчиков годными сервисами и будет расширять свою поддержку .Net-сообщества, а сообщество будет работать над тем, чтобы не пополнять печальную статистику утёкших секретных данных.
Всем добра!
ссылка на оригинал статьи https://habr.com/ru/post/660449/
Добавить комментарий