Хабрахабр

Управление секретами при помощи HashiCorp Vault

Как правильно хранить секреты? В репозитории, в системе деплоя или в системе управления конфигурациями? На личном компьютере, на серверах, а может в коробке под кроватью? А как управлять секретами, чтобы не допускать утечек?

В Авито два года активно используют HashiCorp Vault, за это время набили шишки, и прокачали опыт до уровня «Мастер». Сергей Носков (Albibek) — руководитель группы информационной безопасности платформы из Авито, знает ответ на эти вопросы и поделится с нами.

В статье всесторонне поговорим про Vault: что это такое, где и как используется в компании, как в Авито управляют секретами с помощью HashiCorp Vault, как используют Puppet и Kubernetes, варианты использования с Puppet и другими SCM, какие возникают проблемы, что болит у безопасников и разработчиков, и, конечно, поделимся идеями, как все исправить.

Что такое секрет

Любая конфиденциальная информация:

  • логин и пароль, например, к БД;
  • API-ключи;
  • ключ сертификата сервера (*.google.com);
  • ключ клиентского сертификата (партнёры, Yandex money, QIWI);
  • ключ для подписи мобильных приложений.

Всю информацию, которую мы хотим сохранить в тайне, мы называем секретом. Это создает проблему с хранением: в репозитории хранить плохо, в зашифрованном виде — нужно где-то держать ключи шифрования.

HashiCorp Vault — одно из неплохих решений проблемы.

  • Безопасно хранит и управляет ключами.
  • Заточен на мир микросервисов, так как сам по себе микросервис.
  • В HashiCorp Vault много сделано для аутентификации и авторизации доступа к секретам, например, ACL и принцип минимальных привилегий.
  • REST интерфейс с JSON.
  • Безопасность не идеальна, но на достаточно высоком уровне.

На мой взгляд, это достаточно удобный инструмент.

Что нового в HashiCorp Vault

Инструмент развивается и за последнее время в нем появилось много интересных фич: CORS-заголовки для GUI без посредников; встроенный GUI; нативная интеграция с Kubernetes; плагины для logical- и auth-бэкендов и фреймворк.

Большинство изменений, которые понравились лично мне, — это возможность не писать расширения и дополнения, которые будут стоять снаружи относительно инструмента.

До изменений приходилось поднимать дополнительный сервис, который стоит перед Vault, и проксирует все запросы: сначала запросы идут в сервис, потом — в Vault. Например, есть Vault, вы хотите его расширить — написать дополнительную логику или свой UI для автоматизации, который будет что-то автоматизировать. Риски безопасности намного выше, когда секрет проходит через несколько точек сразу! Это плохо тем, что в промежуточном сервисе может быть пониженный уровень безопасности, а через него идут все секреты.

Проблема курицы и яйца

Когда вы поднимаете вопрос хранения конфиденциальной информации и решаете шифровать, то, как только вы что-то зашифровали, ваш секрет смещается из места шифровки туда, где хранится ключ. Это происходит постоянно: как только вы сохранили куда-то секрет или изменили существующий, у вас появляется еще один и начинается заколдованный круг — где хранить секрет для доступа к секрету.

У безопасности есть еще одна часть — авторизация. В процессе авторизации проверяется, можно ли пользователю получить доступ именно туда, куда он запрашивает. Секрет доступа к секрету — это та часть безопасности, которая называется аутентификацией. Авторизация лишь частично решает проблему. В случае с Vault есть доверенная третья сторона, которая решает, выдать секрет или нет.

HashiCorp Vault в Авито

В Авито HashiCorp установлен в единственной большой инсталляции на всю сеть. У HashiCorp Vault много разных бэкендов. Мы используем бэкенд на базе Consul тоже от HashiCorp, потому что Vault умеет поддерживать собственную отказоустойчивость только через Consul.ь.

Когда запускается Vault, он все шифрует на каком-то ключе, и снова появляется проблема курицы и яйца: где хранить секрет, которым будут шифроваться все остальные секреты. Unseal — это способ не держать в одном месте мастер-ключ. В Авито мы настроили в опциях Unseal на 3 человека из 7. Чтобы избежать этой проблемы, в Vault предусмотрен составной ключ, для получения которого требуется несколько частей ключа, которые мы раздаём нескольким сотрудникам. Ключ поделен на 7 частей и можно принести любые из них. Если мы запускаем Vault, то чтобы он начал работать, обязательно должны прийти как минимум 3 человека и ввести свою часть ключа.

