Где хранить API-ключи: Vault, KMS или Kubernetes
Разбираем, где хранить API-ключи для LLM, SaaS и внутренних сервисов: сравниваем Vault, KMS и Kubernetes по ротации, аудиту и рискам.

Почему хранение ключей быстро становится проблемой
Пока сервис один, хранение API-ключа кажется простой задачей. Его кладут в переменную окружения, в CI или в закрытый файл, и все работает. Проблемы начинаются позже, когда появляются несколько сервисов, test и prod, фоновые задачи, временные джобы и аварийные скрипты. Один и тот же секрет быстро оказывается в пяти-шести местах.
После этого любая утечка перестает быть локальной. Если один ключ используют веб-приложение, воркер и ночной cron, компрометация в любой точке открывает доступ ко всей цепочке. Отозвать такой ключ тоже непросто: вы не только закрываете дыру, но и рискуете остановить часть продового трафика.
Обычно команда упирается не в сам факт хранения, а в ротацию. Ручная замена секрета в проде почти всегда задевает соседние системы. Нужно обновить CI, перезапустить контейнеры, проверить фоновые задачи, не забыть про старые инстансы и служебные скрипты. Достаточно пропустить один сервис, и часть запросов пойдет со старым ключом, а часть - с новым. Дальше начинаются ошибки в логах и ночной поиск, где осталась старая версия.
Типичный сценарий выглядит скучно, но бьет больно. У команды есть ключ к внешнему API, и его используют три приложения. Разработчик меняет значение в Kubernetes, но забывает про отдельную джобу. Веб-интерфейс продолжает работать, а фоновые задачи тихо падают еще несколько часов. Такие сбои часто принимают за случайность, хотя причина у них одна.
Без журнала чтений и изменений разбираться еще тяжелее. Команде нужен не просто факт хранения секрета, а ответы на простые вопросы: кто читал значение, кто его менял, когда это произошло и что было до этого. Это нужно и во время инцидента, и после ухода сотрудника, и при обычной диагностике, когда расходы по внешнему API внезапно выросли.
Общий токен для всех стирает границы ответственности. Если один секрет используют все сервисы и все инженеры, вы не поймете, какой сервис дал лишнюю нагрузку, откуда пришла утечка и какую часть доступа надо отзывать. Приходится менять все сразу, а это уже прямой риск для продакшена.
В какой-то момент команда почти всегда приходит к одному и тому же набору требований: нужен один источник истины для секрета, понятная ротация без ручной беготни по сервисам, нормальный аудит чтений и изменений и отдельные права для разных сервисов и людей. Пока этого нет, хранение ключей остается постоянным источником сбоев.
По каким признакам сравнивать варианты
Vault, KMS и секреты в Kubernetes лучше сравнивать не по длинным спискам функций, а по рабочим сценариям. Один и тот же подход может быть удобным для одного сервиса и совсем неудачным для команды, у которой десятки интеграций и частая смена доступов.
Сначала разберите роли. Кто создает секрет, кто заносит его в систему, кто читает в рантайме и кто может ночью отозвать доступ после инцидента. Если разработчики сами заводят ключи у провайдеров, а платформа только выдает их приложениям, требования будут одни. Если все делает отдельная DevOps или security-команда, схема будет другой.
Дальше смотрите на ротацию. Ключ, который меняют раз в год вручную, и токен, который нужно обновлять часто, требуют разного подхода. Vault обычно удобнее там, где много короткоживущих секретов и автоматической ротации. KMS чаще выигрывает в более простой облачной схеме, когда секретов немного и доступ уже завязан на IAM. Kubernetes Secret удобен для доставки значения в pod, но сам по себе не закрывает весь жизненный цикл секрета.
Есть еще один вопрос, который часто пропускают: где лежит исходное значение секрета. Оно может жить в CI, на ноутбуке инженера, в панели провайдера, в GitHub Actions или во внешнем хранилище. Именно это место нередко оказывается самым слабым звеном. Красивый способ доставки секрета в приложение не помогает, если первоисточник никто не контролирует.
После инцидента важнее всего журнал действий. Вы должны быстро понять, кто прочитал секрет, какой сервис использовал его последним, когда значение заменили, кто выдал или отозвал доступ и что система сохранила для разбора. Аудит нужен не только ИБ. Он экономит часы, когда сервис вдруг начинает ходить в чужой аккаунт провайдера или старый job продолжает стучаться со старым ключом.
Еще один хороший критерий - число ручных шагов при отзыве доступа. Если инженеру нужно отдельно удалить секрет, перезапустить deployment, обновить переменные в CI и написать в чат команде, схема уже хрупкая.
Это особенно заметно там, где команда ходит к нескольким внешним API и моделям. Чем больше провайдеров и ключей, тем сильнее каждая ручная операция бьет по стабильности. Нормальная схема не только хранит секрет, но и делает замену, отзыв и проверку понятными за несколько минут.
Когда Vault подходит лучше всего
Vault имеет смысл там, где секретов много, они часто меняются, а доступ к ним нужен разным сервисам и командам. Если ключи лежат отдельно от кластера, репозитория и CI, вы снижаете риск простой ошибки: секрет не попадет в образ, values-файл или лог сборки просто потому, что его там не должно быть.
Такой подход особенно полезен в компаниях, где один сервис ходит в несколько внешних API, второй работает с базой, а третий вызывает LLM-шлюз. В этой схеме удобно держать чувствительные данные в одном месте и выдавать их по понятным правилам, а не разносить по Kubernetes Secret, переменным окружения и ручным настройкам.
Где Vault выигрывает
Главный плюс Vault - короткоживущие учетные данные. Вместо постоянного ключа сервис получает доступ на ограниченное время, иногда на час, иногда на несколько минут. Если токен утек, окно риска заметно меньше. Для продакшена это часто важнее, чем сам способ хранения.
С ротацией у Vault тоже все проще. Ее можно запускать по сроку действия или по событию: сотрудник ушел из команды, в логах нашли подозрительный доступ, провайдер попросил срочно заменить ключ, сервис переехал в другой контур. Вручную повторять такую операцию в десяти местах тяжело. Через Vault процесс остается в одном месте.
Сильная сторона Vault - и аудит. Обычно видно не только факт изменения секрета, но и кто его читал, когда это произошло и откуда пришел запрос. Для команды, которая регулярно разбирает инциденты или готовит проверки доступа, это уже не приятный бонус, а рабочий инструмент.
Хороший пример - набор микросервисов в Kubernetes. Один сервис берет токен к платежному API, другой использует ключ к модели, третий получает доступ к очереди. Если все это живет в Vault, можно выдать каждому сервису только тот минимум, который ему нужен, и быстро отозвать доступ без массовой переконфигурации.
Но у Vault есть цена. Это отдельный важный сервис, за которым нужно следить: обновлять, резервировать, настраивать политики, хранить мастер-ключи, проверять отказоустойчивость и регулярно смотреть аудит. Если команда пока не готова обслуживать еще один критичный компонент, Vault может оказаться тяжелее самой задачи. Он лучше работает там, где ротация, разделение прав и аудит уже стали ежедневной потребностью.
Когда KMS проще и понятнее
KMS обычно выбирают команды, которые уже живут в одном облаке и не хотят добавлять новый слой инфраструктуры. Если сервисы, базы данных, очереди и права доступа уже завязаны на IAM, KMS дает самый короткий путь: выдали роли, включили шифрование, начали работать.
Его сильная сторона не в том, что он хранит все секреты лучше остальных, а в том, что он хорошо решает задачу с ключами шифрования. Через KMS удобно шифровать токены, параметры и конфиги, которые потом лежат в другом месте: в parameter store, базе, object storage или CI/CD. Для многих команд этого уже достаточно, особенно если секретов немного и схема доступа несложная.
Ротацию в KMS обычно настраивают быстро. Во многих облаках это делается одной политикой, после чего сервис сам меняет материал ключа по расписанию. Это проще, чем поднимать Vault, продумывать политики, следить за unseal и поддерживать отдельную схему отказоустойчивости.
С аудитом у KMS все тоже довольно прозрачно. Он почти всегда пишет события в журнал облачных действий: кто запросил расшифровку, какой сервис использовал ключ, когда изменили политику доступа. Для проверок и разборов этого часто хватает без отдельной лог-сборки.
KMS хорошо подходит, если у вас один основной облачный провайдер, немного сервисов, предсказуемые права доступа и аудит уже строится вокруг облачных журналов. В такой ситуации он часто закрывает задачу быстрее, чем отдельное хранилище секретов.
Но есть важное ограничение: KMS не заменяет нормальную систему для выдачи секретов приложениям. Он не слишком удобен как место, откуда приложение само получает десятки секретов с версиями, TTL и тонкими политиками чтения. На практике схема чаще выглядит так: KMS защищает ключи шифрования, а сами секреты живут в secret manager, Vault или в Kubernetes с включенным шифрованием etcd.
Простой пример: у команды три внутренних сервиса, один CI-пайплайн и пара API-ключей к внешним провайдерам. Если все это работает в одном облаке, KMS вместе со штатным secret manager обычно закрывает задачу быстро и без лишней операционной нагрузки. Если сервисов уже двадцать, секреты часто меняются, а читать аудит нужно по каждому обращению, одного KMS обычно мало.
Что реально дают секреты в Kubernetes
Секреты в Kubernetes хороши там, где задачу нужно решить быстро и без лишних слоев. Приложению нужен токен, job нужен пароль к базе, cron-задаче нужен API-ключ для ночной выгрузки. Secret уже рядом с workload, и команда может передать значение в pod через переменные окружения или файл.
На повседневной работе это удобно. Один сервис берет ключ к внешнему API, другой использует отдельный токен, cronjob запускается раз в ночь с тем же способом доставки. Для команды схема понятная: настроил манифест, выдал права, задеплоил.
RBAC тоже дает реальную пользу. Можно ограничить доступ по namespace, service account и ролям, чтобы один сервис не видел секреты другого. Если кластер настроен аккуратно, это уже неплохая внутренняя изоляция.
Но у этого удобства есть предел. Kubernetes хорошо отвечает на вопрос, как передать секрет в pod. Намного хуже он отвечает на вопрос, где хранить исходное значение, кто менял его месяц назад и как быстро отозвать доступ сразу у всех потребителей. Если использовать только Kubernetes Secret, очень легко получить просто набор копий без нормального учета.
Есть и еще один практический минус. Если секрет попал в переменную окружения, приложение часто не подхватит новую версию без перезапуска. Значит, одна только запись нового значения в Kubernetes не гарантирует, что сервис действительно начал работать с ним.
Поэтому секреты в Kubernetes лучше воспринимать как способ доставки в рантайм, а не как единственный центр хранения, ротации и аудита. Для небольших систем этого иногда хватает. Для команды с несколькими средами, внешними провайдерами и регулярной ротацией - уже редко.
Как выбрать схему по шагам
Выбор обычно ломается не на технологии, а на учете. Сначала соберите полный список секретов: API-ключи, токены, пароли интеграций, webhook secrets, ключи для CI и служебных аккаунтов. Рядом с каждым секретом запишите владельца, место использования и что сломается при его отзыве.
После этого разделите среды. Prod, test и песочница не должны жить в одном наборе доступов, даже если команда маленькая. Если тестовый сервис может читать продовый секрет, схема уже плохая, даже если сам секрет лежит в хорошем инструменте.
Дальше полезно пройтись по простому порядку:
- Определите, где секрет создается и кто имеет право его менять.
- Назначьте роль, которая запускает ротацию.
- Назначьте того, кто проверяет, что новый ключ реально подхватили сервисы.
- Убедитесь, что история чтения, изменения и отзыва хранится в одном понятном месте.
- Проведите пробный отзыв одного не самого важного ключа без остановки сервиса.
После этого выбор обычно проясняется сам. Если секретов мало, все живет в одном облаке, а аудит строится вокруг IAM, KMS часто оказывается самым простым вариантом. Если секретов много, ротация сложная, команд несколько и нужны гибкие политики доступа, удобнее обычно Vault. Kubernetes Secret полезен как способ выдать значение в pod, но редко подходит как единственный центр учета.
Проверьте историю доступа отдельно. Нужен не просто факт хранения, а ответы на три вопроса: кто читал секрет, кто менял его и кто отозвал старую версию. Если эти события размазаны между логами кластера, облака и CI, разбор инцидента затянется.
Небольшой пример. У команды есть сервисы в Kubernetes, несколько внешних API и отдельный доступ к api.rullm.com для LLM-запросов. В такой схеме часто удобно держать источник секретов в Vault или KMS, а в Kubernetes только выдавать их приложениям. Так проще разделить prod и test, вести журнал доступа и менять ключ без ручного копирования по namespace.
Самая полезная проверка простая: отзовите один ключ в рабочем сценарии и посмотрите, что произойдет. Если команда не может быстро сделать это, понять по логам, кто использовал старое значение, и вернуть сервис в норму без простоя, схему еще рано считать рабочей.
Пример для команды с несколькими сервисами
Представим команду, у которой есть чат-бот поддержки, ночные batch-задачи и две среды: test и prod. Бот ходит во внешние API и в LLM-шлюз, batch-процессы забирают файлы из хранилища и отправляют отчеты в другие сервисы. Секретов быстро становится много: токены провайдеров, доступы к базе, ключи для очередей и учетные данные для резервного копирования.
Самая частая ошибка здесь проста: test и prod живут слишком близко. Разработчик берет рабочий ключ, чтобы быстро проверить гипотезу, и через неделю этот ключ уже лежит в переменной окружения, в CI и у пары старых job. Потом никто не помнит, где именно он используется.
В более здоровой схеме роли разделены. Vault хранит внешние API-ключи и другие чувствительные секреты. Kubernetes не становится главным хранилищем, а получает значения только на запуске pod или через контролируемую синхронизацию. KMS шифрует параметры среды в CI, резервные копии и конфиги, которые все же приходится хранить вне Vault.
На практике это выглядит просто. В Vault лежат отдельные секреты для test и prod с разными политиками доступа. Чат-бот в test получает только test-значения и не видит продовый контур вообще. Kubernetes отдает приложению только тот секрет, который нужен его service account. KMS закрывает шифрование backup-файлов и служебных параметров, попадающих в пайплайн или хранилище артефактов.
Такая схема удобна не потому, что она сложнее, а потому что в ней меньше путаницы. Разработчик смотрит в одно место, когда ему нужен доступ. Платформа видит, какой сервис запросил секрет. ИБ получает внятный след: кто читал значение, кто менял политику, кто запускал расшифровку.
Хорошая проверка для такой архитектуры - смена подрядчика. Если внешняя команда поддерживала batch-задачи и имела доступ к нескольким провайдерским ключам, администратор за один проход отключает их роль в Vault, отзывает права сервисного аккаунта в Kubernetes, ротирует нужные ключи у провайдеров и при необходимости пересобирает зашифрованные параметры через KMS. Не нужно искать токены по wiki, старым Helm values и чужим ноутбукам.
Для команды с несколькими сервисами это обычно разумный компромисс: Vault отвечает за хранение и выдачу секретов, Kubernetes - за доставку в рантайм, KMS - за шифрование там, где без файлов и бэкапов не обойтись.
Ошибки, которые обходятся дорого
Самые дорогие сбои обычно выглядят буднично: старый .env в архиве, общий токен для десяти сервисов, забытая ротация после замены ключа у провайдера. Проблема редко начинается с атаки. Намного чаще команда сама делает секреты слишком удобными, а потом теряет над ними контроль.
Одна из самых частых ошибок - хранить рабочие ключи в .env и считать задачу закрытой. Такой файл быстро попадает в архивы, дампы, резервные копии и старые образы контейнеров. Даже если вы удалили ключ из репозитория, он может жить в бэкапах еще очень долго.
Следом идет лишнее копирование. Один и тот же ключ разносят по множеству namespace и сервисов, хотя они не делят один контур доступа. Так команда сама увеличивает радиус проблемы: хватает одной утечки в тестовой среде, чтобы под угрозой оказался прод.
Есть и более тихая поломка: провайдер уже выдал новый ключ, а приложение все еще ходит со старым значением из памяти или старого pod. В итоге часть запросов падает, часть уходит в retry, а команда долго ищет причину не там. Ночью такие ситуации особенно неприятны, потому что выглядят как случайный сетевой сбой.
Часто проблемы повторяются в одних и тех же местах:
- CI получает доступ ко всем секретам сразу, потому что так проще настроить пайплайн.
- администратор вручную читает секрет для проверки, но журнал потом никто не смотрит.
- секрет обновили в хранилище, а приложение не перечитало его и продолжает работать со старым значением.
- один и тот же токен используют в dev, stage и prod.
Все это кажется мелочами, пока не нужно срочно отзывать доступ. В этот момент становится видно, насколько схема зависит от человеческой памяти.
Короткий чек-лист перед запуском
Перед отправкой схемы в прод стоит пройтись по пяти проверкам.
- У каждого секрета должен быть владелец. Не "команда в целом", а конкретная роль или человек.
- Ротация должна проходить без входа на сервер руками. Если инженер меняет файл через SSH и перезапускает сервис вручную, схема уже хрупкая.
- Журнал должен хранить не только изменение, но и чтение и отзыв секрета.
- Тестовый отзыв секрета нужно провести заранее в среде, близкой к боевой.
- У команды должен быть короткий runbook: кто принимает решение об экстренном отзыве, где лежит запасной секрет и как проходит откат.
Эти проверки одинаково важны для Vault, KMS и Kubernetes Secret. Инструмент можно заменить позже. Плохие правила работы с секретами потом исправлять намного дольше.
Что делать дальше
Вопрос "где хранить API-ключи" лучше закрывать не большой миграцией, а маленьким пилотом. Возьмите один сервис, который легко проверить в работе, и один тип секрета: например, ключ к внешнему API или токен для модели. Так вы увидите, как в реальности работают ротация, права доступа и журнал событий.
Сразу зафиксируйте правила для каждой среды отдельно. Для dev обычно хватает короткого срока жизни и простого журнала. Для stage нужен тест ротации без ручных шагов и понятный откат. Для prod нужен доступ по ролям, история изменений и проверка того, что старый ключ действительно перестал работать.
Если команда работает с несколькими LLM-провайдерами, отдельная польза бывает от сокращения числа внешних ключей. В этом смысле единый шлюз вроде RU LLM на rullm.com упрощает схему: приложению часто достаточно одного доступа к api.rullm.com вместо набора ключей к разным провайдерам. Это не заменяет Vault или KMS, но делает ротацию и аудит заметно проще.
После пилота не переносите все сразу. Сначала проверьте две вещи: команда умеет менять секрет без простоя, а журнал доступа отвечает на обычные вопросы безопасника за пару минут. Если на это уходит полдня, схема еще сырая.
Дальше повторите тот же шаблон на остальные сервисы. Не меняйте одновременно хранилище, правила и формат аудита. Один понятный подход для пяти сервисов почти всегда лучше, чем три разные схемы, которые никто не может быстро объяснить на ревью или во время инцидента.
Часто задаваемые вопросы
Когда Vault лучше KMS?
Vault берите там, где секретов много, они часто меняются, и разным сервисам нужны разные права. Он удобнее для ротации, коротких токенов и разбора инцидентов.
KMS проще, когда у вас одно облако, немного секретов и доступ уже держится на IAM. В такой схеме вы быстрее запуститесь и не будете обслуживать отдельный критичный сервис.
Хватит ли Kubernetes Secret для небольшого проекта?
Для небольшого проекта часто да, если вам нужно просто передать токен в pod и команда понимает, кто и где его меняет. На старте это самый короткий путь.
Но Kubernetes Secret плохо закрывает весь цикл работы с секретом. Он не решает сам по себе вопрос с источником значения, полной ротацией и удобным аудитом чтений и изменений.
Где лучше держать исходное значение секрета?
Храните первоисточник там, где вы контролируете доступ, историю изменений и отзыв. Обычно это Vault, secret manager в облаке или другая централизованная система.
Не держите исходное значение только в CI, на ноутбуке инженера или в панели провайдера без учета. Именно там команды чаще теряют контроль.
Как менять ключи без простоя?
Сначала разделите секреты по сервисам и средам, чтобы не менять один токен сразу для всего контура. Потом настройте схему, где система обновляет значение централизованно, а приложение умеет перечитать его без ручной беготни.
Перед продом проверьте ротацию на одном сервисе. Если после замены вам нужно вручную искать старые job и перезапускать все подряд, схема еще сырая.
Зачем вообще нужен аудит чтения секрета?
Да, нужен. Без журнала вы не поймете, кто прочитал секрет, кто поменял его и какой сервис продолжает ходить со старым значением.
Этот журнал помогает не только ИБ. Он экономит часы, когда расходы по внешнему API выросли или фоновые задачи внезапно начали падать.
Почему нельзя дать один API-ключ всем сервисам?
Общий токен стирает границы ответственности. Любая утечка или ошибка в одном сервисе сразу бьет по всем остальным.
Потом вы не видите, кто дал лишнюю нагрузку и что именно надо отзывать. В итоге команда меняет все сразу и рискует сломать прод.
Что выбрать, если вся инфраструктура уже в одном облаке?
Если почти все живет в одном облаке, начните с KMS и штатного secret manager. Вы получите шифрование, роли и аудит без лишнего слоя инфраструктуры.
Такой вариант хорош, пока секретов немного и права доступа простые. Когда сервисов станет больше, а ротация станет частой, вы быстро упретесь в ограничения.
Как правильно разделить test и prod?
Разведите test и prod на уровне секретов, ролей и сервисных аккаунтов. Тестовый сервис не должен даже видеть продовые значения.
Не копируйте рабочий ключ в тест ради скорости. Такие временные решения потом живут месяцами и создают самые неприятные утечки.
Что делать, если сервис не подхватывает новый секрет?
Сначала проверьте, как приложение читает секрет. Если оно берет значение только при старте, одного обновления в хранилище мало — сервис продолжит жить со старым ключом.
Тогда настройте контролируемый перезапуск или механизм перечитывания секрета. И всегда проверяйте по логам, что новый ключ реально начал работать.
С чего начать переход на нормальное хранение секретов?
Не переносите все сразу. Возьмите один сервис и один секрет, например токен к внешнему API, и прогоните полный сценарий: выдача, ротация, отзыв и проверка по журналу.
Если команда после этого может быстро сменить ключ и понять, кто использовал старое значение, масштабируйте тот же подход дальше. Если нет, сначала упростите схему.