Trusted Publishing в npm: пошаговое руководство по настройке

В связи с участившимися атаками на цепочки поставок и последовавшим отзывом долгоживущих npm-токенов, разработчикам необходимо перейти на механизм доверенной публикации (trusted publishing). Официальная документация npm описывает общие принципы работы, на практике процесс настройки содержит ряд неочевидных требований. В статье рассмотрены шаги для настройки доверенной публикации через GitHub Actions, включая конфигурацию пакета, права доступа и корректные параметры публикации, а также разобраны типичные ошибки и способы их устранения.

Введение

В последнее время экосистема npm столкнулась с серией атак на цепочки поставок, наиболее известной стала атака с использованием червя Shai-Hulud 2.0. В ответ на это GitHub и npm предприняли ряд мер для повышения безопасности публикации пакетов. Ключевое изменение — отказ от долгоживущих npm-токенов в пользу короткоживущих токенов и механизма доверенной публикации (trusted publishing).

Для многих разработчиков, использующих автоматическую публикацию через GitHub Actions, это означает необходимость обновления своих рабочих процессов. Именно с такой ситуацией столкнулся и я: давно настроенный workflow, публикующий новые версии пакетов при создании тега, перестал работать из-за отзыва устаревших токенов.

Документация npm по доверенной публикации даёт общее понимание механизма, но, как выяснилось на практике, некоторые важные детали либо опущены, либо изложены недостаточно явно. Изучая вопрос, я обнаружил на GitHub обсуждение, где другие разработчики описывали те же трудности.

Цель статьи — собрать в одном месте все необходимые действия для успешного перехода на доверенную публикацию. Ниже — пошаговая инструкция, основанная на реальном опыте, с пояснением моментов, которые обычно вызывают вопросы.

Настройка доверенного издателя на npmjs.com

Первый шаг — указать npm, какому именно GitHub-репозиторию и какому workflow разрешено публиковать ваш пакет. Это делается в интерфейсе npm для каждого пакета отдельно.

Где искать настройки

Документация npm лаконично сообщает: «Перейдите в настройки пакета на npmjs.com и найдите раздел "Trusted Publisher"». Однако найти этот раздел с первой попытки удаётся не всем.

