Как организовать безопасное хранение секретов в Docker: лучшие практики
Хей, Хабр! Секреты — это такая щекотливая тема, из‑за которой у безопасников начинаются нервные подёргивания глаза. Вроде бы «просто пароль» или «просто токен», но в 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» на VPS, то всё чуть сложнее. Официальный docker-composeSecrets нет, но есть:
передача переменных через
.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, друзья!