Защита CI-CD в open source-проекте, часть 3: учётные данные, верификация и что дальше

Команда VK Cloud перевела заключительную часть цикла Cilium про защиту цепочки поставок. Часть 1 была про контроль доступа, часть 2 — про укрепление зависимостей. Эта же часть о том, как изолировать секреты CI и продакшена за разными окружениями GitHub, подписывать каждый релиз без долгоживущих ключей через Sigstore Cosign, и какие пробелы безопасности остаются открытыми. Отдельно разбор дорожной карты безопасности GitHub Actions на 2026 год и того, как платформенные изменения соотносятся с уже выстроенными контролями. Полезно DevOps- и SRE-инженерам, специалистам по безопасности и мейнтейнерам OSS-проектов.
Защита учётных данных
Мы исходим из того, что любой отдельный слой может отказать. Если CI-workflow когда-нибудь скомпрометируют, важно, чтобы атакующий не дотянулся до чего-то значимого.
Сильные значения по умолчанию
По умолчанию наши GITHUB_TOKENs ограничены минимальными правами на чтение для contents и packages. Workflow, которым нужно больше, обязаны подключить это явно. Поэтому workflow, который забыл объявить разрешения, не получает широкого права записи на всю организацию.
Мы держим два разных набора учётных данных реестра за отдельными защищёнными окружениями GitHub:
-
Учётные данные CI могут пушить в реестр образов для разработки (
quay.io/cilium/*-ci) и доступны CI-сборкам. Даже если CI-workflow скомпрометируют, этими данными нельзя запушить в продакшен-теги образов. -
Продакшен-данные лежат за окружением
release, и до них workflow доберётся только после явного одобрения мейнтейнера. Ни форк, ни feature-ветка, ни CI-сборка к этим секретам не доберутся. Только релизные сборки по тегу, одобренные мейнтейнером.
В худшем случае при компрометации CI атакующий опубликует вредоносный -ci-образ. Но не опубликует в quay.io/cilium/cilium:v1.x.x или docker.io/cilium/cilium:v1.x.x. Этих учётных данных просто нет на раннере.
Каждый вызов actions/checkout также ставит persist-credentials: false, поэтому GITHUB_TOKEN никогда не попадает в git config раннера, где его мог бы перехватить более поздний шаг.
Подписание и аттестация того, что мы выпускаем
Каждый контейнерный образ из релиза (cilium, operator-*, hubble-relay, clustermesh-apiserver) подписан через Sigstore Cosign с keyless (без ключей) OIDC. Долгоживущих ключей подписи, которые можно украсть, нет.
Конвейером подписания управляет переиспользуемое composite-действие:
.github/actions/cosign/action.yaml
- name: Install Cosign
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
- name: Generate SBOM
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0
with:
artifact-name: sbom_${{ inputs.sbom_name }}.spdx.json
output-file: ./sbom_${{ inputs.sbom_name }}.spdx.json
image: ${{ inputs.image_tag }}
- name: Sign Container Image
shell: bash
run: cosign sign -y "${{ inputs.image }}"
- name: Attach SBOM Attestation
shell: bash
run: |
cosign attest -y
--predicate "./sbom_${{ inputs.sbom_name }}.spdx.json"
--type spdxjson
"${{ inputs.image }}"
Это выполняется для каждой сборки релизного образа и для наших OCI-артефактов Helm chart. Инструкции по верификации есть в документации Cilium.
Релизные сборки тоже идут внутри защищённых окружений (release, release-tool, release-helm), поэтому продакшен-данные реестра ограничены правилами защиты окружений. Запустить релизную сборку из форка или feature-ветки нельзя.
Команда безопасности Cilium
Если вы когда-нибудь сообщали проекту о проблеме безопасности (через GitHub security advisories или security@cilium.org), вы уже общались с командой безопасности Cilium. Помимо триажа отчётов об уязвимостях, команда ведёт и операционную сторону безопасности цепочки поставок:
-
Аудит и ротация учётных данных и разрешений по всей GitHub-организации.
-
Расследование инцидентов и аудиты, когда это нужно.
-
Мониторинг паттернов в наших проблемах безопасности и отраслевых событиях, чтобы предлагать меры снижения рисков и контроли там, где наша защищённость слаба.
Дополнительные слои
Несколько мелочей, которые стоит упомянуть:
-
Неизменяемость тегов. После публикации GitHub-релиза привязанные к нему теги и ассеты изменить нельзя. Настройка — на странице Settings → Releases репозитория.
-
Принудительный DCO sign-off. Каждый коммит обязан нести строку
Signed-off-by. Наша конфигурация maintainers-little-helper блокирует мёрджи меткойdont-merge/needs-sign-off, пока sign-off нет. -
Сторонние аудиты безопасности. Нас аудитировала ADA Logics, и мы поддерживаем опубликованную модель угроз.
Над чем мы ещё работаем
Мы сверили наш каталог .github/ с текущими лучшими практиками (OpenSSF Scorecard, SLSA, рекомендации StepSecurity) и нашли несколько реальных пробелов. Из крупных:
-
Нет SLSA provenance. Каждый вызов
docker/build-push-actionставитprovenance: false. Мы подписываем образы через Cosign, но не генерируем аттестации SLSA build provenance. Потребитель может проверить, кто подписал образ, но не как его собрали. Принятьslsa-framework/slsa-github-generator(или хотя бы включить BuildKit-native provenance) — в планах. -
Нет dependency review во время PR. Мы полагаемся на
vulnerabilityAlertsRenovate, чтобы помечать известные уязвимые зависимости, но это реактивно. actions/dependency-review-action перехватывало бы вредоносные или уязвимые новые зависимости до мёрджа. -
Нет
govulncheckв CI. Мы фаззим и линтуем, но пока не запускаем официальный сканер уязвимостей Go. Он проверяет, действительно ли наш код вызывает уязвимые функции, а не просто появляется ли уязвимый пакет в go.sum. -
68 внутренних ссылок @main. Куча workflow для conformance- и scale-тестов ссылается на
cilium/cilium/.github/actions/set-commit-status@main— это изменяемая branch-ссылка. Риск меньше, чем у стороннего тега, но это расходится с нашей политикой SHA-пиннинга. План: вынести все наши composite-действия из cilium/cilium в выделенный репозиторий, и тогда @main здесь не понадобится.
Ещё несколько пунктов поменьше из того же аудита:
-
Нет workflow OpenSSF Scorecard для непрерывного мониторинга здоровья цепочки поставок.
-
Наш
SECURITY-INSIGHTS.ymlистёк в январе 2025 года и не обновлялся. (Мы, собственно, и заметили это, пока писали этот пост.) -
Нет шага
go mod verifyдля проверки целостности vendor-директории по контрольным суммам go.sum.
Если что-то из этого выглядит как хороший first issue, пришлите PR.
Дорожная карта безопасности Actions от GitHub на 2026 год и как она соотносится с тем, что мы делаем
В апреле 2026 года GitHub опубликовали дорожную карту безопасности Actions с изменениями уровня платформы по трём слоям: экосистема, поверхность атаки и инфраструктура. Читать её было как получить подтверждение проблем, которые мы годами обходили, и реальный сигнал, что платформа наконец догоняет потребности крупных open source-проектов. Вот как это соотносится с тем, что мы делаем сегодня.
Блокировка зависимостей: делаем SHA-пиннинг первоклассным
Мы пиннуем каждое действие по SHA и опираемся на Renovate, чтобы держать эти пины актуальными, но для транзитивных ссылок у нас всё ещё слепое пятно. Запланированная GitHub секция dependencies: в workflow YAML заблокировала бы все прямые и транзитивные dependencies по commit SHA, с проверкой хэша до начала выполнения. Это закрывает пробел.
Выполнение на основе политик: централизуем то, что сегодня принуждаем по файлам
Мы ограничиваем, кто может запускать workflow (allow-list от Ariane), какие события разрешены (конфигурация на уровне workflow) и кто может одобрять релизы (защищённые окружения). Всё это сейчас зашито в десятках YAML-файлов плюс кастомный бот, и чтобы увидеть полную картину, нужно прочитать каждый файл.
Запланированные GitHub защиты выполнения workflow на основе rulesets позволили бы задать эти контроли централизованно, на уровне организации: какие акторы могут запускать workflow, какие события разрешены, к каким репозиториям применяются правила. Мы могли бы запретить pull_request_target на всю организацию, кроме workflow, для которых мы намеренно спроектировали безопасный двухфазный checkout, вместо того чтобы полагаться на код-ревью и CODEOWNERS, которые это обеспечивают.
Scoped secrets: закрываем пробел неявного наследования
Изоляция учётных данных CI и продакшена — один из наших сильнейших контролей, но внутри одного окружения секреты всё ещё видны слишком широко: их может получить любой workflow, который в этом окружении выполняется.
Секреты с областью видимости (scoped secrets) позволили бы привязывать учётные данные к конкретным путям workflow, веткам или даже отдельным переиспользуемым workflow. Релизные данные можно было бы ограничить не просто окружением release, а конкретным файлом workflow release.yaml, тогда новый workflow, добавленный в это окружение (по случайности или атакующим), не унаследовал бы учётные данные.
Дорожная карта также отделяет управление секретами от права записи в репозиторий. GitHub планирует перенести управление секретами в отдельную кастомную роль. Это в духе принципа минимальных привилегий, который мы уже применяем к разрешениям workflow, но пока не можем применить к администрированию секретов.
Нативный egress-файрвол
Запланированный GitHub нативный egress-файрвол ограничивал бы исходящий сетевой доступ с размещённых на GitHub раннеров. Он работает вне VM раннера, на L7, поэтому неизменяем, даже если атакующий получит root внутри раннера. Организация задавала бы разрешённые домены, IP-диапазоны и HTTP-методы, а всё остальное блокируется.
Для Cilium это менее критично, чем остальное. Наши самые чувствительные к безопасности workflow уже выполняются с изоляцией учётных данных и минимальными привилегиями, что ограничивает скомпрометированный шаг даже при неограниченном сетевом доступе. Построить точный egress allow-list было бы заметным куском работы. Публичное превью ждут через 6–9 месяцев — тогда и оценим.
Поток данных Actions: делаем CI наблюдаемым
Наши workflow пишут логи, но централизованной телеметрии по ним нет. Если workflow начнёт вести себя странно (резолвить неожиданные зависимости, выполняться дольше обычного, делать странные сетевые вызовы), нам пришлось бы заметить это вручную.
Actions Data Stream доставлял бы телеметрию выполнения почти в реальном времени во внешние системы (S3, Azure Event Hub) — детали выполнения workflow, паттерны разрешения зависимостей и в конечном счёте сетевую активность.
Суть
Безопасность цепочки поставок — это в основном практика снова и снова спрашивать «а что, если эта штука, которой я доверяю, окажется скомпрометирована?» и добавлять слой, который ограничивает радиус поражения, когда это случается.
Мы постарались выстроить эшелонированную защиту: контроли доступа — чтобы сборки запускали только доверенные люди; запиннованные дайджесты — чтобы скомпрометированный тег до нас не добрался; минимальные привилегии — чтобы зловредное действие не вынесло секреты; изоляцию учётных данных — чтобы CI никогда не дотянулся до продакшена; и подписи — чтобы пользователи могли проверить то, что запускают.
Ничто из этого не делает нас неуязвимыми. Но безопасность через неясность работает плохо, и обратное тоже верно: чем больше open source-проектов открыто делятся своими защитами, тем выше общая планка для атакующих. Мы показали вам свои, включая части, которые пока не очень хороши. Если вы держите CI/CD для open source-проекта и решили то, что не решили мы, — откройте issue, напишите свой пост. Цепочка поставок open source сильна настолько, насколько силён её слабейший проект, и укрепить её можно только вместе.
Автор: levashove