Ключевой нюанс: настройки доверенной публикации находятся не в общем списке ваших пакетов (по адресу https://www.npmjs.com/settings/ваш-username/packages), а на странице конкретного пакета в разделе управления доступом:

https://www.npmjs.com/package/название-вашего-пакета/access

Что нужно указать

На этой странице потребуется заполнить три поля:

  • Provider — выбираем GitHub (или GitLab, если используете его).
  • Repository — указывается в формате пользователь/репозиторий.
  • Workflow name — имя файла вашего workflow (например, publish.yml). Без этого npm не поймёт, какой именно процесс имеет право публиковать пакет.

Важное замечание о масштабировании

Процедуру необходимо повторить для каждого пакета, который вы планируете публиковать через доверенную публикацию. Если у вас один-два пакета — это не проблема. Для мейнтейнеров с десятками проектов процесс может оказаться утомительным, поскольку автоматизации на уровне списка пакетов на данный момент не предусмотрено.

После того как доверенный издатель настроен на стороне npm, можно переходить к обновлению самого проекта.

Подготовка проекта: что должно быть в package.json

Прежде чем переходить к настройке GitHub Actions, важно убедиться, что файл package.json вашего пакета содержит корректные данные. Некоторые проблемы с доверенной публикацией возникают именно из-за несоответствий в этом файле.

Указываем ссылку на репозиторий

Система доверенной публикации сверяет данные, которые вы указали на сайте npm, с тем, что написано в package.json. Если они разойдутся, публикация может завершиться ошибкой.

Поле repository должно содержать прямую ссылку на GitHub-репозиторий. Рекомендуемый формат:

{
"repository": {
"type": "git",
"url": "git+https://github.com/ваш-username/название-пакета.git"
}
}

Обратите внимание: git+https — это стандартный префикс для GitHub-ссылок в npm. Именно такую ссылку система ожидает увидеть при проверке.

Включаем provenance (подтверждение происхождения)

Одно из ключевых преимуществ доверенной публикации — автоматическое создание подтверждений происхождения кода. Они позволяют тем, кто устанавливает ваш пакет, убедиться, что он действительно собран и опубликован из указанного репозитория.

Включить provenance можно тремя способами. Первый — через настройки в package.json:

{
"publishConfig": {
"provenance": true
}
}

Преимущество этого способа в том, что настройка хранится прямо в коде пакета и не требует дополнительных флагов при каждом запуске публикации. Если вы предпочитаете управлять этим через переменные окружения или флаги командной строки, соответствующие варианты будут показаны в следующей части, при разборе GitHub Actions.

Модернизация GitHub Actions: пишем правильный workflow

Теперь, когда пакет настроен, а доверенный издатель зарегистрирован на npmjs.com, необходимо обновить сам скрипт публикации в GitHub Actions. Именно здесь возникает большинство сложностей, поскольку требования изменились, а документация описывает их не всегда полно.

Права доступа: OIDC-токен

Для работы механизма доверенной публикации GitHub Actions должен иметь возможность сгенерировать OIDC-токен (OpenID Connect). Этот токен npm использует для проверки подлинности вашего workflow.

Достаточно добавить одну строку в начало вашего workflow-файла или в описание конкретной job:

permissions:
id-token: write # Разрешение на создание OIDC-токена

Без этого разрешения GitHub не предоставит токен, и npm отклонит попытку публикации.

Обновление npm до актуальной версии

В документации npm указано важное требование: доверенная публикация работает только с npm версии 11.5.1 и выше. В виртуальном окружении GitHub Actions по умолчанию может стоять более старая версия.

Чтобы гарантировать совместимость, рекомендуется добавить отдельный шаг для обновления npm перед публикацией:

- name: Update npm to latest version
run: npm install -g npm@latest

Это простая мера предосторожности избавляет от ошибок, связанных с устаревшим клиентом.

Команда публикации: важность флага --provenance

Самый неочевидный момент, с которым столкнулся автор и другие разработчики в GitHub-обсуждениях, касается флага --provenance.

Документация npm утверждает, что при использовании доверенной публикации подтверждения (provenance attestations) добавляются автоматически, и указывать флаг явно не требуется. Однако на практике, по крайней мере при первой публикации, это может не сработать.

Надёжный вариант — указывать флаг явно:

- name: Publish package
run: npm publish --provenance

Возможно, флаг требуется только при первом запуске, после чего система запоминает настройки. Однако его постоянное присутствие в скрипте гарантирует работоспособность и не создаёт проблем.

Альтернативный способ: переменная окружения

Если по каким-то причинам вы не хотите добавлять флаг в команду npm publish, можно воспользоваться переменной окружения. Этот способ даёт тот же эффект, но управляется через конфигурацию окружения, а не через аргументы командной строки:

env:
NPM_CONFIG_PROVENANCE: true
run: npm publish

Выбор между флагом и переменной окружения — вопрос личных предпочтений или принятых в проекте стандартов оформления.

Полный пример рабочего workflow

Для наглядности соберём все описанные элементы в единый пример:

name: Publish package

on:
push:
tags:
- 'v*' # Запуск при создании тега с префиксом v

permissions:
id-token: write # Ключевое разрешение для trusted publishing

jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'

- name: Update npm
run: npm install -g npm@latest

- name: Install dependencies
run: npm ci

- name: Publish package
run: npm publish --provenance
env:
NODE_AUTH_TOKEN: $

Обратите внимание: переменная NODE_AUTH_TOKEN всё ещё может использоваться для аутентификации, но теперь она работает в паре с доверенной публикацией, а не заменяет её.

Часто задаваемые вопросы

Нужно ли удалять старый NPM_TOKEN из секретов GitHub после настройки trusted publishing?

Удалять токен не обязательно, но это может быть полезно для минимизации используемых секретов. Trusted publishing не требует токена для аутентификации — его заменяет OIDC-токен, который генерирует GitHub.

Однако если ваш workflow использует переменную NODE_AUTH_TOKEN для других целей (например, для установки приватных пакетов из npm-реестра), токен может оставаться необходимым. В таком случае он просто перестаёт отвечать за публикацию, но продолжает использоваться в других операциях.

Почему я получаю ошибку "403 Forbidden" при попытке публикации, хотя всё настроил по инструкции?

Ошибка 403 чаще всего указывает на проблемы с аутентификацией или несовпадение данных. Проверьте последовательно:

  1. Совпадает ли имя репозитория? Убедитесь, что в настройках доверенного издателя на npmjs.com указан именно тот репозиторий, который используется в workflow.
  2. Правильно ли указано имя workflow? В настройках требуется указать точное имя файла (например, publish.yml). Ошибка даже в одну букву приведёт к отказу.
  3. Есть ли разрешение id-token: write? Без этого разрешения GitHub не сгенерирует OIDC-токен.
  4. Корректен ли repository в package.json? Адрес репозитория должен точно совпадать с тем, что указан на npmjs.com.
Нужно ли добавлять флаг --provenance при каждой публикации или достаточно только первой?

Из опыта автора и других разработчиков, упоминавших эту проблему в GitHub-обсуждениях, надёжнее оставить флаг в скрипте на постоянной основе.

Даже если технически после первой успешной публикации флаг становится необязательным (как утверждает документация), его наличие не создаёт проблем. При этом оно гарантирует, что при копировании workflow в другой проект или при смене окружения публикация продолжит работать без дополнительных настроек.

У меня несколько десятков пакетов. Неужели нужно настраивать каждый вручную через веб-интерфейс?

К сожалению, на данный момент интерфейс npmjs.com не предоставляет инструментов для массовой настройки доверенных издателей. Однако процесс можно автоматизировать через npm API.

Пример запроса для добавления доверенного издателя через командную строку:

curl -X POST https://registry.npmjs.org/-/npm/v1/access/название-пакета/trusted-publishers \
-H "Authorization: Bearer ваш-токен-доступа" \
-H "Content-Type: application/json" \
-d '{
"type": "github",
"owner": "ваш-username",
"repo": "название-репозитория",
"workflow": "publish.yml"
}'