Он есть в виде Docker-контейнера и создает простые секретики, чтобы люди могли потрогать инструмент руками, освоиться. Мы собрали маленький тестовый Vault — песочницу для разработчиков, где они могут играть. В песочнице нет Consul и кластеризации, это просто файловая система, на которой Vault держит зашифрованные секреты, и маленький скрипт для инициализации.

Вот что мы сейчас храним в Vault:

  • Практически все секреты для микросервисов Kubernetes: пароли от баз, API-ключи, всё, что перечислено выше.
  • Секреты для выкладки на «железные» серверы и LXC.
  • Секреты для билдов CI/CD в TeamCity мы тоже кладем в Vault. Покрытие не 100%, но вполне приемлемое.
  • Ключи всех сертификатов: внутренний PKI, внешние CA, например, GeoTrust и подобных.
  • Общие секреты для команд.

Внутри себя Vault хранит всё только в JSON, это не всегда удобно и требует дополнительных действий от разработчика, поэтому в основном мы выкладываем секреты в виде файла.

Мы стараемся доставлять секреты в виде файлов.

Мы не говорим разработчику: «Иди в Vault, бери секрет!», а выкладываем на диск файл и сообщаем: «Разработчик, у тебя на диске появится файл, забирай секрет из него, а мы уже разберемся, как достать его из Vault и принести тебе».

Это метаданные для файловой системы, а поле data — это закодированная строка c самим секретом, которая станет содержимым файла. Мы приняли простое соглашение для полей JSON, в котором указываем, с какими правами выкладывать файл.

Puppet + Hiera + Vault

Практически во всей инфраструктуре Авито используется Puppet, им раскатываются все серверы.

Vault очень хорошо интегрируется с Hiera через дополнительный модуль, потому что в эту библиотеку идёт запрос ключ-значение, а Vault сам база ключ-значение, но со всеми функциями безопасности — с прозрачным шифрованием и возможностью выбора доступа к ключам. У Puppet есть удобный инструмент организации иерархии — Hiera.

Router backend — отдельный модуль Hiera, просто файлы на диске, в которых написано, куда Hiera должна идти за ключом — в Vault или в другое место. Поэтому первое, что мы внедрили — это Vault в Puppet, но с одним дополнением — у нас есть промежуточный слой, который называется Router backend.

Это не проблема Vault или нагрузки на него, а особенность работы самой Hiera. Он нужен, чтобы Hiera не ходила в Vault постоянно, потому что она идет всегда по всей иерархии. Поэтому если оставить только модуль для Vault без Router backend, Puppet-мастер будет очень долго собирать конфигурацию для Puppet-агента, так как будет проверять каждый ключ в Vault.

Именно он выдает секрет для доступа к секрету. Для Puppet проблема курицы и яйца решается за счёт того, что авторизующая сторона у нас — Puppet-мастер. Хост на Puppet-мастере уже авторизован своим сертификатом, который генерируется локально и пределов хоста не покидает. Puppet-мастер имеет доступ ко всем секретам сразу, но каждому хосту разрешено получать только тот, который ему предназначен. В принципе, секрет для доступа к секрету остается, но это уже не так критично.

Наш процесс выкладки нового секрета в Puppet состоит из следующих этапов.

  • Мы где-то берём секрет — нам его кто-то отдает или выкладывает.
  • Кладем секрет в Vault, с иерархией как в Hiera: /puppet/role/www/site.ssl.key.
  • Прописываем в манифест Puppet префикс, с указанием, что файл лежит в Vault и где его брать.
  • Прописываем в YAML для Hiera router путь в Vault и указание на backend, чтобы Hiera могла его найти.
  • Pull request через GIT в репозиторий манифестов.
  • Прогоняем или дожидаемся прогона Puppet-агента.

Puppet-агенты прогоняются у нас каждые 30 мин, поэтому приходится немного подождать, пока выкатится секрет. Проблем это не вызывает — мы выкладываем секреты не каждый день. Пока в дело не включается Kubernetes, накладных расходов немного и мы готовы выкладывать секреты в Vault руками с минимальной автоматизацией.

Дополнительным плюсом мы получаем «фишку» Hiera — секрет можно выкладывать сразу для группы хостов или в зависимости от роли хоста, которую мы выставляем в переменной role.

Если злоумышленник подменит факт на клиенте, Puppet-мастер отдаст ему чужие секреты. Единственная опасность: если у вас Puppet, и вы используете Hiera, не подставляйте в пути в шаблоны переменных что попало, потому что многие факты и переменные собираются на клиентской стороне. Обязательно проверяйте переменные: используйте только те, что Puppet-мастер запрещает определять на клиентской стороне.

Как быть с SCM без мастера?

Если вдруг у вас не Puppet, то, скорее всего, Ansible. Для Chef и других централизованных SCM свои решения — это плагин, который умеет обращаться к Vault. Предлагаю несколько вариантов, которые можно реализовать с Ansible.

Локальный агент

Локально для сервера генерируете токен, который фактически является паролем для доступа к Vault. Токен действует постоянно. Вы можете обновлять его или автоматизировать. С этим токеном в обращаетесь в Vault и забираете свои секреты.

Мы используем агента на нескольких отдельных серверах, где нет Puppet. Идея в том, что у вас на сервере, куда надо доставить секреты, крутится агент, который приходит в Vault, смотрит все секреты и выкладывает их в виде файлов.

Минусы:

  • Токен легко вписать в небольшой сегмент, но если у вас деплоятся несколько десятков серверов в день, придется для каждого сервера генерировать токен и прописывать политику. Это неудобно.
  • Токен надо обновлять.
  • Группировка серверов по роли, назначению или фактам, затруднена, её надо синхронизировать с Vault.

Транзитное шифрование

Vault имеет функцию transit encryption, суть которой в том, что Vault выступает сервером шифрования. Вы просто приносите ему открытый текст, а он на своем закрытом ключе, который есть только у него, шифрует и выдает закрытый текст. Дальше вы выбираете, кто этот закрытый текст сможет расшифровать.

Это — не HashiCorp Vault, а Ansible Vault. У Ansible есть сущность, которая тоже называется Vault. В Ansible есть уже готовый плагин для доcтавки секретов из Hashicorp Vault. Здесь нужно не перепутать, а секреты можно хранить как в первом, так и во втором. Когда вы накатываете Ansible, он от вашего имени идёт в Vault, расшифровывает секреты, которые в зашифрованном виде находятся в репозитории, и выкатывает в продакшен. Если вам дать личный доступ в Vault, то вы сможете расшифровывать секреты.

Но есть аудит: Vault умеет вести журнал активности о том, какой пользователь приходил, какой секрет читал, какой получил доступ. Здесь тоже есть недостаток — каждый администратор получает доступ к секретам. Этот вариант мне кажется неплохим. Вы всегда знаете, кто, когда и что делал с секретом.

Большой Недостаток № 1

Самый большой недостаток, который вызывает самую большую боль у нас — то, что в Vault нельзя никому делегировать полное управление какой-то частью данных. В Vault доступ к секрету осуществляется по путям, похожим на пути в UNIX — имена принято разделять слешами, и в результате получается «директория». Когда у вас есть такой путь, иногда вы хотите взять часть пути и отдать её на управление кому-то еще.

В Vault не получится этого сделать. Например, вы завели сертификаты, назвали /certs, и хотите отдать отдельным безопасникам, которые занимаются PKI. Вы не можете дать право выдавать права внутри этого префикса — чтобы безопасники сами могли раздавать права на сертификаты еще кому-то.

Как только вы дали право на выдачу прав, то выдали и возможность получить полный доступ ко всем секретам. В Vault нет возможности выборочно выдавать права на выдачу прав. Другими словами, вы не можете дать доступ на часть Vault.

У меня есть идея, как ее решить, позже про нее расскажу. Это одна из самых больших проблем.

Kubernetes

На РИТ++ я рассказывал про отдельную систему, которую мы реализовали для Kubernetes: она служит третьей стороной, ходит в API, проверяет доступ и потом запрашивает секрет в Vault.

9 появилась нативная поддержка Kubernetes. Сейчас наша система потеряла актуальность, потому что в Vault 0. Делает он это при помощи Service Account Token. Теперь Vault сам умеет ходить в Kubernetes и убеждаться, что доступ к секрету разрешён. С токеном можно также авторизоваться в Vault и получить секреты именно для вашего namespace. Например, когда у вас выкатился pod, там лежит специальный, подписанный и авторизованный для него JWT, предназначенный для запросов к API Kubernetes.

