Проблема появляется не в момент deploy, а раньше: секрет или неподписанный артефакт проходит через CI и попадает в образ, а в журнале изменения остаётся только факт выкладки. Аудитор видит последний шаг, но не может связать его с исходной ревизией, исполнителем build и точным digest результата. Цена такого разрыва практическая: нельзя честно сказать, что нужно остановить, заменить или проверить, когда один из промежуточных фактов вызывает сомнение.
Обычная реакция — добавить в release описание «образ подписан» или «attestation приложен». Это создаёт ещё одну декларацию, а не связь доказательств. Если subject attestation не сопоставлен с digest артефакта, identity сборщика не проверена, а факт передачи секрета не отделён от его значения, текст не закрывает пробел. Команда начинает спорить о платформе вместо того, чтобы назвать отсутствующее звено и его владельца.
Сначала нарисуйте маршрут одного выпуска
Берём один узкий delivery-поток. У него есть исходная ревизия, идентичность build, граница выдачи секрета, получившийся артефакт, statement об artefact и вход deploy. Это не список продуктов и не попытка нарисовать всю инфраструктуру. Для каждой стрелки нужен один вопрос: какой факт мы ожидаем увидеть и с чем его можно сопоставить. Пока вопроса нет, стрелка остаётся предположением, даже если рядом написано знакомое имя инструмента.
Секрет в этой схеме — не строка, которую надо вывести для доказательства. Наоборот, evidence должно говорить о границе: какой этап вправе запросить ссылку на секрет, какой процесс не должен переносить значение в build output и как будет видно отсутствие разрешённого пути. Значение секрета, реальный адрес хранилища, командная политика и журналы не нужны в статье и не должны становиться артефактом расследования. Нужна различимая запись о том, что именно ещё предстоит проверить в рабочем контуре.
| Звено | Наблюдаемый факт | Чего этот факт не доказывает | Первое действие |
|---|---|---|---|
| Исходная ревизия | есть идентификатор выбранного исходника | что именно собрал CI | связать revision с заявкой на build |
| Идентичность build | назван ожидаемый исполнитель | что он действительно запускался из доверенного контекста | отделить identity от факта выполнения |
| Граница секрета | определена допустимая ссылка на секрет | что значение не попало в output | спланировать отдельную проверку передачи |
| Артефакт | есть digest результата | что это именно то, что развёрнуто | сохранить digest как предмет сопоставления |
| Attestation | есть statement о subject | что statement подписан, проверен или правдив | сверить subject с digest и определить проверку |
| Deploy | есть запись о запуске выкладки | что вход deploy равен проверенному артефакту | связать deploy input с digest |
Не смешивайте данные, statement и проверку
Digest — это имя конкретного результата в выбранном формате. Он удобен как ключ сопоставления: тот же digest должен быть предметом statement и входом deploy. Но digest не рассказывает, кто его собрал и откуда взялись входы. Statement attestation добавляет утверждения о subject, builder или исходнике. Оно тоже не становится доказательством автоматически: надо отдельно определить, чем и в каком контексте будет проверяться подпись, идентичность, формат и связь с ожидаемым input.
Именно здесь полезна SLSA v1.0, доступная в июне 2023: она даёт общий язык для уровней поставки и provenance, а не готовую печать качества. Sigstore и Cosign дают форматы и команды для работы с подписями и attestation. Ни один из документов не наблюдает ваш pipeline. Поэтому в change нельзя писать «SLSA закрыта» только потому, что появился файл с похожими полями. Корректная формулировка скромнее: подготовлен договор evidence, а реальная проверка перечислена отдельно.
Соберите локальный договор до подключения CI
Ниже не конфигурация CI и не шаблон для production. Это маленький fixture, который создаёт только помеченные synthetic записи в памяти. Он принимает закрытый набор полей: source label, builder label, ссылку на секрет без значения, digest и тот же subject в synthetic declaration. Неожиданное поле, включая secretValue, возвращает отказ до сборки записи. Такой тест полезен для ревью структуры: он не может прочитать секрет, запустить runner, создать certificate или увидеть развёрнутый образ.
import {
assembleSyntheticSupplyChain,
runSupplyChainFixture,
} from './upgrade-2023-06.mjs';
const flow = assembleSyntheticSupplyChain({
synthetic: true,
sourceRevision: 'synthetic-source-revision-A',
builderIdentity: 'synthetic-builder-identity-A',
secretLabel: 'synthetic-secret-reference-no-value',
artifactDigest: 'synthetic-digest-artifact-A',
attestationSubjectDigest: 'synthetic-digest-artifact-A',
attestationDeclaration: 'synthetic-attestation-declaration',
});
const report = runSupplyChainFixture();
if (!Object.values(report.assertions).every(Boolean)) throw new Error('fixture failed');
console.log(flow.release.status); // manual-review-required
// Не создаёт secret, certificate, signature, attestation или deploy.
node web/scripts/upgrade-2023-06.mjs --verify-fixture
# PASS означает только: учебный договор непротиворечив.
# PASS не означает: secret защищён, statement подписан или deploy проверен.
Важная ветка fixture — совпадение artifactDigest и attestationSubjectDigest. Это минимальная защита от ошибки, когда красивый statement относится к другому output. Но совпадение двух строк не проверяет криптографию, доверие к builder или содержимое образа. В реальной цепочке такой результат становится лишь входом для следующей проверки. Если проект не знает, кто её выполняет и где хранится результат, лучше оставлять статус «не подтверждено», а не превращать fixture в формальный допуск.
Маршрут: симптом → причина → проверка → действие
- Симптом. В release виден deploy, но нельзя назвать его source revision, digest или границу работы с секретом.
- Причина. Последний шаг хранит событие выкладки, а evidence промежуточных звеньев не имеют общего ключа и владельца.
- Проверка. Для одного учебного выпуска заполните строку на каждое звено таблицы. Прогоните fixture: он обязан отклонить отсутствие builder, непомеченный input, иной subject и value-like label секрета.
- Действие. Выберите один общий ключ сопоставления: digest артефакта. Рядом определите, какой реальный источник позднее подтвердит source, build, secret boundary, attestation и deploy input.
- Проверка границы. Отдельно спросите владельца CI, кто будет проверять подпись и identity. Ответ «в declaration написано» не является шагом проверки.
- Следующий шаг. Добавьте контракт evidence в release design и договоритесь о минимальном безопасном запуске проверки вне этого fixture.
Что откатывать, когда цепочка неполна
Rollback здесь не означает удалить секрет, отозвать сертификат или повернуть production-трафик. Учебная функция умеет только вернуть snapshot своей записи и помечает provenance как requires-verification, release как no-system-change. В настоящем выпуске сначала решают операционный вопрос: какой deploy input надо остановить, как сохранить рабочее доказательство без чувствительных данных и какой другой артефакт разрешён как известный. Это отдельный план с владельцем и средой; его нельзя вывести из одной attestation.
Опасная ошибка — признать неполную цепочку «безопасной», потому что текущий deploy уже прошёл. Правильный статус иной: выпуск недостаточно объяснён, поэтому решение о продолжении требует явного владельца и дополнительного evidence. Такой статус не обвиняет CI и не объявляет инцидент. Он не позволяет подменить неизвестность уверенным текстом, а значит уменьшает риск сделать необратимый шаг на ложной связи.
Границы модели и следующий рабочий шаг
Эта статья не содержит настоящего секрета, токена, адреса, командной политики, CVE, scanner data, CI log, registry или deploy. Она не создаёт и не проверяет certificate, signature или attestation и не заявляет, что какой-либо артефакт имеет provenance. Отдельно важно: даже подписанная декларация может говорить только о том, что конкретный statement был подписан. До проверки subject, доверенной identity, условий builder и связи с deploy она не доказывает ни происхождение, ни факт использования артефакта.
Следующий шаг для команды — выбрать один выпуск, построить карту из шести звеньев и обозначить у каждого владельца evidence. Если на любой стрелке нельзя назвать проверяемый факт, не достраивайте его предположением. Зафиксируйте пробел и сузьте задачу до одного реального вопроса: например, как сопоставить deploy input с digest, не публикуя чувствительные данные. Так системная практика растёт из наблюдаемой связи, а не из набора терминов.
Историческая граница июня 2023
К июню 2023 уже были доступны стабильная SLSA v1.0, опубликованная 19.04.2023, и Cosign v2.0.0, опубликованный 24.02.2023; SSDF v1.1 был финальным документом NIST с февраля 2022. В статье используются только их версии и общий понятийный аппарат того периода. Поздние возможности платформ доставки, современные форматы bundle и текущие интеграции не приписываются автору 2023 года.
Проверяемые источники
- SLSA: SLSA v1.0 is now final!, 19 апреля 2023 — официальное объявление фиксирует первый стабильный выпуск SLSA v1.0, доступный к июню 2023. Сам факт выпуска не доказывает соответствие какого-либо pipeline.
- SLSA specification v1.0 — версионная спецификация описывает уровни и рекомендуемые форматы attestation, включая provenance. Сейчас версия помечена retired, но это сохранённая спецификация именно v1.0.
- sigstore/cosign: Release v2.0.0, 24 февраля 2023 — официальный release проекта Sigstore, опубликованный до июня 2023. Наличие инструмента и его формата не означает, что конкретный артефакт был им подписан или проверен.
- NIST SP 800-218 SSDF Version 1.1, Final, 3 февраля 2022 — официальное руководство NIST по практикам безопасной разработки и общему словарю для поставщиков и потребителей. Оно не заменяет evidence конкретного выпуска.