Секреты сборки и giterminism
Что нужно знать перед уроком
- M3.05 — Stapel-импорт — нужен опыт с werf.yaml и обеими моделями билдеров для секретов сборки
Что нужно знать перед уроком
M1 урок 4 — Secret в кластере (runtime-секреты, монтируемые в Pod). Здесь — принципиально другая категория: build-time секреты, нужные только во время сборки образа (например, токен доступа к приватному npm-registry), которые не должны попасть в итоговые слои образа.
Теория
Почему секреты нельзя передавать через build-аргументы или обычные ENV
| |
Проблема: build-аргументы (ARG) и переменные окружения (ENV), даже если их значение больше не используется в последующих слоях, остаются в метаданных истории образа (docker history, docker inspect) — извлечь секрет из готового образа тривиально даже без доступа к исходному Dockerfile. Это касается и RUN export SECRET=... — переменная не попадает в файловую систему следующего слоя, но команда, её устанавливающая, видна в истории.
secrets: env/id/src/value в werf.yaml
| |
Четыре способа объявить секрет сборки:
| Способ | Источник значения | Обязателен id? |
|---|---|---|
env: VAR_NAME | текущая переменная окружения на машине, где выполняется werf build | нет, id по умолчанию = имени переменной |
id: X + env: VAR_NAME | та же переменная, но с явным собственным id (для читаемости в /run/secrets/<id>) | да, явно указан |
src: <path> | локальный файл (например, ~/.aws/credentials) | нет, id по умолчанию = базовому имени файла |
id: X + value: <строка> | значение прямо в werf.yaml (обычно — само в свою очередь через переменную окружения на этапе рендера шаблона) | да, обязателен для value |
Секреты не попадают в итоговый образ и не видны в истории — они монтируются как временные файлы, доступные только во время выполнения конкретной инструкции, и физически удаляются сразу после.
Как читать секрет в shell/ansible/Dockerfile
| |
| |
Во всех случаях секрет доступен по пути /run/secrets/<id> только в момент выполнения конкретной инструкции, использующей монтирование секрета (--mount=type=secret,id=... в Dockerfile — тот же синтаксис BuildKit, который используется и без werf; для Stapel — секрет доступен во всех shell/ansible-инструкциях данного образа автоматически, без явного mount). Файл не сохраняется ни в одном итоговом слое.
Giterminism: почему обязательное разрешение через werf-giterminism.yaml, а не опция
Giterminism — принцип, введённый werf: конфигурация сборки и весь контекст сборки должны читаться строго из текущего Git-коммита, без скрытых внешних зависимостей (файлы вне репозитория, недекларированные переменные окружения). Любое отклонение от этого правила (использование переменной окружения, файла вне репозитория, секрета) должно быть явно разрешено в отдельном файле werf-giterminism.yaml:
| |
Без соответствующей записи в werf-giterminism.yaml секрет, объявленный в werf.yaml, не будет использован — werf откажется собирать образ с ошибкой валидации. Это не опциональная строгость, а фундаментальный принцип: giterminism гарантирует воспроизводимость сборки любым членом команды или CI-раннером из одного и того же коммита, без скрытых зависимостей от локального окружения конкретной машины разработчика. Без этого принципа сборка «работает у меня локально» становится частой проблемой — если бы секреты (и любые другие внешние входы) допускались без явной декларации, разные машины могли бы собрать разный результат из одного и того же коммита.
Частые ошибки и подводные камни
- Передавать секрет через
ARG/ENVвместоsecrets:+giterminism. Секрет физически остаётся в метаданных истории образа — классическая утечка, которую легко найти черезdocker history. - Забыть добавить разрешение в
werf-giterminism.yaml. Секрет, объявленный только вwerf.yaml, без соответствующей записиallowEnvVariables/allowFiles/allowValueIdsвwerf-giterminism.yaml, приведёт к ошибке сборки — это осознанное архитектурное решение, а не забытая фича. - Хранить
value:секрета прямо вwerf.yamlкак строку в открытом виде. Формально это разрешённый синтаксис, но так секрет попадает в Git-историю самого репозитория — используйтеvalue:только для переменных, вычисляемых в момент рендера Go-шаблонов werf.yaml (например, из переменной окружения CI, а не хардкод).
Практика в кластере
Практики в кластере в этом уроке нет — секреты сборки существуют только на этапе werf build, они принципиально отличаются от runtime Secret из M1 урока 4.
Практика разработки
- Смоделируйте сборку frontend-образа Vikunja, которому нужен токен для приватного npm-реестра (в реальности Vikunja использует публичный registry, но упражнение иллюстративно):
| |
- Создайте
werf-giterminism.yamlс явным разрешением:
| |
- Попробуйте собрать без этого файла (временно удалите/переименуйте) и убедитесь, что werf отказывается собирать с понятной ошибкой о неразрешённом секрете; затем восстановите файл и убедитесь, что сборка проходит:
| |
- Проверьте, что секрет не попал в итоговый образ:
| |
Шпаргалка команд урока
| |
| |
Вопросы для самопроверки
ARG/ENV сохраняются в метаданных истории слоёв — секрет можно извлечь из готового образа без доступа к исходному Dockerfile.
Секрет монтируется как временный файл /run/secrets/<id>, не сохраняющийся в итоговых слоях образа.
Источник: Werf — документация
Giterminism требует явного разрешения любого отклонения (включая секреты) — без записи в werf-giterminism.yaml сборка завершится ошибкой.
Источник: Werf — документация — Configure Giterminism for Build Secrets
Giterminism — принцип детерминированной, воспроизводимой сборки строго из содержимого репозитория, с явным декларированием любых допустимых отклонений.
Источник: Werf — документация
Для env/src id по умолчанию берётся из имени переменной/файла; для value id должен быть указан явно.
Источник: Werf — документация — Using secrets in Stapel shell instructions
Рекомендуемая литература
Официальная документация
Статьи и блоги
- werf v1.2 — стабильный релиз утилиты для доставки приложений в Kubernetes (Stapel) — Flant на Habr.
- Первые шаги с werf: собираем и деплоим простое приложение — Flant на Habr.
- GitLab + K8s + Werf — Habr.
Книги
- Nigel Poulton. Docker Deep Dive. 2023. ISBN 978-1916585206.
Связанные материалы
- Предыдущий урок: M3.05 — Stapel-импорт.
- Следующий урок: M3.07 — Cleanup registry.
- Runtime-секреты Kubernetes (принципиально другая категория): M1 урок 4 — ConfigMap/Secret.
✓ Урок пройден — все вопросы самопроверки отвечены верно