Как организовать безопасное хранение секретов в Docker: лучшие практики

в 5:15, , рубрики: buildkit, devops, docker, Docker Secrets, hashicorp vault, kubernetes, безопасность, контейнеризация, секреты, хранение данных
Как организовать безопасное хранение секретов в Docker: лучшие практики - 1

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

В двух словах, схема следующая:

  1. Устанавливаете/настраиваете Vault (можно в отдельном контейнере/VM/Kubernetes).

  2. Создаёте в Vault хранилище (типа kv-v2) и кладёте туда секреты (вручную или скриптом).

  3. Генерите политику доступа, например, чтобы сервис А мог читать секции только /app/serviceA/*.

  4. Сервис А поднимается и аутентифицируется (через 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:

  1. Скрипт/утилита (Vault CLI, Ansible, etc.) предварительно получает секреты из Vault.

  2. Кладёт их в локальные файлы/docker-compose.override.yml (сильно шифровать или ограничивать права).

  3. Поднимаем контейнеры.

Да, это менее элегантно, чем в 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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js