Как стать автором
Обновить

Комментарии 16

Еще один вариант — хранить пароль в зашифрованном виде вместе с кодом приложения. Но и здесь не все в порядке — ведь мы получаем ещё один ключ шифрования, который нужно как-то безопасно хранить и периодически ротировать. Так что этот вариант тоже не подходил.


В статье намеренно опущен процесс получения токена аутентификации для API Vault, так как растягивать текст и все усложнять не хотелось. Для простоты будем считать, что токен аутентификации приходит в приложение через переменную окружения VAULT_TOKEN, неважно каким образом


Меня не покидает ощущение, что это взаимоисключающие параграфы.

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


Я постараюсь сейчас кратко описать в чём отличие. Второй вариант можно доработать средствами Kubernetes, чтобы в переменную окружения VAULT_TOKEN попадал автоматически созданный в Vault токен. Т.е. токены тоже будут жить недолго, если не продлевать их аренду. В случае с первым вариантом, ключ шифрования нужно будет менять руками или писать скрипт, который будет автоматизировать данный процесс. По сути, отличие лишь в том кто или что будет ротировать токен/ключ шифрования.


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

Например, можно было просто вынести пароли в переменные окружения CI/CD системы и не ломать голову. В этом случае у меня не было уверенности в том, что пароль не утечёт через добавление в CI-пайплайн строки echo $DB_PASSWORD

Это конечно самый базовый, но вполне рабочий вариант до которого многим хотя бы дорасти:

в Gitlab существует функция маскировки как раз для таких случаев (masked)

+ переменную можно сделать protected, чтобы просто так в какой-то произвольной ветке нет могли с ней пытаться что-то делать

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

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

  • Environment scope: (Optional) All, or specific environments.

  • Protect variable (Optional): If selected, the variable is only available in pipelines that run on protected branches or tags.

  • Mask variable (Optional): If selected, the variable’s Value is masked in job logs. The variable fails to save if the value does not meet the masking requirements.

https://docs.gitlab.com/ce/ci/variables/

Помимо Gitlab отмечу хорошее и достаточно простое yaml friendly решение https://github.com/mozilla/sops

Спасибо за уточняющий комментарий. Да. Protected и masked переменные могут помочь, если вы являетесь единственным Owner и Maintainer проекта. Вы очень верно использовали фразу дорасти. Мой pet-проект пока не дорос до такой оргструктуры, которая могла бы позволить использовать всю мощь protected и masked переменных в GitLab.


Про https://github.com/mozilla/sops не слышал. Почитаю. Спасибо.

Не соглашусь с тем, что это хоть и базовое решение, но подходит лишь для ситуации, когда вы единственный owner/maintainer.

Чаще всего так или иначе бывает человек или группа людей с привилегированным доступом, назовём её админы. Они должны иметь доступ к паролям прода. Это очень базовый сценарий, но допустим так.

И есть вторая группа, назовём её разработчики. Они не являются ни owner'ами ни maintainer'ами репозитория. Доступа к переменным не имеют. И даже в бесплатном Gitlab сейчас достаточно гибко настраиваются роли для работы с репозиторием, т.е. если разработчики не owner'ы и не maintainer'ы, то всё равно могут делать то, что нужно делать. Остальное это просто "хотим видеть то, что видели раньше, а сейчас почему-то нельзя", но из практики в 95+% это чепуха. Далее для, например, dev окружения (environment scope) переменные делаются не masked, а обычными и не protected. Теоретически для dev окружений конечно можно было бы и чуть ли не в коде переменные хранить, но есть высокая вероятность, что когда будете выкатывать на следующее окружение, то ошибетесь и будет плохо. Не советую так делать.

Многие проекты даже до этого уровня не дорастают, хотя это уже гораздо лучше хранения секретов в коде репы и никакого стороннего софта. Вот когда дальше уже нужно гибко настраивать роли по определённым группам и переменным, нужны логи обращения к ним и т.д, вот там нужен vault.

Но я бы рекомендовал начать с простого. Далее не проблема перенести переменные из Gitlab в Vault, если это вообще когда либо понадобится (многим нет), т.к. у Gitlab прекрасно работающий api для работы в том числе с переменными.

Хорошая рекомендация. Спасибо.

Вы заменили один секрет на другой.

Раньше для работы "всего" было достаточно иметь логин/пароль в postgres, теперь нужно иметь токен в vault, который постоянно выдаёт новые логины/пароли в postgres.

Вы решили задачу ротации паролей (раз в 5 минут? Для того, чтобы утырить базу паролей 5 минут более чем достаточно, для того, чтобы что-то отладить - нет), но решили её ценой усложнения.

