Управление секретами
Вот в чём суть форков: это побайтовые копии зашифрованного образа со всеми учётными данными. Живой ключ Stripe, пароль базы данных, API-токен в репозитории? Форк их наследует. Ваша песочница начнёт снимать деньги с реальных карт.
Правильное место: rdc repo secret. Два режима доставки, только запись по замыслу, и форк стартует пустым. В этом уроке мы задаём оба вида, развёртываем приложение, которое их использует, доказываем, что значения реально попадают в контейнер, а затем наблюдаем, как форк не может запуститься, потому что секреты отказываются за ним следовать.
Смотреть урок
Ловушка: .env в репозитории
Большинство команд кладут .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.
Прочитать его обратно невозможно
Вот часть, которая удивляет людей с первого раза, включая меня самого.
Шаг 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 это гарантирует.
Финальный аккорд: форк
Помните ловушку? Сделайте форк репозитория и посмотрите.
Шаг 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 родителя.
Секреты, которые форк не может забрать.