ADG Оставить заявку
Блог Информационная безопасность 5 мин чтения

Supply chain Q1 2026: два npm-инцидента и как мы закрыли вход через Harbor + Cosign + Trivy

Два supply chain инцидента в Q1 2026 затронули популярные npm-пакеты. Рассказываем, как настроили Harbor, Cosign и Trivy для блокировки непроверенных образов до staging.

Контекст момента

Атаки на цепочку поставок ПО продолжают расти - новые инциденты в open source в начале 2026

В январе и феврале 2026 года supply chain снова отметился. Два отдельных инцидента с npm: в первом - компрометация учётки мейнтейнера небольшой, но популярной утилиты для работы с конфигами, во втором - typosquatting под реальный пакет с дополнительной нагрузкой в postinstall-скрипте. Оба разобрали в публичных postmortem'ах, оба вектора не новые. Но каждый раз находятся команды, для которых инцидент становится поводом наконец разобраться со своим пайплайном.

У нас после тех событий выстроилась очередь из клиентов с примерно одинаковым вопросом: «мы тянем зависимости из публичного npm, у нас есть Harbor, но как конкретно настроить, чтобы непроверенный образ вообще не попал на staging?» Это хороший вопрос, и ответ на него конкретнее, чем кажется.

Где обычно стоит дыра

Типовая картина, которую мы видим при аудите в подобных случаях: Harbor стоит, образы в него пушатся, но на этом история заканчивается. Harbor как хранилище без политик - это просто S3 с красивым UI. Образ, собранный с вредоносной npm-зависимостью, спокойно ляжет рядом с легитимным, и ничего его не остановит.

Три вещи, которых не хватает в такой схеме:

  • Сканирование на CVE и вредоносный код - Trivy умеет встроиться в Harbor как сканер; можно выставить политику, при которой образ с незакрытыми критическими уязвимостями вообще не будет помечен как доступный.
  • Подпись образов - Cosign плюс Notation (Harbor поддерживает оба) позволяют подписать образ после сборки и верифицировать подпись перед деплоем. Образ, которого нет в transparency log, не проходит.
  • Admission control в Kubernetes - без политики на уровне кластера все предыдущие шаги носят рекомендательный характер. Kyverno или OPA Gatekeeper закрывают последний рубеж.

Как мы это настраивали

Работали с одним из клиентов, у которых стек - GitLab CI, Harbor 2.11, Kubernetes 1.34 с Kyverno. Цель: образ, собранный из кода с вредоносной зависимостью, не должен попасть на staging-окружение.

Первый шаг - Trivy в Harbor. В настройках Harbor включается встроенный Trivy-сканер и выставляется порог: образы с критическими CVE без фикса блокируются при pull. Это работает через interceptor на уровне registry API. Важный нюанс: «блокируются при pull» - не при push. Образ попадает в Harbor, сканируется, и только после этого становится доступным или недоступным для pull. Первое время это удивляет команду - образ есть в реестре, но не тянется. Надо объяснять заранее.

Для npm-специфики Trivy умеет сканировать node_modules внутри образа и находить known-bad пакеты через базу advisories. Это не панацея от свежих атак - база обновляется с задержкой, - но typosquatting из февральского инцидента Trivy поймал бы: пакет уже был помечен как malicious в GHSA к моменту, когда мы это проверяли.

Второй шаг - Cosign и подпись в CI. После сборки образа и его прохождения Trivy-сканирования добавили stage подписи:

sign-image:
  stage: security
  image: registry.internal/devtools/cosign:2.4
  needs: [build, trivy-scan]
  id_tokens:
    SIGSTORE_ID_TOKEN:
      aud: sigstore
  script:
    - cosign sign --yes
        --oidc-issuer ${CI_SERVER_URL}
        --identity-token ${SIGSTORE_ID_TOKEN}
        ${HARBOR_HOST}/${CI_PROJECT_PATH}:${CI_COMMIT_SHA}
  only:
    - main
    - /^release\/.*/

OIDC-интеграция с GitLab означает, что долгоживущих ключей нет - подпись привязана к identity конкретного pipeline. Подпись и прозрачность через Rekor - стандартная схема для Cosign keyless.

Harbor 2.11 умеет хранить Cosign-подписи как OCI-артефакты рядом с образом. Ничего дополнительно настраивать не пришлось - подпись автоматически ассоциируется с дайджестом образа.

Третий шаг - Kyverno-политика. Без этого шага первые два - просто метаданные без последствий. Политика запрещает запуск Pod'ов с образами без валидной Cosign-подписи:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-signed-images
spec:
  validationFailureAction: Enforce
  rules:
    - name: check-image-signature
      match:
        any:
          - resources:
              kinds: [Pod]
              namespaces: [staging, production]
      verifyImages:
        - imageReferences:
            - "harbor.internal/*"
          attestors:
            - count: 1
              entries:
                - keyless:
                    issuer: "https://gitlab.internal"
                    subject: "https://gitlab.internal/org/*"
                    rekor:
                      url: "https://rekor.sigstore.dev"

Namespace staging и production - явно. Dev-окружение намеренно оставили без принудительной верификации: разработчики локально собирают образы, которые не проходят через CI-подпись, и блокировать dev слишком болезненно.

Что получилось и что не получилось

Схема работает. Образ из вредоносного npm-пакета типа февральского инцидента не попадёт на staging по двум причинам: Trivy поймает known-bad пакет при сканировании в Harbor, и даже если Trivy пропустит (база устарела) - образ не подписан через легитимный CI-pipeline, и Kyverno его заблокирует.

Но честно про ограничения:

  • Latency базы Trivy - новый вредоносный пакет первые несколько дней после публикации не будет в базе. Это не недостаток Trivy, это природа threat intel.
  • Cosign keyless через публичный Rekor - часть клиентов не готова к тому, что metadata о подписях уходит во внешний публичный лог. Для них нужен self-hosted Rekor, а это отдельная инфраструктура.
  • Dev-namespace дыра - да, мы её оставили осознанно, но это компромисс. Если в dev попадёт контейнер с чем-то плохим, он может навредить dev-данным или CI-пайплайну. Идеальным решением был бы отдельный, менее строгий Kyverno-профиль для dev, а не полное отсутствие политики.

Supply chain - это не задача, которую решают один раз. Это постоянный процесс: следить за новыми векторами, проверять, что существующие контроли их покрывают. После каждого публичного инцидента мы проходимся по своим клиентам и смотрим, что нужно поправить. Иногда это быстро, иногда - как в этот раз - превращается в нормальный проектный спринт.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.