Теперь у вас есть vault, у которого свой комплект секретов, плюс комплект секретов в vault.

Другими словами, если мне нужно слить базу данных ваших клиентов, то в старой схеме (у приложения логин/пароль) мне нужно было либо получить доступ на сервер СУБД, либо логин/пароль для доступа в базу. Модель безопасности совершенно понятна. Секрет при деплое и всё такое.

Теперь ваша схема: мне, как злоумышленнику дьявола, для того, чтобы слить вашу базу клиентов достаточно (одно из):

  • Получить доступ к СУБД

  • Получить логин/пароль приложения для доступа к СУБД (и успеть слить базу за 5 минут)

  • Получить токен приложения к VAULT'у

  • Получить доступ к VAULT-серверу

  • Получить логин/пароль VAULT'а для доступа к СУБД.

Было две точки уязвимости и один комплект секретов, стало 5 точек уязвимости и 3 комплекта секретов.

-1 багов сидело на стене...

Данным решением я не ставил задачу защититься от ситуаций когда злоумышленник смог проникнуть на сервера проекта. Если такое произошло, то проблема не в том где лежат пароли от базы, а в том что злоумышленник смог как-то проникнуть на сервера (при помощи эксплоита или социнженерии или взломал компьютер разработчика проекта). Эту проблему нужно точно решать другими средствами. У меня не было цели сделать из Vault "серебрянную пулю", которая будет защищать от всех видов атак.


Данным решением я решал задачу по устранению утечки паролей через окружение для разработки (исходный код и CI/CD среда). Т.е. пароли от базы будут находиться только в среде боевых серверов сервиса.


По поводу новых точек уязвимостей:


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


  • Получить доступ к VAULT-серверу — получить доступ недостаточно для совершения атаки, нужно как-то получить root-токен, чтобы вытянуть данные о паролях. Root-токен обычно не хранится на Vault-серверах. Как Вы собирались использовать данную точку уязвимости я, к сожалению, не понял.


  • Получить логин/пароль VAULT'а для доступа к СУБД. — в статье про это написано. Логин/пароль VAULT'а для доступа к СУБД периодически ротируется и не хранится в открытом виде. Так что я тут тоже не понял как Вы сможете их получить, если у Вас нет root-токена от Vault.


Если я могу, утащив логин/пароль от postgres стырить данные из postgres, то я ровно так же, утащив токен от vault'а, могу получить логин/пароль для доступа в postgres из самого vault'a. Я абсолютно не понимаю, что вы выиграли в этой ситуации, кроме job security.

Если я могу, утащив логин/пароль от postgres стырить данные из postgres, то я ровно так же, утащив токен от vault'а, могу получить логин/пароль для доступа в postgres из самого vault'a.

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


Я абсолютно не понимаю, что вы выиграли в этой ситуации, кроме job security.

Вы всё верно поняли. Решение в данной статье нацелено на job security и только на неё.

В этом случае у меня не было уверенности в том, что пароль не утечёт через добавление в CI-пайплайн строки echo $DB_PASSWORD

Вам достаточно в gitlab просто вынести пайплайн в отдельный репозиторий, это базовая возможность.

Тоже хороший вариант. Спасибо.

У vault предусмотрено резервирование или он теперь является единой точкой отказа?

У Vault конечно есть High Availability вариант. Есть подробный туториал как такое решение можно поднять руками: https://learn.hashicorp.com/tutorials/vault/ha-with-consul. На моём pet-проекте я использовал helm-чарт для поднятия Vault HA: https://www.vaultproject.io/docs/platform/k8s/helm

Какой даунтайм вы ожидаете при обновлении Vault?

Ожидаем нулевой даунтайм, если речь не идёт об обновлении до новой мажорной версии Vault.


Буду честен с Вами. В pet-проекте я ещё не обновлял Vault HA. Но если соберусь, то буду использовать rolling deploy в kubernetes. По сути, кубер не добавит в раздачу под с новой версией Vault пока я не произведу unseal нового пода, а в это время будут продолжать работать поды со старой версией. А когда я сделаю постепенно unseal всех подов с новой версией Vault, то кубер постепенно удалит поды со старой версией Vault. Тем самым приложения даже не заметят, что Vault обновился.


Если мы говорим про обновление до новой мажорной версии Vault, то даунтайм конечно не должен длиться дольше чем TTL учётных данных (в статье это 5 минут), чтобы не устроить коллапс в системе, когда поды приложений не смогут получить новые учётные данные для доступа в базу данных.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий