描述#
开源库构成了世界数字基础设施的重要组成部分。因此,保障开源供应链 (OSSC) 安全越来越受到关注,针对生态系统的复杂攻击事件(例如,2024 年的 xz
工具后门)和 PyPI 上的恶意软件攻击 突显了必须认真对待供应链安全的重要性。Python 软件基金会 (PSF) 也非常重视 OSSC,这一点体现在 2023 年创建 PSF 安全驻留开发人员职位 上。
随着 软件工件供应链级别 (SLSA) 框架 和 OpenID Connect (OIDC) 标准 得到广泛采用,由专业安全团队维护的几个高级开发人员工具已经创建,并提供了明确的使用建议。
本 SPEC 概述了采用这些安全工具的实用建议,以及有关如何安全发布版本工件的建议。安全地构建版本工件将在以后的 SPEC 中介绍。这套建议补充了 SPEC 6 — 要塞钥匙 中的建议。
虽然本 SPEC 是针对 GitHub 编写的,但相同的建议也适用于其他服务,例如 GitLab。
徽章#
项目可以通过包含 SPEC 徽章来突出显示其对本 SPEC 的采用。
[](https://scientific-python.cn/specs/spec-0008/)
|SPEC 8 — Securing the Release Process|
.. |SPEC 8 — Securing the Release Process| image:: https://img.shields.io/badge/SPEC-8-green?labelColor=%23004811&color=%235CA038
:target: https://scientific-python.cn/specs/spec-0008/
实施#
专注于保障版本工件分发流程的安全,应采用以下流程和标准。
记录发布流程#
发布流程应在开发者文档中清晰完整地记录,并描述发布的每个步骤以及执行这些步骤所需的权限。建议在文档网站的开发者部分专门创建一个页面,尽管在存储库顶层提供 RELEASING.md
中的说明也是一种常见方法。
加强工作流环境权限#
-
发布版本工件的工作流应具有运行触发器,这些触发器需要发布团队的有意操作(例如,GitHub Actions 中的
workflow_dispatch
),并且需要发布团队的多个成员批准工作流才能运行(参见下面的“使用 GitHub Actions 环境”部分)。这样做是为了防止任何一个维护者能够提交到默认分支并直接进行发布。 -
还强烈建议存储库要求 签署提交,以便每个版本都对应一个经过验证的提交。
-
创建版本的那个分支也应受到保护。
将 CI 运行器的权限限制在最低限度#
为了限制 CI 运行器中任意代码执行的攻击面,应将默认运行器权限限制在最低限度(读取访问权限)。在 GitHub Action 工作流中,这是通过在定义任何作业之前定义以下工作流全局权限块来实现的。
permissions:
contents: read
应在作业级别重新定义权限块来提升超出此范围的权限。
限制工作流中允许的操作#
GitHub 允许通过 https://github.com/$ORG/$PROJECT/settings/actions
处的存储库操作权限设置来限制工作流可以使用哪些操作。一个合理的默认设置是选择
- 允许
$ORG
,并选择non-$ORG
,操作和可重用工作流
选项以及子选项
- 允许 GitHub 创建的操作
- 允许指定的操作和可重用工作流
有关更多详细信息,请参阅 管理存储库的 GitHub Actions 权限。
使用 GitHub Actions 环境#
environment:
name: publish-package
并强制执行至少一名其他发布团队维护者的额外审查,以运行发布到 PyPI 的 GitHub Actions 工作流。可以在 https://github.com/$ORG/$PROJECT/settings/environments/
中的“部署保护规则”部分为每个 GitHub Actions 环境配置其他审阅者要求。
将 GitHub Actions 发布工作流固定到其完整的发布提交 SHA#
必须使用与正在使用的发布版本相对应的完整提交 SHA 固定 GitHub 操作。使用版本或小哈希容易受到攻击。
- uses: actions/some-action@1fe14e04876783b259436247a3898d2fe7d5548f #vX.Y.Z
Dependabot 可用于自动更新哈希。重要的是,这必须作为审查流程的一部分进行。
# .github/dependabot.yml
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
groups:
actions:
patterns:
- "*"
通过使用 GitHub 证明采用 SLSA#
SLSA 的一个组成部分是 软件证明,它允许对软件工件和来源进行公开验证。GitHub 提供了 actions/attest-build-provenance
GitHub Action,它实现了 SLSA 以生成工作流工件的签名构建来源证明。证明发布到项目 GitHub 的 https://github.com/$ORG/$PROJECT/attestations/
下。
- uses: actions/attest-build-provenance@<full action commit SHA> # vX.Y.Z
with:
subject-path: "dist/<package name>-*"
GitHub 还将 gh attestation verify
命令添加到 GitHub CLI 实用程序中,该命令使用其关联的加密签名证明来验证工件的完整性和来源。这可供个人用户使用,也可在默认安装了 GitHub CLI 实用程序的 GitHub Actions 工作流中使用。
- name: Verify artifact attestation
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
for artifact in dist/*; do
echo "# ${artifact}"
gh attestation verify "${artifact}" --repo ${{ github.repository }}
done
通过使用 PyPI 可信发布者采用 OIDC#
可信发布者 提供了一种在项目存储库和分发平台(如 PyPI)之间安全建立短期身份验证令牌的方法。它取代了使用长期令牌进行身份验证的需要,从而降低了与身份验证令牌相关的安全风险(例如,令牌被泄露、需要轮换令牌)。
可信发布者可通过在 GitHub Actions 中使用 pypa/gh-action-pypi-publish
GitHub Action 的默认值在 GitHub Actions 环境中使用。
jobs:
publish:
name: Publish release to PyPI
runs-on: ubuntu-latest
environment: publish-package
permissions:
# IMPORTANT: this permission is mandatory for trusted publishing
id-token: write
steps:
# retrieve your distributions here
# ...
- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@<full action commit SHA> # vX.Y.Z
with:
print-hash: true
工作流示例#
以下是一个完整的工作流示例,可用作起点
name: publish distributions
on:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
publish:
name: Publish Python distribution to PyPI
runs-on: ubuntu-latest
permissions:
id-token: write
attestations: write
environment:
name: publish-package
steps:
# - name: Collect built artifacts
# ...
- name: Generate artifact attestation for sdist and wheels
uses: actions/attest-build-provenance@<full action commit SHA> # vX.Y.Z
with:
subject-path: "dist/<package name>-*"
- name: Verify artifact attestation
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
for artifact in dist/*; do
echo "# ${artifact}"
gh attestation verify "${artifact}" --repo ${{ github.repository }}
done
- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@<full action commit SHA> # vX.Y.Z
with:
print-hash: true