-
-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix security issues with the Trusted Publishing example #8
Comments
I'm having trouble figuring out the recommended pattern here. Is this the best way to do. it? https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-pypi jobs:
release-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: build release distributions
run: |
# NOTE: put your own distribution build steps here.
python -m pip install build
python -m build
- name: upload windows dists
uses: actions/upload-artifact@v4
with:
name: release-dists
path: dist/
pypi-publish:
runs-on: ubuntu-latest
needs:
- release-build
permissions:
id-token: write
steps:
- name: Retrieve release distributions
uses: actions/download-artifact@v4
with:
name: release-dists
path: dist/
- name: Publish release distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1 In that example the build step happens in an entirely different job, which uploads the built asset as an artifact. Then a separate job with Is there a simple pattern for this? I'm happy to go with that if it's the right thing to do. |
Looks like that's the approach described here too: https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/#checking-out-the-project-and-building-distributions |
Yes, the PyPUG guide is mine. So it already shows what I believe is best: separate jobs, environments etc. So this issue I filed is a part of my effort to present people who land here from Google with better defaults.. |
Here's the new workflow: name: Publish Python Package
on:
release:
types: [created]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip
cache-dependency-path: pyproject.toml
- name: Install dependencies
run: |
pip install '.[test]'
- name: Run tests
run: |
python -m pytest
build:
runs-on: ubuntu-latest
needs: [test]
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
cache-dependency-path: pyproject.toml
- name: Install dependencies
run: |
pip install setuptools wheel build
- name: Build
run: |
python -m build
- name: Store the distribution packages
uses: actions/upload-artifact@v4
with:
name: python-packages
path: dist/
publish:
name: Publish to PyPI
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
needs: [build]
environment: release
permissions:
id-token: write
steps:
- name: Download distribution packages
uses: actions/download-artifact@v4
with:
name: python-packages
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1 I tested it by creating a new package called I'm going to delete that package from PyPI now though since it's effectively useless. |
Before I delete the PyPI package this works:
|
@webknjaz does #8 (comment) look good to you? If so I'll duplicate it in my other cookiecutter templates - |
Is the on:
release:
types: [created] |
What does including the I have to admit I am very confused by GitHub Actions "environments" - I don't understand what they do, or what they are for. |
@simonw so I recommend using specifically GitHub has this thing called Deployments API. It's meant to represent project deployments into different environments. It's integrated with webhooks, and you can add the deployment information via API. When configuring the trust on the PyPI side, you're asked to type in the environment name. If entered, PyPI will match it on upload and reject any attempts not originating from jobs with that environment selected. So this is another security aspect related to the feature. To summarize, the GitHub Environments represent deployment targets — places where you put your dists. So it's only logical to name them for what they are. This is why I'm pushing for If you were to use GitHub Pages with the modern GHA-based publishing method, they auto-create an environment called
N.B. Technically, you don't have to create these environments via GH UI in the repo settings. They are being auto-created on first use @ GHA. However, if you were to apply the protection settings I mentioned above, that would need access to the settings of repositories where you do this. Having them configured in the repository template will not complicate things for the users. If they skip configuring the environments, it'll still work. |
In addition to what I explained above, I also noticed something else that is not strictly required but is usually nice to have — in your example, building the dists happens after testing. This means that your tests are running against something else. Another thing to run continuously is |
https://github.com/simonw/python-lib/blob/4b825ed/%7B%7Bcookiecutter.hyphenated%7D%7D/.github/workflows/publish.yml#L44-L49 suggests that building the dists within the same job that publishes them is okay. But it's not.
Such a structure opens the workflow users to privilege escalation through poisoning the build dependencies, which is why I've always insisted on the separation — the build scripts must never have access to
id-token: write
.Another suggestion is to fix the GitHub Environment name to represent the deployment target as it's meant to. I usually go for
pypi
andtestpypi
so it's obvious that uploading to both is separate.I saw
release
here https://github.com/simonw/python-lib/blob/4b825ed/%7B%7Bcookiecutter.hyphenated%7D%7D/.github/workflows/publish.yml#L33C5-L33C25, which is not an upload target but a process name which is very generic.The declaration syntax can also be extended to include a URL:
The text was updated successfully, but these errors were encountered: