Перейти к основному содержанию Перейти к навигации Перейти к нижнему колонтитулу
Ограниченное время: Программа Design Partner. План BUSINESS бесплатно на всю жизнь.

Управление секретами

Положите учётные данные развёртывания в место, недоступное форкам. Только запись по замыслу.

Управление секретами

Вот в чём суть форков: это побайтовые копии зашифрованного образа со всеми учётными данными. Живой ключ Stripe, пароль базы данных, API-токен в репозитории? Форк их наследует. Ваша песочница начнёт снимать деньги с реальных карт.

Правильное место: rdc repo secret. Два режима доставки, только запись по замыслу, и форк стартует пустым. В этом уроке мы задаём оба вида, развёртываем приложение, которое их использует, доказываем, что значения реально попадают в контейнер, а затем наблюдаем, как форк не может запуститься, потому что секреты отказываются за ним следовать.

Смотреть урок

Ловушка: .env в репозитории

A .env file inside the repo image gets cloned by every fork

Большинство команд кладут .env в репозиторий. Очевидный шаг.

Потом они делают форк.

Форк является побайтовой копией образа родителя. Что есть в .env родителя, то есть и в .env форка. Контейнеры форка запускаются. Они читают тот же ключ Stripe. Они вызывают тот же Stripe API с продакшен-учётными данными. Для Stripe этот вызов исходит от вас.

Плохой день. Я знаю по опыту.

Шаг 1: Установка секрета в режиме env

rdc repo secret set --name my-app --key DB_HOST --value postgres.internal --mode env

Сначала установим секрет в режиме env. Значение попадёт в контейнер как переменная окружения. Первая запись не требует подтверждений, доказательство нужно только при перезаписи существующего секрета.

--mode env делает так, что значение попадает в контейнер как переменная окружения. Для первой записи не нужны никакие ритуалы; доказывать текущее значение требуется только при перезаписи существующего секрета.

Шаг 2: Установка секрета в режиме file

rdc repo secret set --name my-app --key STRIPE_KEY --value sk_test_xxx --mode file

Теперь установите секрет в режиме файла. File mode никогда не передаёт значение через переменные окружения контейнера: оно записывается в файл в директории /run/secrets с помощью стандартного механизма секретов Docker. Для любых чувствительных данных предпочитайте file mode.

Режим file никогда не помещает значение в окружение контейнера. Вместо этого он записывает его в /run/secrets/stripe_key, используя стандартный механизм Docker. Предпочтительно для всего чувствительного.

Шаг 3: Посмотреть, что у вас есть

rdc repo secret list --name my-app

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

Вы видите имена и режимы. Никаких значений. Список никогда не показывает значения, кто бы ни спрашивал.

Подключение к compose

Откройте docker-compose.yml. Укажите оба режима:

services:
  api:
    image: myapp:latest
    environment:
      DATABASE_HOST: ${REDIACC_SECRET_DB_HOST}
    secrets:
      - stripe_key

secrets:
  stripe_key:
    file: /var/run/rediacc/secrets/${REDIACC_NETWORK_ID}/STRIPE_KEY

${REDIACC_SECRET_DB_HOST} использует режим env: обёртка compose в renet раскрывает его из хранилища секретов во время развёртывания.

Блок secrets: соответствует режиму file и использует стандартный механизм Docker. В пути хоста используется ${REDIACC_NETWORK_ID}, поэтому один и тот же compose-файл работает для родителей и форков. У каждого форка свой сетевой ID.

Прочитать его обратно невозможно

Write-only model: get returns a digest, never the value

Вот часть, которая удивляет людей с первого раза, включая меня самого.

Шаг 4: Get возвращает дайджест

rdc repo secret get --name my-app --key STRIPE_KEY

Команда secret get возвращает дайджест, а не значение, и нет флага для восстановления открытого текста. Это соответствует модели GitHub Actions: секреты доступны только для записи по замыслу.

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

Это модель GitHub Actions: только запись. Вы можете доказать, что знаете значение секрета, передав --current <value> и убедившись, что предусловие выполняется. Но попросить Rediacc сказать вам, что это такое, нельзя.

Шаг 5: Ротация при забывании

Забыли значение? Не пытайтесь подсмотреть. Ротируйте.