Для использования этого метода потребуется сгенерировать токен доступа к npm с соответствующими правами. Такой подход требует изучения документации API, но для поддержки большого количества пакетов он может быть оправдан.

Работает ли trusted publishing только с GitHub или есть другие провайдеры?

На момент написания статьи доверенная публикация поддерживается для двух провайдеров:

  • GitHub
  • GitLab

При настройке на npmjs.com нужно будет выбрать соответствующего провайдера в выпадающем списке. Механика настройки для GitLab аналогична описанной в статье, за исключением формата указания репозитория.

Что делать, если пакет уже опубликован, и я просто перевожу его на trusted publishing?

Процесс перехода не отличается от первичной настройки:

  1. Зарегистрируйте доверенного издателя на странице пакета.
  2. Обновите workflow в соответствии с рекомендациями из Части 3.
  3. Убедитесь, что package.json содержит корректную ссылку на репозиторий.

При следующей публикации пакет будет выпущен с использованием нового механизма. Существующие версии пакета остаются доступными, их обновлять не требуется.

Заключение

Переход на короткоживущие токены, доверенную публикацию и подтверждение происхождения (provenance) — это не просто ужесточение правил со стороны npm и GitHub. Это важный шаг к повышению безопасности всей экосистемы. Чем больше пакетов будет публиковаться с использованием trusted publishing, тем сложнее злоумышленникам будет внедрять вредоносный код в цепочки поставок.

Предложенная в статье последовательность действий основана на реальном опыте обновления рабочего процесса. Возможно, у вас есть десятки пакетов, и настройка каждого вручную покажется утомительной. Или, как в моём случае, вы обновляете пакет, которым, скорее всего, пользуетесь только вы. В любом случае, понимание механизма доверенной публикации и умение его настраивать — полезный навык для любого поддерживающего npm-пакеты разработчика.

На момент написания статьи trusted publishing поддерживается для GitHub и GitLab. Хочется надеяться, что со временем список провайдеров будет расширяться, а доля пакетов, публикующихся через этот механизм, — расти. И, конечно, хочется верить, что атак вроде Shai-Hulud 2.0 в будущем станет меньше.

Если у вас остались вопросы или вы столкнулись с нюансами, не описанными в статье, — пишите в комментариях. Опыт каждого помогает сделать экосистему безопаснее.

Комментарии


Дополнительные материалы

Предыдущая Статья

Чистое API для чтения PHP-атрибутов