From 884ebb6fba708dcf780dacfb103df4b4f5f7af81 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Thu, 11 Jan 2024 22:24:09 +0000 Subject: [PATCH 1/5] Google Trusted Publishing docs --- .../trusted-publishers/adding-a-publisher.md | 36 ++++++++++++- .../creating-a-project-through-oidc.md | 10 +++- .../user/trusted-publishers/security-model.md | 16 +++++- .../trusted-publishers/using-a-publisher.md | 51 ++++++++++++++++++- 4 files changed, 109 insertions(+), 4 deletions(-) diff --git a/docs/user/trusted-publishers/adding-a-publisher.md b/docs/user/trusted-publishers/adding-a-publisher.md index d7711c50834a..42813cee1999 100644 --- a/docs/user/trusted-publishers/adding-a-publisher.md +++ b/docs/user/trusted-publishers/adding-a-publisher.md @@ -45,7 +45,41 @@ each. === "Google Cloud" - TODO + For Google Cloud, you **must** provide the email address of the account or + service account used to publish. [You can learn more about Google Cloud + service accounts + here](https://cloud.google.com/iam/docs/service-account-overview). + + For example, if you have created a service account named + "SERVICE_ACCOUNT_NAME" in the project "PROJECT_NAME" which is in use by + the environment where you would like to publish to PyPI from, your service + account email would take the form + `SERVICE_ACCOUNT_NAME@PROJECT_NAME.iam.gserviceaccount.com`, and you would do + the following: + + ![](/assets/trusted-publishing/google/project-publishing-form.png) + + !!! warning + + Google Cloud also provides [default service + accounts](https://cloud.google.com/iam/docs/service-account-types#default) + for various products: + + * Compute Engine: `PROJECT_ID-compute@developer.gserviceaccount.com` + * App Engine: `PROJECT_NAME@appspot.gserviceaccount.com` + + However it is NOT recommended that these be used for publishing, as + they are provided by default to every service when they are created. + + !!! note + + Configuring the subject is optional. The subject is the numeric ID that + represents the principal making the request. While not required, providing the + subject further restricts the identity which is used for publishing, ensuring + that only a specific instance of a service account can publish, not any service + account with the configured email. See + + for more details === "ActiveState" diff --git a/docs/user/trusted-publishers/creating-a-project-through-oidc.md b/docs/user/trusted-publishers/creating-a-project-through-oidc.md index 6a59dd621cc9..4f453311abb8 100644 --- a/docs/user/trusted-publishers/creating-a-project-through-oidc.md +++ b/docs/user/trusted-publishers/creating-a-project-through-oidc.md @@ -41,8 +41,16 @@ provide the name of the PyPI project that will be created. === "Google Cloud" - TODO + If you have a service account named + `SERVICE_ACCOUNT_NAME@PROJECT_NAME.iam.gserviceaccount.com`, which is in use by + the environment where you would like to publish to PyPI from, then you would do + the following: + + ![](/assets/trusted-publishing/google/pending-publisher-form-filled.png) + + !!! note + Like with "normal" trusted publishers, configuring the subject is optional. === "ActiveState" diff --git a/docs/user/trusted-publishers/security-model.md b/docs/user/trusted-publishers/security-model.md index d1acc887d4ee..9344065e6540 100644 --- a/docs/user/trusted-publishers/security-model.md +++ b/docs/user/trusted-publishers/security-model.md @@ -133,7 +133,21 @@ own security model and considerations. === "Google Cloud" - TODO + ### Security Model + + If a trusted publisher is configured for a given PyPI project, any service + that uses the configured service account can request an OpenID Connect token + from Google's identity provider on behalf of that identity. That token can be + exchanged for a PyPI API token with the ability to publish to the PyPI project. + + ### Considerations + + When using trusted publishing with Google Cloud, you must trust the service account + and _any service which uses it as the default ephemeral identity_. + + Specifically, it is not recommened to configure the [default service + accounts](https://cloud.google.com/iam/docs/service-account-types#default), as + they are provided by default to every service when they are created. === "ActiveState" diff --git a/docs/user/trusted-publishers/using-a-publisher.md b/docs/user/trusted-publishers/using-a-publisher.md index f1ee0b004509..9d32d82017b8 100644 --- a/docs/user/trusted-publishers/using-a-publisher.md +++ b/docs/user/trusted-publishers/using-a-publisher.md @@ -212,7 +212,56 @@ below describe the setup process for each supported trusted publisher. === "Google Cloud" - TODO + You can use the tool to automatically detect + and produce OIDC credentials on Google Cloud services. + + First, ensure that `id` and `twine` are installed in the environment you + plan to publish from: + + ``` + $ python -m pip install -U id twine + ``` + + If you're unsure what the email address is for the service account your + service is using, you can verify it with: + + ``` + $ python -m id pypi -d | jq 'select(.email) | .email' + ``` + + Generate an OIDC token from within the environment and store it. The + audience should be either `pypi` or `testpypi` depending on which index you are + publishing to: + + ``` + $ oidc_token=$(python -m id pypi) + ``` + + **NOTE**: `pypi` is only correct for PyPI. For TestPyPI, the correct + audience is `testpypi`. More generally, you can access any instance's expected + OIDC audience via the `{index}/_/oidc/audience` endpoint: + + ```console + $ curl https://pypi.org/_/oidc/audience + {"audience":"pypi"} + ``` + + Finally, we can submit that token to PyPI and get a short-lived API token + back: + + ```bash + resp=$(curl -X POST https://pypi.org/_/oidc/mint-token -d "{\"token\": \"${oidc_token}\"}") + api_token=$(jq '.token' <<< "${resp}") + ``` + + **NOTE**: This is the URL for PyPI. For TestPyPI, the correct + domain should be is `test.pypi.org`. + + This API token can be fed into `twine` or any other uploading client: + + ```bash + TWINE_USERNAME=__token__ TWINE_PASSWORD="${api_token}" twine upload dist/* + ``` === "ActiveState" From 11c6c803017b614f5a023b46ca621b9f1703dce9 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Fri, 12 Jan 2024 07:45:33 -0500 Subject: [PATCH 2/5] Update docs/user/trusted-publishers/security-model.md Co-authored-by: Facundo Tuesca --- docs/user/trusted-publishers/security-model.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/user/trusted-publishers/security-model.md b/docs/user/trusted-publishers/security-model.md index 9344065e6540..e02ce76e6574 100644 --- a/docs/user/trusted-publishers/security-model.md +++ b/docs/user/trusted-publishers/security-model.md @@ -139,6 +139,8 @@ own security model and considerations. that uses the configured service account can request an OpenID Connect token from Google's identity provider on behalf of that identity. That token can be exchanged for a PyPI API token with the ability to publish to the PyPI project. + The identity used for publishing can be optionally constrained further by + by specifying the subject, an ID that represents the principal making the request. ### Considerations From cb81bd593078b051a6215c0ea44a79fdd140abff Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Fri, 12 Jan 2024 08:12:02 -0500 Subject: [PATCH 3/5] Update docs/user/trusted-publishers/security-model.md Co-authored-by: Facundo Tuesca --- docs/user/trusted-publishers/security-model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/trusted-publishers/security-model.md b/docs/user/trusted-publishers/security-model.md index e02ce76e6574..226d0330ae9a 100644 --- a/docs/user/trusted-publishers/security-model.md +++ b/docs/user/trusted-publishers/security-model.md @@ -140,7 +140,7 @@ own security model and considerations. from Google's identity provider on behalf of that identity. That token can be exchanged for a PyPI API token with the ability to publish to the PyPI project. The identity used for publishing can be optionally constrained further by - by specifying the subject, an ID that represents the principal making the request. + specifying the subject, an ID that represents the principal making the request. ### Considerations From b0c8d734bb4f9bb74dacff9c5c44fbd0f79fcbe4 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Fri, 12 Jan 2024 14:48:05 -0500 Subject: [PATCH 4/5] Apply suggestions from code review Co-authored-by: William Woodruff --- .../trusted-publishers/adding-a-publisher.md | 2 +- .../trusted-publishers/using-a-publisher.md | 21 +++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/user/trusted-publishers/adding-a-publisher.md b/docs/user/trusted-publishers/adding-a-publisher.md index 42813cee1999..a75d39ad8db6 100644 --- a/docs/user/trusted-publishers/adding-a-publisher.md +++ b/docs/user/trusted-publishers/adding-a-publisher.md @@ -68,7 +68,7 @@ each. * Compute Engine: `PROJECT_ID-compute@developer.gserviceaccount.com` * App Engine: `PROJECT_NAME@appspot.gserviceaccount.com` - However it is NOT recommended that these be used for publishing, as + However it is **not** recommended that these be used for publishing, as they are provided by default to every service when they are created. !!! note diff --git a/docs/user/trusted-publishers/using-a-publisher.md b/docs/user/trusted-publishers/using-a-publisher.md index 9d32d82017b8..82deb7afa804 100644 --- a/docs/user/trusted-publishers/using-a-publisher.md +++ b/docs/user/trusted-publishers/using-a-publisher.md @@ -237,14 +237,15 @@ below describe the setup process for each supported trusted publisher. $ oidc_token=$(python -m id pypi) ``` - **NOTE**: `pypi` is only correct for PyPI. For TestPyPI, the correct - audience is `testpypi`. More generally, you can access any instance's expected - OIDC audience via the `{index}/_/oidc/audience` endpoint: + !!! note + `pypi` is only correct for PyPI. For TestPyPI, the correct + audience is `testpypi`. More generally, you can access any instance's expected + OIDC audience via the `{index}/_/oidc/audience` endpoint: - ```console - $ curl https://pypi.org/_/oidc/audience - {"audience":"pypi"} - ``` + ```console + $ curl https://pypi.org/_/oidc/audience + {"audience":"pypi"} + ``` Finally, we can submit that token to PyPI and get a short-lived API token back: @@ -254,8 +255,10 @@ below describe the setup process for each supported trusted publisher. api_token=$(jq '.token' <<< "${resp}") ``` - **NOTE**: This is the URL for PyPI. For TestPyPI, the correct - domain should be is `test.pypi.org`. + !!! note + + This is the URL for PyPI. For TestPyPI, the correct + domain should be is `test.pypi.org`. This API token can be fed into `twine` or any other uploading client: From 81253d6d6c22b11cab419020bf905edfa812b629 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Fri, 12 Jan 2024 19:18:03 -0500 Subject: [PATCH 5/5] Apply suggestions from code review --- docs/user/trusted-publishers/using-a-publisher.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/user/trusted-publishers/using-a-publisher.md b/docs/user/trusted-publishers/using-a-publisher.md index 82deb7afa804..171d1d63c9ff 100644 --- a/docs/user/trusted-publishers/using-a-publisher.md +++ b/docs/user/trusted-publishers/using-a-publisher.md @@ -252,7 +252,7 @@ below describe the setup process for each supported trusted publisher. ```bash resp=$(curl -X POST https://pypi.org/_/oidc/mint-token -d "{\"token\": \"${oidc_token}\"}") - api_token=$(jq '.token' <<< "${resp}") + api_token=$(jq -r '.token' <<< "${resp}") ``` !!! note @@ -263,7 +263,7 @@ below describe the setup process for each supported trusted publisher. This API token can be fed into `twine` or any other uploading client: ```bash - TWINE_USERNAME=__token__ TWINE_PASSWORD="${api_token}" twine upload dist/* + TWINE_USERNAME=__token__ TWINE_PASSWORD=${api_token} twine upload dist/* ``` === "ActiveState"