tech.guitarrapc.cóm

Technical updates

Trusted PublishingでNuGetパッケージ公開をトークンレスにする

NuGet Trusted Publishingが2025年9月22日にアナウンスされ、OpenID Connect (OIDC)を使ってトークンレスでCIからNuGetパッケージを公開できるようになりました。

今回は、NuGet Trusted Publishingを使ってGitHub ActionsからトークンレスでNuGetパッケージを公開するメリットと手順を解説します。NuGet Trusted Publishingは積極的に使っていきましょう。

はじめに

NuGetに限らず、多くのパッケージシステムはPersonal Acces Token(PAT)を用いています。PATはシンプルで使いやすい反面、長期間有効であることや手動ローテーションが必要なことから、セキュリティ上の課題が指摘されています。

例えば利用者が特に多いnpmは、パッケージ作者から詐取したPATを使ってマルウェア入りパッケージを公開する事件が何度も発生しています。npmにおけるパッケージPAT認証の課題に対するGitHubの対策は「TOTPからPasskeyベースへの移行 (認証のフィッシング対策強化)」と「OIDCを使ったトークンレスパッケージ公開(Trusted Publishing)」です。npmにおいてTrusted Publishingが先行していているので、本記事と合わせて読むと参考になる記事を紹介します

本記事で紹介するNuGetのTrusted Publishingは、細かい違いはありつつもnpm Trusted Publishingと同様の仕組みを導入した考えて差し支えありません。

NuGetパッケージ公開の課題

C#や.NETでライブラリを配布する場合、.nupkgにパックしてnuget.orgへ公開します。従来、NuGetへパッケージ公開するには、PAT1を発行してdotnet nuget pushコマンドに渡す必要がありました。npm同様に、ここ数年はPATの課題が指摘されています。

  • PATは長期間有効なので漏洩リスクが高い (最短1週間)
  • PATは手動ローテーションなので運用コストが高い (最長1年)
  • PATをCI/CD環境へ安全に渡す必要がある
  • PATが漏れたらだれでもどこからでもパッケージを公開できてしまう

Trusted Publishingのメリットとリスク

トークンレスパッケージ公開は、「手元からパッケージ公開せずパッケージ公開はCI/CDからのみ行う」、という前提を置くことでPAT管理の煩雑さを解消します。

  • 自動発行されたPATは短命
  • ユーザーによるPATローテーションが不要になる
  • CI/CD環境にPATを保存する必要がなくなる
  • 登録したCI/CD環境(リポジトリ、ワークフローファイル名)だけパッケージを公開できる

トークンレスパッケージ公開を導入しても、以下のようなリスクは残ります。

  • CI/CD環境へ不正アクセスされた場合、パッケージを公開されるリスクがある
  • あくまでもパッケージ公開の認証を強化するものであり、パッケージ内容の改ざんやマルウェア混入を防止するものではない

NuGet Trusted Publishingの概要

Trusted PublishingはOIDCを使ってパッケージ公開時のサービス認証を行う仕組みです。OIDCの基本的な仕組みについては、業界で取り組んでいるOpenSSFイニシアチブ)を参照するとフローや概念がよくわかります。

alt text

NuGet Trusted Publishingを用いると、事前登録したCI/CD環境からのリクエストに対して短時間のみ有効なトークンを返し、これを使ってパッケージ公開できます。NuGet Trusted PublishingのOIDC認証フローは次の通りです。

  1. CI/CD環境(GitHub Actions)がOIDCトークンを発行
  2. NuGetにOIDCトークンを送りパッケージ公開用PATを取得(短命)
  3. PATを使ってNuGetパッケージを公開

Trusted Publishingを利用するには、GitHubからの公開設定を登録する必要があります。設定の流れは次の通りです。

  • nuget.orgでTrusted Publishingのポリシーを設定
  • CI/CDはポリシーに沿った設定でNuGetパッケージ公開

なお、NuGet Trusted PublishingはCI/CD環境としてGitHubに対応している一方、他のCI/CD環境は対応していません。2

設定方法

NuGet Trusted Publishingの設定手順を見ていきましょう。

nuget.orgでTrusted Publishingのポリシーを設定

Nugetにログインして、アカウントメニュー -> Trusted Publishingを開きます。

trusted publishing

