Хей! Секреты — это такая щекотливая тема, из‑за которой у безопасников начинаются нервные подёргивания глаза. Вроде бы «просто пароль» или «просто токен», но в 2025 году мы уже знаем, что просто в безопасности - это верная дорога к утечкам и ночным обкаткам плана B. В этой статье поговорим, как правильно хранить секреты в Docker-контейнерах и окрестностях, а заодно разберёмся, чем могут помочь Docker Secrets, HashiCorp Vault и компания.
Зачем вообще усложнять?
Разработчики любят скорость. Но когда на кон ставится безопасность корпоративных данных, хочется быть уверенным, что доступ к базе, платёжным системам и прочим чувствительным штукам никто не перехватит. Если запекать секреты прямо в образ Docker-файла, то стоит этому образу уехать куда-то на публичный реестр - и всё, пароль в открытом доступе. Или если случайно закоммитили .env
с боевыми ключами в GitHub - боль и страдания обеспечены.
Поэтому главный девиз - храни секреты отдельно, чтобы их утечка не была тривиальной при каждом билде или шаринге образа.
Подходы к хранению секретов: краткий обзор
-
Переменные окружения — самый классический и простой вариант. Бейджики работает на деве ему точно выдадим, но хранить в
docker-compose.yml
или вообще в публичном репо секретные переменные — решение на троечку. -
Docker Secrets (в Docker Swarm) — это «фирменная» функциональность Docker для хранения секретов, которая особенно хороша, если используете Docker Swarm. Секреты хранятся зашифрованными в Raft‑логах кластера. Контейнеры получают их через файлы внутри
/run/secrets
. Удобно, безопасно, но требует либо Swarm‑окружения, либо дополнительных телодвижений в Compose с BuildKit. -
Использование BuildKit для сборки — BuildKit умеет передавать секреты в контейнер на этапе сборки без их «пропекания» в образ. Секрет доступен только в момент выполнения
RUN
, а затем уничтожается и не остается в слоях. Круто для случаев, когда нужно, например, стянуть приватные пакеты из npm/pip/apt и не засветить ключ. Но на этапе рантайма это уже не поможет, там другой механизм. -
HashiCorp Vault — полноценное хранилище секретов со своими методами авторизации, политиками доступа, динамическими секретами, раздачей сертов — настоящий швейцарский нож. Идеально, когда микросервисов много, у них разная логика доступа, а ещё хотелось бы все секреты хранить централизованно, да ещё и обновлять их без рестартов. Vault, разумеется, не единственный в своём роде (есть AWS Secrets Manager, Google Secret Manager и т. д.), но очень популярен в DevOps‑мире.
-
Другие менеджеры секретов — если у вас всё крутится в AWS — AWS SSM / Secrets Manager; на GCP — Secret Manager; на Azure — Key Vault. Принцип тот же: централизация и гибкие политики.
Классическая боль: “Только не в ENV!”
Многие до сих пор засовывают пароли в ENV
. Причём и при сборке, и при рантайме. А потом:
-
Эти переменные отображаются в
docker inspect
. -
Часто их можно прочитать через логи, если приложение где-то опечаталось и вывело
printenv
. -
Они оказываются в истории
docker history
, если были объявлены черезARG
и использованы неаккуратно.
Если всё же ENV неизбежны, то никогда не коммитьте .env
в публичный репо, используйте .env.example
для примера и храните боевые .env-файлы отдельно (или шифруйте их, как вариант).
Docker Secrets: когда Swarm - ваш друг
В контексте Docker Swarm всё красиво:
-
Разворачиваете кластер,
-
Командой
docker secret create
кладёте нужный файл или строку в зашифрованное хранилище, -
Дальше в docker-compose v3+ при описании сервиса указываете
secrets: …
, -
Внутри контейнера секрет будет лежать в
/run/secrets/<имя_секрета>
.
Например:
version: "3.7"
services:
my-service:
image: myuser/my-service:latest
secrets:
- db_password
secrets:
db_password:
external: true
Где db_password
уже создан командой docker secret create db_password ./db_password.txt
.
Плюсы:
-
Простая реализация;
-
Секреты не лежат на диске в открытом виде, а передаются по TLS в зашифрованном виде между нодами Swarm.
Минусы:
-
Привязка к Docker Swarm. Если вы на Kubernetes или просто Compose без Swarm, надо городить костыли;
-
Иногда люди не хотят или не могут перейти на Swarm, если прод уже на Kubernetes.
BuildKit: секреты на этапе сборки
Есть случаи, когда ваш Dockerfile должен дернуть приватные ресурсы в момент RUN
. Например, проприетарные пакеты из закрытого репозитория Python или npm. Передавать их через ARG
- плохая практика: секрет может попасть в итоговый слой образа.
BuildKit предлагает синтаксис:
# syntax=docker/dockerfile:1.2
FROM python:3.9
# ...
RUN --mount=type=secret,id=my_secret
bash -c 'source /run/secrets/my_secret && pip install my_private_pkg'
Где my_secret
передаётся при сборке (например, docker build --secret id=my_secret,src=secret.env .
). BuildKit монтирует файл secret.env
только в момент выполнения этого RUN, в слое образа его не останется.
HashiCorp Vault: король гибкости
Когда у вас десятки микросервисов и куча окружений (dev, staging, production), Vault позволяет всё централизовать и гибко рулить политиками: кто какой секрет может читать, когда у него истекает время жизни, как безопасно передавать токены для авторизации.
В двух словах, схема следующая:
-
Устанавливаете/настраиваете Vault (можно в отдельном контейнере/VM/Kubernetes).
-
Создаёте в Vault хранилище (типа
kv-v2
) и кладёте туда секреты (вручную или скриптом). -
Генерите политику доступа, например, чтобы сервис А мог читать секции только
/app/serviceA/*
. -
Сервис А поднимается и аутентифицируется (через AppRole или JWT, или ещё какой-то механизм), Vault проверяет политику и отдаёт нужный секрет.
Фишка: Vault умеет выдавать динамические секреты к БД (когда он сам генерирует временного пользователя в базе), автоматически ротацию credential’ов (например, каждые 6 часов), управлять сертификатами (PKI), и ещё много чего.
Варианты интеграции:
-
Vault Agent (сайдкар или демоночек), который обновляет секреты и релоадит приложение или подгружает их в среду.
-
direct API - ваше приложение само обращается в Vault, если умеет (библиотеки доступны для разных языков).
Docker Compose: пара трюков
Если вы не хотите заводить Swarm или Kubernetes, а Deploy у вас - это условный “docker-compose up -d” на
-
передача переменных через
.env
(но это небезопасно, если.env
утекает); -
поддержка BuildKit (при сборке) и допиливание секретов в версии 3.x (но будет нужна экспериментальная фича).
Лайфхак: Можно частично комбинировать Vault (или другой менеджер) и docker-compose:
-
Скрипт/утилита (Vault CLI, Ansible, etc.) предварительно получает секреты из Vault.
-
Кладёт их в локальные файлы/
docker-compose.override.yml
(сильно шифровать или ограничивать права). -
Поднимаем контейнеры.
Да, это менее элегантно, чем в Swarm, но тоже рабочий вариант, если не хочется перетаскивать всю инфраструктуру.
Практические советы
-
Не коммитить секреты в исходники - Да, звучит банально, но GitHub полон “AWS_ACCESS_KEY_ID” и “db_password”. Используйте
.gitignore
, шифруйте или храните в менеджерах. -
Минимизировать время жизни - Если есть возможность (Vault, временные токены, etc.), уменьшайте TTL секретов, чтобы при утечке ущерб был ниже.
-
Сегментировать доступ - Один сервис - одна зона секретов. Не надо, чтобы веб-приложение видело пароль к стороннему API, если оно им не пользуется.
-
Вести журнал - И Vault, и Docker Swarm умеют хранить логи: кто, когда и зачем брал секрет. Это бывает полезно для аудита.
-
Обновлять секреты без перезагрузки - Если ваше приложение поддерживает hot reload конфигов (Nginx, PostgreSQL, собственное Go-приложение и т.д.), старайтесь использовать агента (Vault Agent или docker secrets watch) для seamless-обновлений.
-
Шифровать всё - Передаваемые данные, хранилище на диске, бэкапы - всё. TLS, at-rest encryption и VPN-туннели - ваши друзья.
Заключение
Безопасное хранение секретов — это не так страшно, как звучит. Да, придётся потратить чуть больше времени на настройку Vault или Docker Secrets, но взамен вы получите спокойный сон и меньше седых волос, когда кто‑то случайно от кого‑то «уплыл» Docker‑образ.
И помните: какой бы метод вы ни выбрали, главное — не храните пароли в открытом виде в репозитории. На этом фоне использование Docker Secrets, Vault или других менеджеров секретов будет уже серьёзным шагом вперёд.
Если остались вопросы или хотите поделиться своим боевым опытом — пишите в комментариях! Обсудим, а может, и продолжение статьи родим с ещё более продвинутыми сценариями.
Stay safe & secure, друзья!
Автор: itBestRay