rdc repo secret set --name my-app --key STRIPE_KEY --value sk_test_new --mode file --rotate-secret

Если вы потеряли значение секрета, ротируйте его вместо попытки восстановить. Флаг rotate-secret пропускает проверку предусловия, а журнал аудита фиксирует изменение как намеренную ротацию.

--rotate-secret пропускает проверку предусловия. Журнал аудита отмечает изменение как намеренную ротацию: громкую и осознанную.

Если вы помните старое значение, лучше докажите это с помощью --current <old-value>. Это безопаснее. Не раз меня выручало, когда я был не в том терминале или не на той машине.

Развёртывание и подтверждение доставки

Секреты, которые никогда не доходят до приложения, – это просто база данных с претензиями. Разверните и проверьте оба пути доставки.

Шаг 6: Развёртывание с обоими секретами

rdc repo up --name my-app --machine <machine-name>

Задеплойте repo. Compose-файл использует оба секрета: env-значение через интерполяцию, файловое значение через монтирование Docker secrets.

Шаг 7: Секрет env дошёл

rdc term connect --machine <machine-name> --repository my-app --command 'docker exec app printenv DB_HOST'

Выведите переменную внутри контейнера: postgres.internal. Секрет в режиме env достиг приложения во время деплоя.

Контейнер печатает postgres.internal. Приложение реально получило значение, раскрытое в его окружение во время развёртывания.

Шаг 8: Секрет file дошёл

rdc term connect --machine <machine-name> --repository my-app --command 'docker exec app cat /run/secrets/stripe_key'

Прочитайте /run/secrets/stripe_key внутри контейнера: там смонтировано ротированное значение. Приложение получает открытый текст, только CLI отказывается его отображать.

И вот ротированное значение, прочитанное из /run/secrets/stripe_key внутри контейнера. Только запись распространяется на людей и CLI; ваше приложение получает настоящий открытый текст там, где Docker это гарантирует.

Финальный аккорд: форк

After fork, the secrets list is empty

Помните ловушку? Сделайте форк репозитория и посмотрите.

Шаг 9: Сделать форк репозитория

rdc repo fork --parent my-app --tag test --machine <machine-name>

Сделайте fork repo. Fork является побайтовой копией зашифрованного образа родителя.

Шаг 10: Форк выводит пустой список

rdc repo secret list --name my-app:test

Список секретов fork возвращает пустое множество: ни ключа Stripe, ни пароля от базы данных, ни API-токена. Fork не может выдавать себя за родителя, что и делает клонирование продакшена безопасным.

Пусто.

В форке нет ключа Stripe. Нет пароля к базе данных. Нет API-токена. Контейнеры в форке не могут раскрыть ${REDIACC_SECRET_STRIPE_KEY}. Файла по пути /var/run/rediacc/secrets/<fork-id>/STRIPE_KEY не существует.

Форк не может притворяться вами.

Шаг 11: Форк даже не может запуститься

rdc repo up --name my-app:test --machine <machine-name>

Запуск fork с compose-файлом родителя завершается ошибкой: файл секрета не существует в рамках сетевого идентификатора fork, поэтому Docker отклоняет bind mount. Продакшен-учётные данные никогда не следуют за fork.

Развёртывание намеренно завершается ошибкой: bind source path does not exist: /var/run/rediacc/secrets/<fork-id>/STRIPE_KEY. Файл секрета находится под сетевым ID родителя, а не форка, поэтому Docker отказывает в монтировании. Сама эта ошибка и есть демо: продакшен-учётные данные никогда не следуют за форком, даже случайно.

Если вам нужны секреты в форке для тестирования, установите их явно с sandbox-значениями, например rdc repo secret set --name my-app:test --key STRIPE_KEY --value sk_sandbox_yyy --mode file. Теперь форк обращается к Stripe-песочнице и запускается без проблем. Продакшен-учётные данные не покинули продакшен.

Итого

  • rdc repo secret хранит учётные данные вне образа репозитория.
  • Оба режима реально доставляют значения в контейнер: раскрытие переменных окружения и /run/secrets.
  • get возвращает дайджест, но не значение. Забыли – ротируйте, не пытайтесь подсмотреть.
  • Форк выводит пустой список и не может запустить compose родителя.

Секреты, которые форк не может забрать.


Далее: Резервное копирование и восстановление.