Секреты Docker: почему их нельзя хранить в образе и как передавать

Автоматизация развёртывания приложений через API ускоряет выпуск новых версий, но одновременно повышает требования к обращению с секретами. Пароли, токены, приватные ключи и строки подключения к базам данных не должны становиться частью Docker-образа или без необходимости передаваться через командную строку.

В этом материале Beget.API рассматривается как инструмент управления процессом автоматизации. Описание API само по себе не определяет, где именно должны храниться секреты и каким способом их следует передавать контейнеру. Поэтому безопасная схема должна опираться на общие возможности Docker, ограничения CI/CD-системы и правила конкретной инфраструктуры.

Почему секреты нельзя хранить в Docker-образе

Docker-образ предназначен для распространения приложения и его зависимостей. После сборки один и тот же образ может попасть в реестр, на тестовый сервер, в резервную копию или на несколько рабочих узлов. Если пароль или ключ записан в Dockerfile, файл конфигурации, скрипт сборки или слой образа, удалить его простой правкой исходного файла уже недостаточно.

Причина в послойной структуре образа. Команды вроде COPY config.env /app/config.env и RUN echo "$TOKEN" > /app/token могут оставить данные в слоях, истории сборки или кэше. Даже если последним шагом файл удалить, предыдущий слой способен сохранить его содержимое. Кроме того, секрет может оказаться в логах сборочного процесса, если команда выводит переменную окружения или использует подробный режим.

Не следует считать безопасными и следующие варианты:

  • передача пароля в аргументе docker build;
  • запись ключа в открытый репозиторий вместе с Dockerfile;
  • размещение секрета в образе под видом обычного файла;
  • добавление токена в URL подключения;
  • передача чувствительных значений в параметрах командной строки;
  • публикация полного вывода CI/CD-задачи.

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

Границы ответственности Beget.API

API провайдера может использоваться для создания ресурсов, запуска операций, управления окружениями и интеграции с внешним пайплайном. Однако наличие API-вызова не означает, что передаваемые ему параметры автоматически становятся защищённым хранилищем секретов.

Проверить подходящий вариант можно на странице Beget.

Перед использованием Beget.API необходимо отдельно проверить:

  1. какие параметры принимает конкретная операция;
  2. где сохраняются переданные значения;
  3. попадают ли они в историю запросов, журналы или ответы;
  4. кто имеет доступ к журналам и учётной записи;
  5. можно ли ограничить срок действия токена;
  6. предусмотрен ли отзыв ключа без остановки остальных процессов.

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

Особенно важно разделять два типа данных. Идентификатор образа, номер версии и имя окружения обычно не являются секретами. Токен доступа, пароль администратора, ключ подписи и сертификат закрытого ключа требуют отдельного режима хранения, ограничения прав и контроля срока действия.

Передача секретов на этапе запуска

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

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

Для контейнерных окружений следует придерживаться таких правил:

  • передавать секрет только по защищённому соединению;
  • не добавлять секрет в URL, имя контейнера или аргументы команды;
  • отключать подробное логирование для участков, где обрабатываются чувствительные данные;
  • не выводить значения переменных при диагностике;
  • ограничивать доступ к сокету Docker и управляющему узлу;
  • использовать отдельные секреты для разработки, тестирования и производства;
  • задавать минимально необходимые права для каждого токена;
  • удалять временные файлы после завершения операции;
  • регулярно отзывать и перевыпускать ключи.

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

Перед настройкой откройте Beget и сверьте актуальные условия.

Практическая схема развёртывания

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

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

На этапе запуска контейнер получает секрет из доверенного хранилища или из защищённого механизма окружения. Приложение проверяет наличие обязательных значений и прекращает запуск при их отсутствии. Такая остановка безопаснее, чем запуск с пустым паролем или тестовым значением.

После запуска нужно проверить, что:

  • секрет отсутствует в истории Docker-образа;
  • секрет не отображается в логах;
  • секрет не включён в метаданные контейнера без необходимости;
  • доступ к файлу или переменной имеют только требуемые процессы;
  • запросы к API выполняются по HTTPS;
  • токен автоматизации можно быстро отозвать;
  • неуспешный запуск не оставляет временные копии ключей.

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

Проверка репозитория и пайплайна

До публикации образа полезно проверять исходный код и историю изменений на наличие строк, похожих на ключи, токены и пароли. Такая проверка не заменяет управление секретами, но помогает обнаружить случайную утечку. Анализировать нужно не только текущие файлы, но и историю коммитов, потому что удалённая строка может оставаться доступной в старых объектах репозитория.

Когда требования понятны, перейдите в Beget и проверьте выбранный вариант.

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

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

Итог

Beget.API может быть частью автоматизированного процесса, но безопасность секретов определяется всей цепочкой: от хранилища CI/CD и сборки образа до запуска контейнера и журналов. Пароли и ключи не следует помещать в Docker-образ, коммиты или команды, доступные другим пользователям. Надёжная схема использует неизменяемый образ без секретов, защищённый канал запуска, минимальные права, короткий срок действия токенов и регулярную проверку журналов.

Если конкретная операция Beget.API не описывает специальную передачу секретов, её не следует трактовать как защищённое хранилище. В таком случае безопаснее передавать через API только несекретные параметры или ссылку на версию развёртывания, а чувствительные значения подключать на стороне целевой инфраструктуры.

Что почитать дальше

Обложка вдохновлена фреской Микеланджело «Сотворение Адама» (около 1512). Посмотреть оригинал на сайте музеев Ватикана.