Createをクリックして、Trusted Publishingポリシーを作成します。下は私の管理しているSkiaSharp.QrCodeリポジトリの設定例です。

  • ポリシー名は任意の名前でOK。識別しやすいようにリポジトリ名などを入れておくとよさそう
  • Package OwnerはNuGetのアカウントを指定。ここでOrgアカウントを選択すれば、NuGet Orgアカウントのパッケージが対象となる
  • Repository OwnerはGitHubのリポジトリ所有者名を指定。GitHub Orgのパッケージなら、GitHub Organization名を指定
  • Repository Nameはリポジトリ名を指定
  • Workflow Fileは、GitHub Actionsワークフローのファイル名を指定。注意書きにあるように、.github/workflows/以下の「ファイル名」のみを指定

Policy

GitHub Actionsワークフローの設定

GitHub Actionsのワークフローを設定します。ポイントは次の通りです。

  • ワークフローファイル名はNuGet Trusted Publishingポリシーで指定したWorkflow Fileと一致させる
  • Nuget/loginアクションを実行するジョブにid-token: writeの権限を付与する3
  • NuGet/loginアクションを使ってNuGetの短命トークン取得する4

サンプルのワークフローrelease.yamlは次の通りです。git tagでバージョンタグをプッシュしたときにパッケージを公開します。

name: Publish NuGet package

on:
  push:
    tags:
      - 'v*.*.*' # バージョンタグをプッシュしたときに実行

jobs:
  publish:
    permissions:
      contents: read
      id-token: write # OIDCトークン発行のために必要
    runs-on: ubuntu-24.04
    steps:
    - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
    # ビルド & パック
    - name: dotnet pack
      run: dotnet pack -c Release -o ./bin
    # NuGet/loginアクションでOIDCトークンを使って短命トークンを取得
    - name: NuGet Login
      uses: NuGet/login@d22cc5f58ff5b88bf9bd452535b4335137e24544 # v1.0.0
      id: login
      with:
        user: my-nuget-username # NuGetのユーザー名を指定、Orgアカウントでもユーザー名となる
    # 取得した短命トークンを使ってパッケージを公開
    - name: Push package
      run: dotnet nuget push ./bin/*.nupkg --api-key "${{ steps.login.outputs.NUGET_API_KEY }}" --source https://api.nuget.org/v3/index.json

ワークフローを実行

タグをプッシュすれば、GitHub ActionsがトリガーされてOIDCを使ってNuGetパッケージが公開されます。

制約

2025年10月1日時点で、Reusable WorkflowでNuGet/Loginを使った場合に、NuGetで登録したポリシーを見つけられないことを確認しています。Reusable Workflowを使わずに、直接ワークフローに記述する場合は問題ないので、こちらを利用することをお勧めします。

Error: Token exchange failed (401): No matching trust policy owned by user '***' was found.

これはNuGet側で対応しないといけなさそうな気配があるのでNuGet/loginのIssueで報告しました。今後の対応を待ちましょう。

再現ワークフローです。

# `.github/workflows/nuget-push.yaml@main`
name: Push NuGet
on:
  workflow_call:

jobs:
  create-release:
    permissions:
      contents: write
      id-token: write # required for NuGet Trusted Publish
    runs-on: ubuntu-24.04
    timeout-minutes: 10
    steps:
      - name: NuGet login (OIDC → temp API key)
        uses: NuGet/login@d22cc5f58ff5b88bf9bd452535b4335137e24544 # v1.1.0
        id: login
        with:
          user: my-nuget-user
# 呼び出し元ワークフロー
name: Build-Release

on:
  workflow_dispatch:

jobs:
  dummy:
    permissions:
      contents: write
      id-token: write # required for NuGet Trusted Publish
    uses: .github/workflows/nuget-push.yaml@main

まとめ

NuGetのPAT認証の課題を解決するTrusted Publishingが公開されました。これまではPATローテーション/対応パッケージの指定でわずらわしかったのですが、Trusted Publishingを使って認証管理をシンプルにできます。

とはいえ、認証の境界ラインがGitHub Actionsに移動するためセキュリティがザルだと意味がありません。GitHub Actionsのセキュリティ対策をしっかり行った上で、Trusted Publishingを導入することをお勧めします。2025年現在ならGitHub 2FAからSMSは削除し、Passkeyベースの認証に移行するのがよいでしょう。


  1. APIキーとも呼びますが表記が紛らわしいため、Personal Access Token (PAT)に統一します。
  2. 2025年10月時点
  3. AWSやGoogle Cloud、AzureといったほかのOIDCデプロイと同じように、OIDCトークン発行のために必要
  4. nuget.orgからパッケージ公開トークンを取得し、ステップ変数にセットしてくれます