Hiratake Web ロゴ

手動でトークンを更新するのがめんどいので npm の Trusted Publisher の設定をする

投稿した日
更新した日
書いたひと
icon
ひらたけ

npm パッケージのサプライチェーン攻撃のニュースを頻繁に目にするようになった昨今。そんな中、先日 npm からトークンに関する変更の通知がメールで送られてきました。有効期限の長いトークンは良くないので 有効期限が無期限な Classic Token を 11 月上旬に廃止する とのこと。

Classic Token の他には Granular Access Token というトークンも発行できますが、こちらも有効期限に任意の日付を指定できたものが 最長 90 日に制限される ことに。

私はいくつかの npm パッケージを自作して公開しているのですが、セキュリティ的に大事なこととはいえ、頻繁にトークンを変更するのは大変。最長 90 日という有効期限も、今後はさらに短くなることがあるかもしれません。というわけで、そんなトークンの更新をしなくてもパッケージの公開ができる Trusted Publisher の設定をしてみることに。とりあえずパッケージを公開することができたので、備忘録的な感じで手順を書いておこうと思います。


Trusted Publisher は、OpenID Connect(OIDC) という仕組みを使って、GitHub Actions や GitLab CI/CD パイプラインから直接 npm パッケージを公開する仕組みとのこと。

あらかじめ設定さえしておけば、GitHub Actions や GitLab CI/CD パイプラインからであれば、こちらからトークンを発行して設定して…と管理をしなくても、ワークフロー実行時に専用トークンが発行されて良い感じに処理してくれる…らしい。設定できる Granular Access Token の最大有効期限がどれだけ短くなろうとも、そもそもトークンを発行しなくても良くなるので、期限が切れるたびにトークンを更新する作業が不要になります。

Trusted Publisher を設定してみる

npm で設定を行うパッケージの設定画面を開き、「Trusted Publisher」という見出しの部分から設定を進めます。現時点では「GitHub Actions」と「GitLab CI/CD」が設定可能となっていて、この投稿では GitHub Actions で設定を行います。

npmのパッケージ設定画面の画像

アスタリスクが付いている必須項目に、それぞれ情報を入力します。ユーザ名に大文字が含まれている場合、その部分はちゃんと大文字で入力する必要があります。私は小文字で入力してしまったせいで、エラーになる原因が分からず時間を溶かしました。

  • Organization or user - 公開するパッケージのリポジトリが含まれる組織もしくはユーザのユーザ名
  • Repository - 公開するパッケージのリポジトリ
  • Workflow filename - リリース時に実行するワークフローのファイル名

また、Trusted Publisher の設定の下にある「Publishing access」で、recommended が付いている二要素認証必須かつトークンの禁止を選択してセキュリティレベルを向上させます。項目名に disallow tokens とありますが、Trusted Publisher でのパッケージ公開は行うことが可能です。

パッケージの設定を保存後、GitHub のリポジトリを開いて GitHub Actions のリリース用ワークフローを変更します。npm のドキュメント にある通り、permissionsid-token: write が必要なのと、npm のバージョンが 11.5.1 以上でないと動作しないため npm install -g npm@latest などを実行して npm の更新を行う必要があります。

以下は、パッケージマネージャーに pnpm を使用かつ semantic-release でバージョン番号を決めたりリリースノートを生成したりしているパッケージでのリリースワークフローです。

name: Release

on:
  workflow_dispatch:

permissions:
  contents: read

jobs:
  release:
    name: Release
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest]
        node: [22]
    timeout-minutes: 10

    permissions:
      contents: write
      issues: write
      pull-requests: write
      id-token: write # Required for OIDC

    steps:
      - name: Checkout
        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8

      - name: Setup pnpm
        uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061
        with:
          run_install: false

      - name: Setup Node.js
        uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903
        with:
          node-version: ${{ matrix.node }}
          registry-url: 'https://registry.npmjs.org'
          cache: 'pnpm'

      # Ensure npm 11.5.1 or later is installed
      - name: Update npm
        run: npm install -g npm@latest

      - name: Install dependencies
        run: pnpm install

      - name: Check dependencies
        run: npm audit signatures

      - name: Release package
        run: pnpm semantic-release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

もともと設定していたワークフローから変更した点は、上記の permissionsid-token: write の追加と、npm を最新バージョン(11.5.1 以上)へ更新するため npm install -g npm@latest コマンドの実行の追加に加えて、actions/setup-noderegistry-url: 'https://registry.npmjs.org' を設定した点と、これまで必要だった npm のトークンを環境変数に設定していたものを削除した点の 4 つです。

ワークフローを更新後、GitHub のリポジトリに設定している npm のトークンを削除します。これで準備は完了です。試しにワークフローを実行してみると、無事パッケージの新しいバージョンをリリースすることができました🎉

まとめ

GitHub Actions を利用する場合は、以下の方法で対応できました。

  • npmjs.com のパッケージの設定で「Trusted Publisher」を画面の指示に従って設定する
  • リリース用のワークフローのファイルを変更する
    • permissionsid-token: write を追加する
    • actions/setup-node 実行時に registry-url: 'https://registry.npmjs.org' を指定する
    • パッケージ公開の処理を実行する前に npm を 11.5.1 以上に更新する(npm install -g npm@latest を実行するなど)
    • Trusted Publisher の設定によって不要になる npm のトークンを削除する

メールで通知が届いたときは「なんか色々変わるっぽいけど…何したらいいんだ?」と困惑していましたが、定期的にトークンを更新する手間が増えるわけでもなくひと安心。手動でトークンを発行しなくても良い仕組みが広がることで、サプライチェーン攻撃が少しでも減ることに期待ですね。