Правда, на каждый namespace надо будет заводить роль, то есть сообщать Vault, что есть такой namespace, в нём будет авторизация, и прописывать, куда ходить в Kubernetes. Все делается на уровне самого Vault. Это делается один раз, а дальше Vault будет сам ходить в API, подтверждать валидность JWT и выдавать свой собственный токен для доступа.

Правила Kubernetes

В плане имени сервисов и дополнительных метаданных мы доверяем разработчикам. Есть маленькая вероятность, что разработчики могут случайно или намеренно получить секреты других сервисов, которые крутятся в одном namespace, поэтому мы ввели правило: один сервис один namespace.

Заводите новый namespace со своими секретами. Новый микросервис? Граница безопасности в Kubernetes на данный момент — это namespace. Если в двух разных namespace нужен один секрет — копируем. Перейти через границу в соседний нельзя — там свои Service Account Token.

Они хранятся в etcd в Kubernetes в незашифрованном виде и могут «засветиться» в dashboard или при запуске kubectl get pods. В Kubernetes есть kubernetes secrets. Именно поэтому мы ввели два правила: запрещено использовать kubernetes secrets и запрещено указывать секреты в переменные окружения в манифестах. Если в вашем кластере отключена аутентификация в etcd, или если вы дали кому-то полный read-only-доступ, то ему видны все секреты. Если вы в deployment.yaml прописываете секрет в environment — это плохо, потому что сам манифест могут посмотреть все кому не лень.

Доставка Kubernetes

Как я уже сказал, мы должны как-то положить файл в Kubernetes. У нас есть какой-то секрет: сущность, пароль, который в JSON записан в Vault. Как его теперь превратить в файл внутри контейнера в Kubernetes?

Первый вариант доставки.

  • Мы заводим специальный init-container.
  • Он запускается с нашего образа.
  • В образе лежит маленькая утилита, которая с Service Account Token идет в Vault, забирает секрет и выкладывает его в Shared volume.
  • Для утилиты монтируется специальный Shared volume только в памяти TMPFS, чтобы секреты не проходили через диск.
  • Init-container идет в Vault, выкладывает в этот volume в виде файлов все секреты, которые найдёт по указанному пути.
  • Дальше Shared volume монтируется в основной контейнер, в котором он требуется.
  • Когда основной контейнер запустился, он сразу получает то, что надо разработчику — секреты в виде файла на диске.

Разработчику всего лишь надо запомнить путь, в котором лежит его секрет.

Мы используем примерно такой префикс:

/k8s/<cluster>/<namespace>/<service>/some_secret

В имя префикса заложены имя кластера, namespace и имя сервиса. У каждого сервиса свой секрет, в каждом namespace свой секрет.

К нему мы сейчас переходим в Авито, потому что с init-container у разработчиков естьпроблемы. Второй вариант — это свой собственный entrypoint. На схеме этот вариант справа.

Мы — можем, поэтому в каждом контейнере мы форсим наш специальный entrypoint. Собственный entrypoint не все могут себе позволить.

Кроме как в файлы, он кладет их еще в environment. Наш entrypoint делает то же самое, что init-container: идет в Vault с Service Account Token, берет секреты и выкладывает их. Вы получаете возможность запускать приложение как это рекомендуется концепцией Twelve-Factor App: приложение берет все настройки, в том числе, секреты, из переменных окружения.

Это не переменные окружения из deployment.yaml, а переменные окружения, которые выставил entrypoint в процессе работы. Переменные окружения не видно в манифестах и дашборде, поскольку их устанавливает PID 1(основной процесс контейнера) при запуске. Они не видны в dashboard, их не видно, даже если сделать kubectl exec в контейнер, потому что в этом случае запускается другой процесс, параллельный PID1.

Workflow

С организационной точки зрения, процесс идет следующим образом. Разработчик узнаёт от security champion или из документации, что ему нельзя хранить секреты в репозитории, а только в Vault. Тогда он приходит к нам и спрашивает, куда класть секреты — подаёт заявку в security на заведение префикса. В будущем можно создавать префикс без заявки, сразу при создании сервиса.

для него главное — time-to-market. Разработчик ждет, и это плохо, т.к. Никогда раньше разработчик не заводил init-container, но он вынужден разобраться и прописывать его в deployment.yaml (helm chart). Дальше он читает инструкции, разбирается с длинными файлами — «вставь туда ту строку, вставь сюда эту строку».

Commit -> deploy -> feel pain -> fix -> repeat

Дополнительно накладывается то, что каждая выкатка в TeamCity ещё может встать в очередь. Коммитит, ждет, пока TeamCity выкатит, видит ошибки в TeamCity, начинает испытывать боль, пытается что-то поправить, снова испытывает боль. Иногда разработчик не может разобраться сам, приходит к нам, и мы разбираемся вместе.

В основном разработчик страдает из-за собственных ошибок: неправильно задал init-container или не дочитал документацию.

Безопасник получает заявку, в которой всегда мало информации, и мы все равно выясняем недостающие вопросы: выясняем имена кластеров, namespace сервиса, так как разработчик не указывает их в заявке и даже не всегда знает, что это такое. Со стороны Security тоже есть проблемы. Когда всё выясним, создаём политики и роли в Vault, прописываем политики группам и вместе с разработчиком начинаем выяснять, где и почему он ошибся, и вместе читаем логи.

Они разрабатывают штуку, которая все генерирует за разработчика, в том числе entrypoint. Проблему помогает решить юнит «Архитектура» сокрытием от разработчика deployment.yaml. Благодаря тому, что мы подставляем свой entrypoint, мы можем использовать его не только для доставки секретов, но и для других вещей, которые может понадобиться делать при старте.

Очевидные проблемы с секретами Kubernetes.

  • Очень сложный workflow и для разработчика, и для безопасника.
  • Нельзя никому ничего делегировать. У безопасника есть полный доступ в Vault, а дать частичный доступ невозможно (см. Большой недостаток № 1).
  • Возникают трудности при переезде разработчиков из кластера в кластер, из namespace в namespace, когда нужны расшаренные секреты, потому что изначально предполагается, что в разных кластерах разные секреты.

Мы говорим: «Зачем вам в dev-кластере секреты production? Заведите тестовый секрет, ходите с ним!» В итоге появляются копи и секретов, которыми сложно управлять. Если секрет поменялся, надо про это не забыть, пойти и поменять везде, и пока нет возможности определить, что это один и тот же секрет, кроме как по имени сервиса.

Идея: Kubernetes KMS

В новых версиях Kubernetes появилась подсистема KMS — Key Management Service — новая возможность Kubernetes по шифрованию секретов. В v1.11 она была с состоянии alpha, в v1.12 её перевели в beta.

Картинка с сайта проекта провайдера KMS для Vault, и на ней есть ошибка. Если найдете — напишите в комментариях.

Смысл KMS в том, чтобы устранить один-единственный недостаток — нешифрованное хранение данных в etcd.

KMS, как Ansible, умеет вот что.

  • Сходить куда-нибудь, зашифровать родной нативный секрет Kubernetes и положить его в зашифрованном виде.
  • По необходимости доставить в pod, расшифровать и выложить в расшифрованном виде.

Разработчики написали специальный сервис, который это делает, используя transit encryption. Идея выглядит рабочей, но важно помнить, что секреты перестают находиться только под контролем Vault и уходят куда-то ещё, в зону ответственности администраторов Kubernetes.

Минусы KMS.

  • Децентрализация хранения вынос из Vault в Kubernetes (etcd). Секреты становятся неподконтрольными Vault, а он хорош как централизованное хранилище секретов. Получается, что половина секретов в Vault, а половина где-то ещё.
  • Kubernetes-only решение. Если у вас Kubernetes-only инфраструктура, вы поднимаете Vault, и почти не думаете, что там хранится, т.к. в нём лежат только ключи шифрования, которыми вы правильно управляете — регулярно ротируете и т.п… Сами секреты при этом находятся в Kubernetes, и это удобно.
  • Сложно делить секреты между кластерами. Для каждого нового кластера нужно заводить всё по новой, копировать секреты как в случае с единым Vault может не получиться.

Плюсы KMS.

  • Нативная поддержка в Kubernetes, включая скрытие при показе environment.
  • Авторизация в зоне ответственности Kubernetes.
  • Практически не надо поддерживать Vault.
  • Ротация ключей из коробки.

