使用可信发布从GitHub Actions轻松发布NuGet包

本文详细介绍了如何利用nuget.org新推出的可信发布功能,通过GitHub Actions工作流安全发布NuGet包,无需生成和存储API密钥,同时提升安全性并简化发布流程。

使用可信发布从GitHub Actions轻松发布NuGet包

在本文中,我将介绍如何使用nuget.org的新功能"可信发布"从GitHub Actions工作流发布NuGet包,无需生成和存储API密钥,同时享受更高的安全性。

当前推送NuGet包的方式

如果你创建NuGet包并将其推送到nuget.org,可能会采用以下几种方式之一:

  • 在本地构建包并手动上传到nuget.org
  • 在CI中构建包,下载后手动上传到nuget.org
  • 在CI中构建包,直接从CI推送到nuget.org

这些方法逐步"优化",通常能提高构建的一致性并减少手动步骤。但从CI推送.nupkg文件到NuGet比通过nuget.org网站在本地上传要"困难"一些。现在你需要:

  • 生成API密钥
  • 在GitHub Actions中安全存储(例如作为Secret)
  • 在GitHub Actions工作流中传递密钥
  • 密钥更改时进行轮换
  • 确保组织中的其他人也能执行相同操作(如果为组织发布包)

这些都不是无法克服的困难,但管理长期密钥的生命周期 notoriously 困难。这就是可信发布的用武之地。

什么是可信发布?

可信发布是一项已在多个生态系统中实施一段时间的倡议。例如Python有PyPI的可信发布,Ruby有RubyGems.org的可信发布,JavaScript有npm的可信发布。上周起,.NET为nuget.org推出了可信发布🎉

可信发布使用现有的身份验证标准(OpenID Connect)将CI基础设施提供商(如GitHub Actions或GitLab Pipelines)与公共包存储库(如PyPI和nuget.org)连接起来。你无需存储和管理API密钥让两个系统相互通信,而是使用OpenID Connect检索短期身份验证令牌,然后使用该令牌将包推送到包存储库。

具体实现因提供商和包存储库而异,但整体流程相同。对于GitHub actions和nuget.org,流程如下:

  1. 用户在nuget.org上配置"信任策略"
  2. 在CI工作流中,从GitHub检索作业的OpenID Connect令牌(GitHub是身份提供商)
  3. 令牌是签名的JSON Web Token(JWT),包含有关仓库和运行工作流的详细信息
  4. 将令牌发送到nuget.org,用令牌交换API密钥
  5. NuGet.org根据配置的信任策略验证JWT内容
  6. 如果一切匹配,nuget.org发出可用于发布包的短期API令牌
  7. CI工作流使用API令牌将.nupkg包推送到nuget.org

由于信任策略是预先配置的,并且你已经通过GitHub身份验证(因其在其基础设施中运行),因此很容易启动和运行。

在NuGet.org上配置可信发布

看到博客文章后,我决定为新的sleep-pc工具尝试一下,因为我还没有为它设置CI。我将首先展示没有NuGet发布的工作流作为基线,然后展示我通过可信发布添加发布的步骤。

工作流起点

没有发布的初始工作流如下所示,存储在文件.github/workflows/BuildAndPack.yml中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
name: BuildAndPack
on:
  push:
    branches: [main]
    tags: ['*']
  pull_request:

jobs:
  build-and-publish:
    permissions:
      contents: read
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 10.0.x

      - name: dotnet pack
        run: |
          dotnet pack
          dotnet pack -r win-x64

这只是做了3件事:

  • 检出仓库
  • 安装.NET 10
  • 运行dotnet pack

值得注意的是,我添加了permissions条目以限制从仓库读取的权限,这是安全最佳实践。这不是必需的,但我添加它主要是为了展示使用可信发布时需要更改的内容。

更新工作流以添加可信发布支持

要添加可信发布,我们需要做三件事:

  1. 添加id-token: write权限
  2. 使用NuGet/login@v1将OIDC令牌交换为NuGet API密钥
  3. 使用生成的API密钥将包推送到NuGet

以下显示更新的工作流,注释突出显示了差异:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
name: BuildAndPack
on:
  push:
    branches: [main]
    tags: ['*']
  pull_request:

jobs:
  build-and-publish:
    permissions:
      contents: read
      id-token: write  # 为此作业启用GitHub OIDC令牌颁发

    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 10.0.x

      - name: dotnet pack
        run: |
          dotnet pack
          dotnet pack -r win-x64

      # 使用环境GitHub令牌登录NuGet并检索API密钥
      - name: NuGet login (OIDC → temp API key)
        uses: NuGet/login@v1
        id: login
        with:
          # Secret是你的NuGet用户名,例如andrewlock
          user: ${{ secrets.NUGET_USER }}

      - name: push to NuGet
        # 仅在构建标签时推送到NuGet(可选)
        if: startsWith(github.ref, 'refs/tags/')
        shell: pwsh
        # 遍历输出文件夹中的所有包并将它们推送到
        # nuget.org,使用上一步login步骤生成的NUGET_API_KEY
        run: |
          Get-ChildItem artifacts/package/release -Filter *.nupkg | ForEach-Object {
            dotnet nuget push $_.FullName `
              --api-key "${{ steps.login.outputs.NUGET_API_KEY }}" `
              --source https://api.nuget.org/v3/index.json
          }

在此工作流中,你需要配置的唯一密钥是NUGET_USER密钥,应将其设置为你的nuget.org用户名(不是电子邮件地址)。考虑到这无论如何都是公共信息,将其存储在密钥中似乎有点过度,但为什么不呢😄

如果你现在运行此工作流,NuGet登录步骤将失败,出现类似以下错误:

1
2
Requesting GitHub OIDC token from: https://run-actions-1-azure-eastus.actions.githubusercontent.com/100//idtoken/aae150a5-757c-411a-b2b0-f82a9b26401f/1943e299-9d47-50e7-8d56-90681c654f24?api-version=2.0&audience=https%3A%2F%2Fwww.nuget.org
Error: Token exchange failed (401): No matching trust policy owned by user '***' was found.

从消息中可以看到,阶段失败是因为我们尚未在nuget.org上配置信任策略,所以这是我们的下一个任务。

在NuGet.org上配置信任策略

配置信任策略非常容易。我们首先在nuget.org登录并导航到可信发布页面:

点击创建以创建新策略,并填写所有必填字段:

  • 策略名称(我认为使用包/GitHub仓库的名称是有意义的)
  • 包所有者(无论是个人账户还是组织)
  • GitHub仓库详细信息(所有者和仓库)
  • 将推送到Nuget的工作流文件

创建信任策略后,它处于"部分"活动状态,直到你使用该策略。

现在如果你再次运行工作流,登录步骤将成功:

1
2
Requesting GitHub OIDC token from: https://run-actions-1-azure-eastus.actions.githubusercontent.com/73//idtoken/21d84c5d-b6a5-49e0-bc04-e46694e083df/b3fac26f-8847-5e96-b7ae-47d656a98f51?api-version=2.0&audience=https%3A%2F%2Fwww.nuget.org
Successfully exchanged OIDC token for NuGet API key.

策略已变为完全活动:

假设其他一切按计划进行,在下一步中,你的包将发布到NuGet,无需长期API密钥🎉

总结

据我所知,截至目前,在nuget.org上使用可信发布除了发布简便和缺乏长期凭据外,没有额外的好处。我当然可以预见未来的额外好处,通过可信发布推送的包将具有额外的验证标记。例如,将来可能与来源证明有一些关联?我猜我们会拭目以待!

概要

在本文中,我讨论了可信发布以及它如何通过避免在GitHub仓库中存储长期凭据来提供帮助。然后我展示了如何使用可信发布将我的sleep-pc包从GitHub仓库配置发布到nuget.org。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计