{ "index": 164, "slug": "editorial-2023-06-mechanism-secrets-supply-chain", "title": "Attestation не доказывает provenance: как связать секрет, сборку и артефакт", "excerpt": "Файл attestation рядом с образом ещё не подтверждает его происхождение. Разбираем границы секрета, digest, builder identity и проверку, которая связывает provenance с тем, что действительно попадает в deploy.", "contentHtml": "
После выкладки в системе лежит образ и файл attestation. Команда открывает файл, видит source revision и builder, затем помечает релиз как проверенный. Позже выясняется, что statement ссылается на другой digest, identity сборщика никто не проверял, а deploy получил образ по тегу latest. Ошибка стоит дорого: нельзя уверенно определить затронутый артефакт, выбрать безопасный rollback и объяснить аудитору, какой факт подтверждён.
Проблема усиливается, когда в ту же декларацию добавляют сведения о секрете. Доступ CI к секрету не доказывает, что значение не попало в слой образа, cache, metadata или журнал. Provenance отвечает за происхождение output. Secret boundary отвечает за путь доступа к чувствительному значению. Это разные утверждения с разными проверками.
\nТезис статьи простой: attestation становится полезным evidence только после независимого сопоставления subject с artifact digest, проверки доверенной identity и связи digest с входом deploy. Наличие файла, подписи или знакомого названия инструмента не заменяет эти операции.
\nРазложите delivery-поток на отдельные факты. Source revision обозначает вход сборки. Builder identity обозначает исполнителя, которому разрешено выпускать результат. Secret boundary задаёт этап, который может получить ссылку или значение, и этапы, которым оно недоступно. Artifact digest обозначает конкретный набор байтов. Attestation statement заявляет свойства этого output. Verification проверяет statement с заданными правилами доверия. Deploy input показывает, что именно система пыталась запустить.
\nНельзя вывести один факт из соседнего. Digest не рассказывает, кто собрал образ. Source revision не доказывает, что именно он попал в output. Подписанная attestation не подтверждает provenance, пока проверка не установила доверенную identity, допустимый формат, claims и тот же subject. Тег образа тоже не заменяет digest: тег может указывать на новый результат.
\n| Уровень | Что можно записать | Чего это не доказывает | Следующая проверка |
|---|---|---|---|
| Source | Выбрана ревизия abc123. | Сборка использовала именно её. | Сопоставить revision с invocation build. |
| Builder | Указана identity CI. | Identity доверена и реально запускала build. | Проверить identity и контекст запуска. |
| Artifact | Известен digest образа. | Этот digest отправили в deploy. | Сверить digest с release input. |
| Attestation | Statement содержит subject. | Statement подписан и правдив. | Проверить подпись, signer и claims. |
| Secret boundary | Сборка получает секрет на названном этапе. | Значение не попало в output. | Проверить конкретный путь передачи и места хранения. |
Секрет должен жить внутри ограниченной границы. Например, job получает короткоживущий токен через secret manager, использует его для чтения зависимости и не записывает значение в environment, артефакт сборки или лог. Даже такая схема описывает только ожидаемый путь. Она не доказывает отсутствие утечки без проверки конкретного pipeline и его output.
\nОсобенно опасны аргументы командной строки, переменные, которые CI печатает при ошибке, кеши package manager и Docker layers. Секрет может исчезнуть из финального файла, но остаться в промежуточном слое. Поэтому вопрос «секрет есть в образе?» слишком широк. Сначала назовите образ, digest, слой или metadata и способ проверки. Если evidence нет, статус должен быть not-observed, а не «утечки нет».
Ниже учебный пример. Он работает только с заранее заданными строками, не читает CI, registry или secret manager и не выполняет deploy. Его задача — показать отрицательный путь: statement про другой subject нельзя принять.
\nconst artifact = {\n digest: 'sha256:artifact-a',\n deployInput: 'sha256:artifact-a',\n};\n\nconst statement = {\n subjectDigest: 'sha256:artifact-b',\n builder: 'ci.example/build',\n};\n\nconst sameArtifact =\n artifact.digest === statement.subjectDigest &&\n artifact.digest === artifact.deployInput;\n\nif (!sameArtifact) {\n throw new Error('manual review: subject is not the deploy artifact');\n}\nПроверка выше не устанавливает, что builder доверенный, подпись действительна или сборка использовала указанную ревизию. Она ловит только несоответствие subject и deploy input. В рабочей системе нужен проверяемый формат attestation, доверенная политика identity, источник digest и результат запуска verifier. Если хотя бы одно звено не наблюдалось, не повышайте статус до verified.
| Симптом | Причина | Проверка | Действие |
|---|---|---|---|
| Attestation есть, но deploy использует тег. | Релиз не сохранил digest как вход. | Найти фактический digest, переданный в deploy. | Остановить вывод о provenance и привязать release к digest. |
| Subject statement не равен digest образа. | Statement собран для другого output или перепутан. | Сравнить subject, digest registry и deploy input. | Не использовать statement; запросить корректное evidence. |
| Builder указан, но signer не проверен. | Identity смешали с результатом verification. | Проверить signer, trust policy и контекст запуска. | Оставить статус not-verified до отдельной проверки. |
| Секрет доступен build, но нет сведений о слоях. | Граница доступа описана общо. | Проверить command line, logs, cache и layers выбранного output. | Сузить исследование до одного digest и не публиковать значение секрета. |
| Локальная декларация выглядит полной. | Текст приняли за наблюдаемый факт среды. | Для каждого поля найти источник и время проверки. | Отделить declaration от evidence и назначить владельца проверки. |
| Нужен срочный rollback. | Неизвестно, какой output был применён. | Связать release record с digest и известным кандидатом. | Сначала остановить следующий шаг; rollback выполнять только при известном безопасном кандидате. |
Provenance не доказывает отсутствие уязвимостей, добросовестность исходного кода или безопасность всех зависимостей. Она описывает происхождение и условия получения output в пределах выбранной модели. Если builder записывает неверные сведения, downstream-проверка должна учитывать доверие к builder и его identity.
\nAttestation не заменяет сканирование, review зависимостей, контроль доступа, ротацию секретов, тесты и наблюдение после выпуска. Подпись подтверждает целостность statement относительно ключа или identity. Она не делает claims истинными сама по себе. Digest связывает байты, но не объясняет, почему эти байты допустимы.
\nУчебный код и таблица не запускались против production и не сообщают результат конкретного pipeline. Источники ниже дают официальные модели и спецификации, но не доказывают соответствие вашего проекта. При отсутствии реального verifier корректная формулировка — «не проверено».
\nЦепочка готова к решению о выпуске, когда source revision, builder identity, artifact digest и deploy input связаны конкретными записями; subject attestation совпадает с digest; signer и claims проверены по названной trust policy; путь секрета ограничен и проверен для выбранного output; отрицательные случаи переводят решение в ручной review. Если есть только файл attestation, зелёный CI или тег образа, доказательство не завершено.
\n