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
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).
durabletask-python/.github/workflows/durabletask.yml
Lines 118 to 123 in e2f0470
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:
Register the PyPI package for Trusted Publishing
Add the environment definition to the
publish-releasejobAdd
id-token: write,contents: writeandpackages: writepermissions to thepublish-releasejobcontents: writeand potentiallypackages: writeRemove
userandpasswordarguments in thepublish-releasejobRemove the
PYPI_API_TOKENproject secret from both the code and the secret store