Секреты: хранилище и ротация
Запустил контейнер - процесс, поднятый из образа с подменённым видом на систему, - передав ему пароль базы через переменную окружения. Дальше три команды: описание контейнера показывает «DB_PASSWORD=СуперСекрет-2026» открытым текстом; выполнение команды внутри контейнера печатает то же самое; а вот файл с правами только для владельца при попытке прочитать от другого пользователя даёт «Permission denied». Три способа хранения - три разных уровня доступности для постороннего.
Стержень: секрет опасен не тем, что где-то лежит, а тем, сколько людей и процессов могут его прочитать и как быстро его можно сменить.
// Формулировки: «где хранить пароли?», «чем плоха переменная окружения?», «как устроена ротация?»
Где секрет виден и кому
Разложим по уровням, от худшего. Аргумент командной строки - худший вариант: список процессов машины читает любой её пользователь, я это проверял отдельно. Переменная окружения - лучше, но её видно в описании контейнера, в любом дампе состояния и любому, кто может выполнить команду внутри; плюс она наследуется всеми дочерними процессами.
Файл с правами только для владельца - следующий уровень: у меня попытка прочитать его от другого пользователя дала прямой отказ. Ещё лучше - файл, который положен во временную память, а не на диск, и исчезает при остановке. И верхний уровень - секрет вообще не хранится у сервиса: он приходит из отдельного хранилища на время работы и живёт минуты.
// Отдельная категория, о которой забывают: секрет в репозитории и в образе. Я проверял оба - и то и другое достаётся простыми командами: файл из истории репозитория читается по имени коммита, а из образа вытаскивается распаковкой слоёв. Удаление следующим коммитом или следующей строкой сборки не помогает ни там, ни там.
- переменная окружения
- видна в описании контейнера и наследуется потомками процесса
- файл с правами владельца
- права 600: читает только тот, от кого запущен процесс
- хранилище секретов
- отдельная система, выдающая значения по правам и на срок
Ротация: главный вопрос не «где», а «за сколько»
Правильный вопрос про секрет звучит так: если он утёк прямо сейчас, за сколько мы его сменим и что при этом сломается. Если ответ «за неделю и половина сервисов», то никакое хранилище не спасёт - утечка станет катастрофой независимо от того, где значение лежало.
Отсюда требования к схеме. Смена секрета не должна требовать выкатки, то есть установки новой версии сервиса: он либо перечитывает значение на ходу, либо получает его при старте нового экземпляра. В любой момент должны работать ДВА действующих значения сразу - иначе смена превращается в одновременный перезапуск всего, а это простой. И должен быть список: какой секрет где используется, иначе после смены что-нибудь отвалится через сутки.
// Идеальный случай - динамические секреты: хранилище само выпускает пару логин-пароль к базе на час и само её отзывает. Утёкшее значение при этом протухает само, а ротация превращается в ничегонеделание. Плата - зависимость от хранилища: оно становится критичным сервисом, падение которого останавливает выдачу новых доступов.
- ротация
- плановая смена секрета; мерится временем, а не фактом наличия процедуры
- два действующих значения
- старое и новое работают одновременно, чтобы смена шла без простоя
- динамический секрет
- выдаётся хранилищем на короткий срок и отзывается автоматически
Как секреты доезжают до приложения
Схем немного. Первая: значения лежат зашифрованными в репозитории, а расшифровываются на месте ключом, который есть только у среды выполнения. Плюс - всё в одном месте и под просмотром коллег; минус - утечка ключа расшифровки открывает всё сразу.
Вторая: в описании сервиса лежит только ССЫЛКА на секрет, а само значение забирается из хранилища при старте. Плюс - в репозитории нет ничего чувствительного, права выдаются на уровне хранилища; минус - зависимость от него в момент запуска.
// Третья: секрет монтируется файлом снаружи, средствами платформы. Тут важна деталь, которую любят спрашивать: смонтированный файл виден изнутри контейнера всем процессам, поэтому его права и владелец имеют значение. И общая для всех схем вещь: значение не должно попадать в логи. Скрипт с включённым выводом всех команд напечатает и его, а лог живёт дольше и доступен шире, чем сам секрет.
- шифрование в репозитории
- в коммите лежит зашифрованный текст, ключ расшифровки - только у среды выполнения
- ссылка вместо значения
- в конфигурации имя секрета, само значение приходит из хранилища
Как отвечать: «Где хранить пароль от базы и чем плоха переменная окружения?»
Переменная окружения не то чтобы плоха, она просто на среднем уровне защищённости, и надо понимать, на каком именно. Я проверял на стенде: значение из переменной видно в описании контейнера и печатается любому, кто может выполнить команду внутри; плюс оно наследуется всеми дочерними процессами и попадает в аварийные снимки. Файл с правами только для владельца уже лучше - попытка прочитать его от другого пользователя даёт прямой отказ. Ещё лучше - тот же файл во временной памяти, а не на диске. А верхний уровень - когда сервис получает динамический секрет из хранилища на час, и хранилище само его отзывает. Но главный вопрос не «где лежит», а «за сколько сменим, если утёк». Если смена требует выкатки всех сервисов и занимает неделю, то место хранения уже неважно.
Ответ выстраивает иерархию с измеренными различиями и переводит разговор на ротацию. Именно скорость смены отличает работающую схему от красивой.
На чём валятся
- −Передают пароль аргументом командной строки: его видит любой пользователь машины.
- −Считают, что удаление секрета следующим коммитом или строкой сборки убирает его.
- −Не умеют менять секрет без выкатки и потому не меняют его никогда.
- −Меняют секрет одним движением, без периода, когда работают два значения, и получают простой.
- −Печатают значение в лог: лог живёт дольше и доступен шире.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 7, остальные разбираются в тренажёре.
- Чем динамические секреты хранилища лучше статических?A)Учётка выдаётся под запрос и гаснет по срокуB)Они шифруются сильнее и потому не подбираются переборомC)Их не нужно передавать по сети между хранилищем и сервисомD)Их можно хранить в репозитории: значение всё равно бесполезно
показать ответ и разбор
+A)Учётка выдаётся под запрос и гаснет по сроку// разбор: Хранилище само создаёт в базе временную роль под конкретное приложение, отдаёт данные на срок в минуты или часы и по истечении удаляет её. Утёкшее значение живёт недолго, каждый доступ виден в журнале, а список того, что нужно менять после инцидента, резко сокращается. Плата — тесная интеграция с системой-источником и зависимость сервиса от доступности хранилища.
- Один токен с доступом ко всем секретам раздали всем сервисам «чтобы проще». Чем это плохо?A)Утечка одного сервиса открывает все секреты сразуB)Общий токен замедляет чтение секретов под нагрузкойC)Хранилище берёт плату за число разных токеновD)Секреты придётся чаще ротировать из-за общего токена
показать ответ и разбор
+A)Утечка одного сервиса открывает все секреты сразу// разбор: Один токен на все секреты для всех сервисов — это широкий радиус поражения: компрометация любого сервиса (или его логов/дампа) отдаёт атакующему сразу весь набор секретов. По least privilege каждый сервис аутентифицируется своей идентичностью и получает доступ только к нужным ему секретам (политики/scoped-роли в хранилище). Тогда утечка одного ограничена его собственными секретами.
- Приложению нужен пароль из хранилища секретов. Но чтобы забрать его, надо сначала как-то аутентифицироваться. Как решают эту «проблему нулевого секрета»?A)Зашить стартовый пароль от хранилища в образ приложенияB)Открыть хранилище без аутентификации из внутренней сетиC)Идентичностью нагрузки от платформы, а не паролемD)Передавать мастер-пароль хранилища аргументом запуска
показать ответ и разбор
+C)Идентичностью нагрузки от платформы, а не паролем// разбор: Secret-zero — проблема начального доверия: чтобы получить секреты, нужен какой-то первичный секрет для входа в хранилище. Решают идентичностью рабочей нагрузки: платформа (облако, Kubernetes) выдаёт сервису подписанную краткоживущую идентичность (workload identity, projected SA-token, instance identity), по которой хранилище его узнаёт и отдаёт секреты — без вечного пароля в образе. Так первичный секрет не хранится, а доказывается платформой.
- Хранилище секретов шифрует данные. Что даёт схема с мастер-ключом и ключами данных (envelope encryption)?A)Данные шифруются дважды одним и тем же ключом для надёжностиB)Данные шифрует ключ данных, а его — мастер-ключ в KMS; ротация дешёваяC)Мастер-ключ хранится рядом с данными для быстрого доступаD)Каждый секрет шифруется мастер-ключом напрямую
показать ответ и разбор
+B)Данные шифрует ключ данных, а его — мастер-ключ в KMS; ротация дешёвая// разбор: Envelope encryption — два уровня ключей: сами данные шифрует data key (DEK), а его, в свою очередь, шифрует мастер-ключ (KEK), который живёт в KMS и наружу не покидает. Плюсы: мастер-ключ изолирован в KMS (аппаратно, с аудитом), а ротация мастер-ключа не требует перешифровывать все данные — достаточно перешифровать компактные ключи данных. Так строят шифрование секретов, дисков, объектного хранилища.
- Что в эксплуатации называют секретами?A)Внутреннюю документацию компанииB)Пароли, токены и ключи, дающие доступ к системамC)Приватные репозитории с исходным кодомD)Персональные данные пользователей сервиса
показать ответ и разбор
+B)Пароли, токены и ключи, дающие доступ к системам// разбор: Секрет это то, чем система доказывает своё право на доступ. Главное правило — не хранить их в репозитории: git помнит всё, и удаление следующим коммитом ничего не исправляет, секрет придётся отзывать.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.