CI/CD: TeamCity

В TeamCity все просто, потому что JetBrains написали плагин, который сам умеет прописать у себя секреты для доступа к секрету, зашифровать их средствами TeamCity, а потом там же в качестве параметра где-нибудь в шаблоне подставить через проценты. В этот момент агент TeamCity сам сходит в Vault, заберёт секрет и принесёт в билд в виде параметра.

AppRole заводится на каждый проект — настройки тоже содержат секрет (данные для AppRole), но он вводится в режиме write-only — прочитать его потом TeamCity не позволяет. Некоторые секреты нужны при деплое, например, миграции БД или оповещения в Slack.

В результате секрет либо не «проезжает» через диск совсем, либо вычищается с диска средствами TeamCity. TeamCity сам заботится о том, чтобы при попадании секрета в логи билда, он автоматически маскировался. В результате вся безопасность секрета хорошо обеспечена самим TeamCity и плагином, и дополнительных танцев с бубном не требуется

CI/CD не TeamCity?

Вот основные вопросы, над которыми надо подумать, если в качестве CI вы используете другую систему (не TeamCity).

  • Изоляция: ограничить область видимости секрета проектом, командой и т.д.
  • Кто авторизует доступ к секрету.
  • Исключить возможность просматривать секрет авторизующей стороны.
  • Отдельным этапом билда импортировать секрет в файлы.
  • Почистить за собой.

В результате, скорее всего, вы напишете для своей CI/CD что-то очень похожее на плагин TeamCity. Авторизующей стороной здесь будет выступать, скорее всего, CI/CD, и именно она будет решать, можно ли этому билду иметь доступ к этому секрету, и по результатам отдавать или не отдавать сам секрет.

Важно не забывать чистить результаты билда по окончании сборки, если они выложились на диск, либо обеспечить, чтобы они находились только в памяти.

Сертификаты

С сертификатами ничего особенного — мы используем Vault в основном для их хранения.

У нас единый внутренний PKI… Корневой CA и CA второго уровня существуют отдельно, а CA третьего уровня мы уже управляем через Vault. Vault имеет специальный PKI backend для выпуска сертификатов, в котором можно заводить Certificate Authority и подписывать новые сертификаты. Формат хранения сертификатов собственный, подходящий для хранения отдельно закрытого ключа и собственно сертификата. Для хранения выпущенных сертификатов любого уровня, включая сертификаты подписанные внешними CA, мы используем отдельный префикс, и кладём туда почти все действующие сертификаты в целях учёта и мониторинга.

Резюме

Слишком много ручной работы для безопасника, слишком большой порог вхождения для разработчика и нет встроенных средств для делегирования, хотя очень хочется…

Дальше начинаются мечты. Как быть?

Идеи: как сделать лучше

Как можно избавиться от кучи копий секрета?

Доставка master-slave

У нас есть master-секрет и специальный демон, который ходит, смотрит секрет и его метаданные, выкладывает куда надо, получается slave-секрет. По пути, куда демон выложил slave, ничего нельзя менять руками, потому что демон придет и заново выложит master-секрет поверх slave.

Оказалось, что возникают проблемы с правами доступа: неизвестно, как права доступа проверять — как в Linux или нет, с родительскими путями, с переходами между точками монтирования. Сначала мы хотели сделать механизм симлинков, чтобы просто указать: «Вот этот секрет ищи там!», как в Linux. Слишком много неоднозначных моментов и шансов ошибиться, поэтому от симлинков мы отказались.

Авторизация на основе владения

Второе, что мы хотим сделать — определить владельца для каждого секрета. По умолчанию секрет принадлежит тому, кто его создал. При необходимости можно расширить зону ответственности до юнита, выдав группу-владельца.

Когда мы научимся делегировать, мы выдадим владельцу права на секрет, и он сможет делать с секретом, что хочет.

  • Выкладывать в k8s — генерируется политика, создаётся slave-копия.
  • Выкладывать на сервер — генерируется политика, создаётся slave-копия.
  • Выкладывать в CI/CD — ...
  • Передавать другому владельцу.
  • Давать новый доступ, генерировать новые ACL.

Сейчас за все секреты и безопасность отвечаем мы, а хотим переложить ответственность на создателя. Безопасность не пострадает, потому что человек, который пришел к нам с запросом хранения секрета, понимает, что ему нужно безопасно хранить секрет и осознает свою ответственность.

