Уже больше двух лет мы развиваем Deckhouse Stronghold — решение для безопасного управления жизненным циклом секретов. С начала 2025 года мы реализовали в продукте привычный пользователям HashiCorp Vault функционал: пространства имён, автоматические бэкапы данных и репликацию для хранилищ KV1/KV2.
После ухода HashiCorp с российского рынка многие компании стоят перед выбором: переехать на Community-редакцию Vault и дорабатывать её под свои потребности или купить готовый продукт, где многие фичи доступны «из коробки» и есть поддержка от разработчиков. В статье мы сравним, как похожие возможности реализованы в Vault CE (а иногда и EE) и Deckhouse Stronghold, плюс поделимся ближайшими планами по развитию своего продукта.

Содержание
Пространства имён
Как можно сделать в HashiCorp Vault CE
В Vault можно очень детализировано выдавать доступы к секретам: по имени и по типу операции (чтение, создание, изменение, удаление, листинг). Но управлять политиками или пользователями и ролями может только администратор.
Технически пользователю тоже можно выдать возможность управления методами аутентификации, ролями или политиками, но на практике это с большой долей вероятности означает, что он сможет выдать себе любые права в конкретном экземпляре Vault. Например, сможет изменить свою политику, создать нового пользователя или роль с привилегированной политикой и так далее. Получается, что в Vault CE нет возможности предоставить права для выдачи прав только на ограниченный круг задач — или всё, или ничего.
Представьте, что у вас в компании есть два отдела с разными секретами. Вы не сможете дать возможность руководителю отдела А управлять доступом своих сотрудников только к секретам отдела А, а руководителю отдела В — управлять доступом своих сотрудников только к секретам отдела В. Пока у вас один администратор Vault, это не проблема. Но в больших компаниях такие ограничения создают серьёзные неудобства.
Решение в примере с отделами А и В может быть простым — для каждого отдела создаётся свой экземпляр или кластер Vault и руководителю отдела выдаются в нём административные права. Однако при кажущейся изящности подхода возникают и свои проблемы. И они не связаны с оркестрацией большого количества экземпляров Vault — с этим может помочь Kubernetes. Зато вам придётся безопасно хранить recovery-ключи для каждого экземпляра Vault (или unseal-ключи, если используется Shamir's unseal), делать резервные копии каждого экземпляра и управлять безопасностью каждого экземпляра отдельно. А если на каком-то этапе вы захотите создать роль, которая имеет доступ к секретам обоих отделов, это будет невозможно, поскольку физические экземпляры Vault — разные.
Другим решением проблемы может быть вынос управления конфигурацией в CI-пайплайны, когда политиками и ролями управляют через коммиты в GitLab или GitHub. Но это решение половинчатое. Хотя токен для управления ролями и не находится у конкретного человека, доступ к управлению ролями и политиками в Vault сможет получить тот, кто имеет доступ либо к Git-репозиторию, либо к раннерам.
Ещё один возможный способ — использовать менеджер конфигурации через операторы Vault в Kubernetes, например bank-vaults operator. Но с точки зрения безопасности этот вариант тоже спорный, ведь доступ к секретам может получить администратор Kubernetes-кластера, изменив конфигурацию, к которой у него может быть доступ.
В Enterprise Vault эту проблему решают с помощью пространств имён, или namespaces. При этом в каждом пространстве имён может быть свой локальный администратор.
Как сделано в Stronghold
Очевидно, что как только возникает необходимость делегировать права на выдачу прав (а это происходит в командах от 10 человек), функционал пространств имён просто незаменим. И недавно мы реализовали его в Stronghold. Теперь наши пользователи могут создавать дочерние рабочие пространства и выдавать права на управление ими, при этом поддерживаются вложенные рабочие пространства. Новый функционал максимально совместим с Vault Enterprise по возможностям и методам API, так что если вы использовали Enterprise-решение от HashiCorp, особой разницы не ощутите.
Вот какие возможности пространства имён дают нашим пользователям:
Использование одной инсталляции Stronghold для работы нескольких изолированных и независимых сред.
Изоляция секретов в пространствах имён одного тенанта, недоступных для других тенантов.
Настройка политик контроля доступа, при которых администратор каждого тенанта имеет определённые права, независимые от администраторов других тенантов.
Представим такую цепочку рабочих пространств: ns2 является дочерним рабочим пространством ns1, а ns3 является дочерним к ns2. При этом в каждом пространстве имён — свои политики и методы аутентификации.

Управление политиками, методами аутентификации и identity внутри каждого рабочего пространства происходит точно так же, как в каждом отдельно взятом экземпляре Vault CE. Но при этом появляется возможность управлять доступом к дочерним рабочим пространствам.
Давайте рассмотрим на примерах из схемы выше:
Политика А даёт доступ к secret1, созданному внутри ns1, и не даёт никаких доступов к ns2 и ns3.
Политика B позволяет пользователю из ns1 получить полный доступ к ns2 и всем его дочерним пространствам имён, то есть в том числе и к ns3.
Политика С позволяет пользователю из ns1 читать secret3, созданный в ns2, при этом не давая никакого доступа к ns1 и ns3.
Политика D для пользователей из ns2 даёт полный доступ как к пространству имён ns2, так и к дочернему ns3.
Политика E для пользователей из ns2 даёт права читать и изменять secret3, созданный в ns2.
Политика F действует только на пользователей ns3 и даёт им возможность взаимодействовать с secret3 пространства имён ns3.
Резервное копирование данных
Как можно сделать в HashiCorp Vault CE
Для пользователя Vault CE с интегрированным хранилищем Raft очевидным и относительно несложным вариантом настройки автоматического создания снапшота данных может быть классическая комбинация cronjob с bash script. В самой основе создания снапшота пользователем лежит команда vault operator raft snapshot.
Стоит обязательно прочитать рекомендации самой HashiСorp о том, как бэкапить Vault. В большинстве случаев для выполнения бэкапа Vault нужно выполнить резервное копирование данных из бэкенда хранения. При этом, для некоторых бэкендов хранения для создания консистентной резервной копии может потребоваться остановка Vault и offline-backup. Здесь нужно руководствоваться требованиями и собственными пожеланиями к архитектуре системы. Кстати, у нас в блоге есть статья о том, как вообще бэкапить Vault.
Если говорить про бэкап с помощью raft snapshot, то для начала вам понадобится токен с политикой read на /sys/storage/raft/snapshot
. Соответственно, надо подумать, где вы будете хранить этот токен, будете ли его перевыпускать или он будет статичным. Ещё нужно будет решить вопрос с метриками и мониторингом: например, может понадобиться проверка на нулевой размер бэкапа после выполнения команды или алертинг на код выполнения команды бэкапа, который не соответствует ожидаемому.
Кроме того, снапшот стоит сохранять с понятным названием и временной меткой, ведь вам вряд ли хватит только одного снапшота. Вероятнее всего, вы реализуете какой-то стандартный Grandfather-Father-Son (GFS) алгоритм хранения.
Ну и хранить снапшот там, где запущен Vault, не имеет большого смысла. Наверное стоит перемещать эти файлы куда-то в имеющееся распределённое хранилище, например S3-совместимое. Для этого опять понадобится подумать над несколькими вещами:
Мониторинг — как убедиться в том, что снапшот скопирован?
Где хранить ключи к хранилищу?
Как удалять старые снапшоты?
Как сделано в Stronghold
Мы всегда используем Integrated Raft Storage в качестве бэкенда хранения, поэтому описанный выше вариант с raft snapshot работает и в Stronghold. Помимо этого в Stronghold реализовано автоматическое создание резервных копий по настроенному расписанию, которое администратор задаёт через командную консоль, API или UI. Данные при этом можно сохранять как в файлы, так и в S3-совместимое объектное хранилище.
Аналогичный функционал есть в HashiСorp Vault EE, и мы постарались использовать те же методы API для настройки. Это даёт возможность пользователями Stronghold использовать уже работающий Terraform или готовые автоматизации, если они были написаны при работе с Enterprise-версией Vault. Единственное исключение — два storage type, которые не реализованы в Stronghold: google-gcs
и azure-blob
.
Чтобы делать бэкапы, например, раз в один час и сохранять их за один день в надёжном S3 внутри нашего облака с кастомным TLS-сертификатом, нужно выполнить команду:
stronghold write sys/storage/raft/snapshot-auto/config/my_s3_hourly \
interval=1h path_prefix=backups file_prefix=hourly retain=24 \
aws_storage_type=aws-s3 aws_s3_bucket=vedro aws_s3_region=ru-1 \
aws_s3_endpoint=s3.ru-1.makes3.greatagain.ru \
aws_access_key_id=very_secret_id \
aws_secret_access_key=very_secret_key \
aws_s3_ca_certificate=@/opt/stronghold/tls/my_s3_ca.crt
Кстати, тут мы тоже немного расширили функционал Vault EE. Ребята из HashiСorp, похоже, считают, что у S3 не может быть кастомных сертификатов, и максимум, что можно сделать для работы с S3 с внутренним Certificate Authority, — это использовать ключ aws_s3_disable_tls. В Stronghold же можно указать свой CA.
Функционал бэкапов даёт возможность посмотреть статус каждого конкретного инстанса расписания:
stronghold read sys/storage/raft/snapshot-auto/status/my_s3_hourly
…или список всех установленных расписаний:
stronghold list sys/storage/raft/snapshot-auto/config
Управлять бэкапами из консоли захочется не всем, поэтому в части UI мы тоже пошли чуть дальше Vault EE и добавили возможность управления расписанием бэкапов и мониторинга их статуса в веб-интерфейсе Stronghold:


Что даёт нашим пользователям автоматическое резервное копирования данных:
Отсутствие необходимости писать отдельные скрипты для создания снапшотов.
Сохранение файлов конфигурации внутри резервных копий при переносе настроек.
Возможность восстановления из резервной копии путём загрузки снапшота или развёртывания новой инфраструктуры и восстановления снапшота в случае полного выхода из строя.
Репликация хранилища секретов типа KV1/KV2
Как можно сделать в HashiCorp Vault CE
Обычная ситуация: у вас есть приложения, запущенные в двух дата-центрах, и вы хотите, чтобы приложения работали с хранилищем секретов в своём дата-центре, и не хотите зависеть от наличия связи между ДЦ. Но секретами хотелось бы управлять из одного места. А значит, секреты нужно как-то синхронизировать.
Для копирования секретов между инстансами Vault можно использовать Medusa — CLI-инструмент для безопасного экспорта и импорта данных. Почему безопасного? Данные можно не только экспортировать и импортировать в текстовом виде, а существует возможность шифрования передаваемых данных собственными ключами с последующей расшифровкой перед импортом.
Давайте представим, что нам надо сделать репликацию по расписанию. К примеру, можно использовать вот такой CronJob:
apiVersion: batch/v1
kind: CronJob
metadata:
name: vault-sync
spec:
schedule: "0 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: medusa
image: medusa-cli:latest
command: ["/bin/sh", "-c"]
args:
- medusa export secret/ --encrypt=true --public-key=public-key.pem \
--address="http://vault-source:8200" --output=encrypted-data.yaml &&
kubectl cp encrypted-data.yaml target-pod:/data &&
medusa import secret/ encrypted-data.yaml --decrypt=true \
--private-key=private-key.pem --address="http://vault-destination:8200"
restartPolicy: OnFailure
Чтобы наш файл с экспортированными данными было безопасно хранить и передавать, его желательно зашифровать. Для шифрования понадобятся сгенерированные RSA-ключи:
openssl genrsa -out private-key.pem 4096
openssl rsa -in private-key.pem -pubout -out public-key.pem
Приватный ключ нужно сохранить в виде секрета в целевой кластер, а публичный — в исходный кластер:
# medusa-secret.yaml в исходном кластере.
apiVersion: v1
kind: Secret
metadata:
name: medusa-config
data:
VAULT_TOKEN: <base64-закодированный-токен>
public-key.pem: <base64-закодированный-публичный-ключ>
Результат можно сохранять в Persistent Volume или S3-совместимое хранилище, например MinIO.
При этом придётся обязательно думать о безопасности. Надо будет использовать TLS для подключения к Vault, соответственно нужно и поддерживать эти сертификаты. Понадобится ограничивать права ServiceAccount только необходимыми секретами, так что, скорее всего, потребуется какой-то репо с IaC-описанием этих политик. А ещё нужно будет регулярно обновлять RSA-ключи для шифрования/дешифрования, при этом ответ на вопрос о том, где именно хранить эти самые секретные RSA-ключи, открыт.
Одной из особенностей такого подхода к «репликации» данных будет то, что в случае синхронизации хранилища типа kv-v2 в целевом кластере при каждой синхронизации будут создаваться новые версии секретов. Фактически medusa import
выполняет операцию write и каждый раз создаёт новую версию секрета, даже если он не изменился. В итоге вы получаете совсем не идентичные данные в исходном и целевом кластерах: совпадают только последние версии секрета.
С одной стороны, использование Medusa или аналогов даёт гибкость в настройке и прозрачность решения. С другой — поддержка множества репликаций в разных местах и мониторинг безопасности и самих репликация являются очень нетривиальными задачами.
Как сделано в Stronghold
Первым этапом большой работы по расширению функционала Stronghold мы реализовали возможность репликации хранилищ секретов типа KV1/KV2. Зачем нам это понадобилось?
Мы хотели, чтобы доступ к реплицируемым секретам был только у самого защищённого хранилища.
Нас не устраивало «дублирование» версий KV, а API Vault просто не позволяет создать секрет желаемой версии.
В нашей практике часто встречались конфигурации, когда хотелось бы реплицировать только часть секретов, и репликация KV через API казалась неплохим выходом.
Клиенты часто задавали вопрос: «Если сделать кластер из 5 реплик Vault, то получим ли мы увеличение производительности в 5 раз?». Ответ на него — нет. А репликация данных в отдельные кластеры — один из способов увеличить производительность чтения.
Ну и такой подход прекрасно работает для миграции данных из Vault CE в Stronghold.
Репликация KV построена на архитектуре master-slave с применением pull-модели получения данных, где подчинённые slave-узлы сами опрашивают master-узел. Подробности о технической реализации этой фичи мы уже рассказывали в отдельной статье.

Вот что получают от репликации хранилищ секретов типа KV1/KV2 пользователи Stronghold:
Более безопасный и удобный доступ к секретам в больших компаниях с геораспределённой структурой.
Параметры репликации задаются дополнительными настройками при монтировании нового KV-хранилища. После завершения настройки локальное хранилище автоматически переводится в режим «только чтение», что гарантирует проведение всех изменений только в исходном хранилище.
Секреты в исходном и целевом хранилищах совпадают с точностью до версии, реплицируются в том числе и deleted-статусы промежуточных версий.
Если планируется высокая нагрузка на чтение из KV, то репликацию можно использовать для создания Performance-реплик KV.
Ближайшие планы по развитию Stronghold
В течение года мы планируем:
пройти сертификацию ФСТЭК России;
реализовать в Stronghold поддержку криптоалгоритмов ГОСТ;
выпустить версии с поддержкой производительной репликации и репликации для аварийного восстановления.
Сертификация ФСТЭК России. Нужна, чтобы компании могли использовать наш менеджер секретов в контурах с дополнительными требованиями к безопасности. Мы уже запустили процесс сертификации на соответствие Deckhouse Stronghold требованиям технический условий и приказа ФСТЭК России от 2 июня 2020 г. № 76 по 4-му уровню доверия. Кстати, Stronghold уже вошёл в состав новой сертифицированной ФСТЭК России версии Deckhouse Kubernetes Platform CSE 1.67 как функциональный модуль, реализующий базовые функции хранилища секретов.
Подробнее о том, зачем нужны лицензии ФСТЭК России, мы уже рассказывали в своём блоге.
Поддержка криптоалгоритмов ГОСТ. Как и в случае с сертифицированными ФСТЭК России продуктами, реализация этого функционала позволит использовать Stronghold компаниям, которым необходимо соответствовать более высоким требованиям по безопасности, в частности российским криптографическим стандартам ГОСТ 34.12-2018. В этом году мы добавим поддержку стандартов в Stronghold:
В продукте будет использоваться сертифицированная библиотека, которая предоставляет возможность шифрования с использованием алгоритмов «Магма» и «Кузнечик».
Исполняемый файл Stronghold будет использовать статическую или динамическую линковку библиотеки с импортом C-функций внутрь кода на Golang.
Производительная репликация (Performance Replication) поможет увеличить скорость чтения.
Репликация для аварийного восстановления (Disaster Recovery Replication) позволит повысить надёжность систем с помощью горизонтального масштабирования.
Performance-реплики нужны для создания дополнительных экземпляров Stronghold. Такую реплику можно использовать для разных сценариев:
Улучшение производительности: разгружает основной кластер, обрабатывая часть запросов, особенно в сценариях с большим количеством чтений.
Устойчивость к сбоям: повышает общую устойчивость системы за счёт того, что при выходе из строя основного экземпляра система продолжает функционировать с минимальными задержками.
Локализация данных: это полезно в географически распределённых системах, где можно развернуть реплику (или несколько реплик) ближе к месту нахождения пользователей, чтобы уменьшить задержки.
Стоит сказать, что performance-реплика поддерживает только операции чтения. Для записей данные должны перенаправляться к основному кластеру, который управляет всей информацией.
Disaster recovery-реплика синхронизирует данные из основного кластера, но не обрабатывает пользовательские запросы, пока её не активируют. Главное её назначение — обеспечение устойчивости системы к критическим сбоям и катастрофам. Обычно реплику для аварийного восстановления размещают в другой локации или отдельном дата-центре: так она функционирует независимо от основного кластера, что защищает её от локализованных сбоев, которые могут повлиять на основную инфраструктуру.
P. S.
Читайте также в нашем блоге: