
Проникновение через зависимости и инфраструктуру сборки стало одним из самых дорогих типов инцидентов. Попадание вредоносного кода в ваш образ контейнера приводит к:
Хорошая новость: базовая защита цепочки поставки внедряется постепенно и без радикальной перестройки. Достаточно трёх вещей:
Этого уже достаточно, чтобы:
Цепочка поставки — путь от исходного кода до запуска в продакшене:
Уязвимости и подмены могут произойти на каждом шаге. Наша задача — «привязать» артефакт к прозрачной истории происхождения и отсеивать все неизвестные и небезопасные варианты на этапе деплоя.
Эта связка даёт быструю отдачу: повышает контроль без удлинения пайплайна релизов на недели.
Это резко снижает риск «тихой» подмены зависимости.
Cosign позволяет подписывать образы и хранить подписи прямо в реестре. Можно использовать «keyless» режим через OIDC — без ручного управления ключами.
Пример GitHub Actions для сборки, подписи и публикации образа в ghcr.io:
name: build-sign-and-push
on:
push:
branches: ["main"]
permissions:
contents: read
id-token: write # нужно для keyless-подписи Cosign
packages: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}/app:sha-${{ github.sha }}
- name: Install Cosign
uses: sigstore/cosign-installer@v3.5.0
- name: Sign image with Cosign (keyless)
env:
COSIGN_EXPERIMENTAL: "1"
run: |
cosign sign --yes ghcr.io/${{ github.repository }}/app:sha-${{ github.sha }}
Подпись будет связана с вашим репозиторием и коммитом через OIDC GitHub.
SBOM — это машинночитаемый перечень компонентов и версий, из которых состоит образ. Рекомендуемые форматы: SPDX или CycloneDX. Пример с Syft:
- name: Generate SBOM (SPDX) with Syft
uses: anchore/sbom-action@v0
with:
image: ghcr.io/${{ github.repository }}/app:sha-${{ github.sha }}
format: spdx-json
output-file: sbom.spdx.json
- name: Upload SBOM as artifact
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.spdx.json
Рекомендуется также положить SBOM в тот же реестр, как аттестацию cosign:
cosign attest \
--yes \
--predicate sbom.spdx.json \
--type spdx \
ghcr.io/OWNER/REPO/app:sha-COMMIT_SHA
Можно использовать Trivy или Grype. Пример с Trivy в CI:
- name: Scan image with Trivy
uses: aquasecurity/trivy-action@0.20.0
with:
image-ref: ghcr.io/${{ github.repository }}/app:sha-${{ github.sha }}
format: 'table'
exit-code: '1'
vuln-type: 'os,library'
severity: 'CRITICAL,HIGH'
При наличии критичных или высоких уязвимостей задача завершится с ошибкой — деплой не пойдёт дальше.
Kyverno — политик‑движок для Kubernetes. Он умеет проверять подписи cosign и блокировать неподписанные образы. Устанавливаем Kyverno и включаем политику валидации.
Пример политики, которая разрешает развертывание только образов, подписанных через OIDC GitHub Actions из вашего репозитория:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-signed-images
spec:
validationFailureAction: Enforce
background: true
rules:
- name: require-cosign-signature
match:
any:
- resources:
kinds: ["Pod", "Deployment", "StatefulSet", "DaemonSet", "Job", "CronJob"]
verifyImages:
- image: "ghcr.io/your-org/*"
keyless:
issuer: "https://token.actions.githubusercontent.com"
subject: "repo:your-org/your-repo:ref:refs/heads/main"
repository: "ghcr.io"
Теперь любые манифесты с неподписанными образами будут отклоняться сервером API кластера.
Вариант 1: проверять в CI/CD и не применять манифесты при провале. Вариант 2: добавить ещё одну политику, проверяющую наличие аттестации SBOM (для продвинутых сценариев можно использовать Kyverno attestations или OPA с внешним валидатором). На первом этапе достаточно «красного светофора» в пайплайне и дашборда, показывающего статус SBOM/скана для каждого релиза.
# этап сборки
FROM golang:1.22 as builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /bin/app ./cmd/app
# финальный образ без лишнего
FROM gcr.io/distroless/base-debian12
USER 65532:65532
COPY --from=builder /bin/app /app
EXPOSE 8080
ENTRYPOINT ["/app"]
cosign verify ghcr.io/your-org/your-repo/app:sha-<commit> \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity "repo:your-org/your-repo:ref:refs/heads/main"
trivy image --severity CRITICAL,HIGH --exit-code 1 ghcr.io/your-org/your-repo/app:sha-<commit>
Грубая оценка: если инцидент обходится бизнесу в N млн, а вероятность снижается хотя бы на десятки процентов, окупаемость наступает после первого «несостоявшегося» инцидента. Плюс репутационный эффект и ускорение продаж в enterprise‑сегменте.
Небольшой набор практик — подпись образов, SBOM и проверка на деплое — даёт бизнесу контролируемые релизы и предсказуемые риски. Это не «мода», а базовая гигиена поставки ПО. Внедрить можно поэтапно, не ломая процессы и не тормозя time‑to‑market: начните с одной команды и одного кластера — и за пару недель получите измеримый результат.