Получается, что владелец сам всем управляет, не надо подавать заявки, нужный префикс можно занять самостоятельно, секреты тоже можно создавать и удалять самому. Поскольку это владелец секрета, то для варианта доставки master-slave он мог бы выбирать, куда и в каком формате ему доставлять секрет.

Делегирование через шаблоны ACL

Политика доступа Access Control List в Vault поделена на две части:

  • Access Control List в классическом представлении, который описывает доступ к префиксу, по какому пути можно читать и писать, по какому только читать и т.д.
  • При создании ACL внутри можно прописывать звездочку на конце, что означает «этот префикс, и все, что ниже него». Префикс можно присваивать отдельной операцией, отдавать пользователю или группе, то есть привязать на несколько разных сущностей.

Сейчас только администратор Vault может менять ACL. Получив доступ на такую ACL, можно внутрь прописать все, что хочется, например, path “*” , и получить полный доступ. Это суть Большого недостатка № 1 — невозможно запретить менять со держимое ACL.

Мы хотим задавать ACL готовым шаблоном, который содержит путь и плейсхолдеры, на которых разрешено генерировать новые ACL по этому шаблону.

Пример

Ниже желтым шрифтом записан путь готовой стандартной ACL от Vault и дальше разрешённые действия на этот путь. Мы ее рассматриваем, как ACL на разрешение менять другую ACL внизу, которая задана в виде шаблона.

Например, давать доступ только на чтение в конкретный кластер, namespace, сервис, но не менять поле capabilities.
Мы хотим делегировать доступ на /k8s, разрешаем генерировать только такие шаблоны.

Дополнительно мы хотим давать разрешение привязывать эти ACL и выдавать разные права.

При шаблонизации он запустил команду $ vault write policy-mgr/create/k8s-microservice .... Мы применили шаблон для выдачи прав разработчику. Права проставились автоматически, создалась политика с именем /k8s/some-srv — это просто имя ACL, которое можно генерировать по шаблону.
И в результате получили ACL, в которой указано cluster=prod, namespace=..., service=… и т.д.

Теперь человек сам ответственен за свой префикс: он управляет всеми секретами, генерирует по шаблону ACL, может присваивать ACL тем, кому захочет. В результате разработчик по своему усмотрению через наш плагин присваивает эту ACL кому захочет, а сам становится владельцем, может ей управлять, как секретом: удалять, отдавать и отнимать у пользователей и групп. Естественно, мы тоже можем его ограничить.

Они представляют собой отдельный сервис, очень похожий на тот, о котором я говорил в начале, и работают почти точно также. Вся магия работает при помощи новой сущности Vault — плагинов. Плагины запускаются «сбоку» от Vault, причём запускает их основной процесс Vault. Единственное важное отличие — они не прокси. За счёт этого все запросы идут не через сервис, а в Vault, который уже сам взаимодействует с плагином, отправляя ему проверенный и очищенный запрос.

Писать их лучше всего на Go, что достаточно просто, т.к. Про плагины, как они устроены и как их писать, можно почитать на сайте Vault. Vault общается с плагином по grpc, запускает его как сервис, но не надо пугаться, вы его не касаетесь — во фреймворке уже все заложено. для Go есть фреймворк. Вы просто пишете, более-менее стандартное REST приложение, в котором указываете endpoints, к ним даете готовые функции, хэндлеры, на которых будет логика.

Плагин — это отдельный сервис. Не бойтесь, что вы что-то сломаете в основном Vault. Vault просто перезапустит плагин и будет работать дальше. Даже если плагин у вас запаниковал и упал, работу Vault это никак не нарушит.

Безопасность запуска плагинов обеспечена. Кроме того, есть дополнительные настройки для самого плагина: он обязательно проверяет хэш-суммы, чтобы никто не подменил бинарник.

Полезные ссылки:

Приходите поделиться своим опытом или посмотреть на других. Про DevOps и безопасность, CI/CD, k8s, Puppet и всё в таком духе будем говорить на HighLoad++ (ближайший в Питере в апреле) и на DevOpsConf. Чтобы не забыть, подпишитесь на блог и рассылку, в которой мы будем напоминать о дедлайнах и собирать полезные материалы.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть