Skip to content

Switch to Trusted Publishing for package upload to PyPI in CI #139

@lmmx

Description

@lmmx

Following the breach (#137) in which a PyPI credential was stolen and used to republish a malicious package, the appropriate remedy is to remove the use of PyPI credentials and switch to Trusted Publishing.

Trusted publishing (with attestations) is currently suggested as a security practice to ensure confidence that what users download from PyPI is the same artifact that was generated in GitHub CI, meaning that what I see in GitHub is the same as what is installed - handy for auditing. It also avoids the presence of long-lived PyPI tokens as 'secrets' in the CI, which can be exfiltrated by bad actors (as has occurred).

- name: Publish package to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} # Store your PyPI API token in GitHub Secrets
run: |
twine upload dist/*

Following the recent supply chain attack on this package, I would strongly advise this code be changed and the stored PyPI API token/GitHub secret removed, not merely rotated.

Instead of using the stored secret token, GitHub vouches for the upload, and PyPI checks the uploading CI workflow filename against a pre-declared filename you set ahead of time (in durabletask’s case the filename is durabletask.yml). It also requires creating an 'environment' for the repo on GitHub (suggested name "pypi") which you register the package with on PyPI.

Steps to implement:

Instead of installing twine directly in the CI runner, use the pypa/gh-action-pypi-publish action which handles the Trusted Publishing for you

  • Register the PyPI package for Trusted Publishing

  • Add the environment definition to the publish-release job

  • Add id-token: write, contents: write and packages: write permissions to the publish-release job

    • specifying any permission restricts the token, and creating a GitHub release requires contents: write and potentially packages: write
  • Remove user and password arguments in the publish-release job

  • Remove the PYPI_API_TOKEN project secret from both the code and the secret store

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions