Vault — это продукт от компании Hashicorp для работы с секретами. В прошлой статье описывал их реализацию схемы разделения секрета Шамира(разделение ключа шифрования на части с порогом) В этой статье я бы хотел описать часть внутреннего устройства продукта. Утилита представляет из себя HTTP сервер, обрабатывающий запросы и складывающий(или получающий) результаты в storage. Storage может представлять из себя, к примеру, базу данных или файловую систему, или что‑то другое, места хранения могут быть разными, данные также зашифрованы.
1. Маршрутизация
Хашикорповцы предоставляют такую схему волта:
Для объединения логики по обработке каких‑либо данных используются движки(структуры с набором методов), например, KV, который работает с key value значениями. System для настройки сервера vault. Стуктура движка. У движка есть методы, к примеру, положить секрет key‑value или создать новый движок через system. т. е. названия метода — функция с логикой(обертка над функцией, функция). Можно написать свой движок со своими методами.
Для выполнения логики нужно получить объект движка, найти функцию по названию(create secret — положить секрет, к примеру) и вернуть результаты работы функции.
За исключением некоторых методов system, HTTP сервер напрямую не маршрутизирует на функции движков. В HTTP пакете идет обработка HTTP запроса, все значения HTTP группируются в структуру request и пробрасываются в ядро.
В получении объектов используется radix дерево или префиксное дерево. т. е. в ядре свой собственный роутер. Листы в radix дереве роутера содержат в себе сам объект движка и все что ему необходимо для выполнения логики(окружение для движка). Path Routing на картинке от хашикорпа — это и есть роутер в ядре, который находит нужный движок. Ниже я бы так описал маршрут запроса в ядре vault:
Здесь происходит поиск листа. До поиска листа с движком и окружением проверяют права доступа токена.
Использование структуры с набором функций и radix дерева для маршрутизации позволяет расширять вольт, создавать свои движки. Проверять права на доступ до обработки запроса и вести логи до и после выполнения логики движка.
2. Доступ к хранилищу
После того как нашли движок c нужным методом, то необходимо взаимодействовать с хранилищем. В функцию движка пробрасывается storage через объект request. Я думаю что cущность, которая предоставляет доступ к хранилищу является ключевым в волте.
Storage представляет из себя матрешку, где каждый слой прокидывает в функцию следующего обработанные данные. Все из них должны реализовывать методы интерфейса BarrierStorage(Put,Get,List,Delete методы). При создании storage мы в конструктор каждого пробрасываем объект предыдущего.
Примерная схема матрешки:
BarrierView(добавление префиксов,разделение барьеров, у каждого движка свой барьер)
SecureBarrier(интерфейс). Шифрует данные, aes‑gcm
Cache(кеш lru)
Physical(запрос к базе или сохранение на диск к примеру)
Для каждого движка создается свой барьер с префиксом — название движка + уникальный идентификатор для того, чтобы один движок не смог получить доступ к данным другого.
Это выглядит так:
Cache и BarrierView(стуктура называется StorageView) уже есть в sdk hashicorp vault, поэтому я выделил в отдельные репы Physical и SecureBarrier. Здесь можно найти пример того, как собирается матрешка из стораджей.
Заключение
Насколько мне известно, многие большие компании, по крайней мере, российские используют hashicorp vault для хранения секретов. Некоторые готовые реализации, как например доступ к хранилищу, полезны для переиспользования в своем коде. Поэтому хочу в следующий раз описать проверку прав доступа к обработчикам движка при маршрутизации.