Skip to content

Commit

Permalink
Add guides around trusted publishing
Browse files Browse the repository at this point in the history
  • Loading branch information
segiddins committed Dec 11, 2023
1 parent 49fa369 commit 1df2895
Show file tree
Hide file tree
Showing 14 changed files with 151 additions and 1 deletion.
9 changes: 8 additions & 1 deletion _layouts/default.html
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
<li><a class="nav--v__link" href="/patterns">Patterns</a></li>
<li><a class="nav--v__link" href="/specification-reference">Specification Reference</a></li>
<li><a class="nav--v__link" href="/command-reference">Command Reference</a></li>
<li><a class="nav--v__link" href="http://docs.seattlerb.org/rubygems">RubyGems API</a></li>
<li><a class="nav--v__link" href="https://docs.seattlerb.org/rubygems">RubyGems API</a></li>
<li><a class="nav--v__link" href="/rubygems-org-api">RubyGems.org API</a></li>
<li><a class="nav--v__link" href="/rubygems-org-api-v2">RubyGems.org API V2.0</a></li>
<li><a class="nav--v__link" href="/rubygems-org-rate-limits">RubyGems.org rate limits</a></li>
Expand All @@ -114,6 +114,13 @@
<li><a class="nav--v__link" href="/credits">Credits</a></li>
<li><a class="nav--v__link" href="/cve">Common Vulnerabilities and Exposures</a></li>
<li><a class="nav--v__link" href="/releasing-rubygems">Releasing RubyGems</a></li>
<li><a class="nav--v__link" href="/trusted-publishing">Trusted Publishing</a></li>
<ul class="nav--v">
<li><a class="nav--v__link" href="/trusted-publishing/adding-a-publisher">Adding to an existing gem</a></li>
<li><a class="nav--v__link" href="/trusted-publishing/pushing-a-new-gem">Pushing a new gem</a></li>
<li><a class="nav--v__link" href="/trusted-publishing/releasing-gems">Releasing gems</a></li>
</ul>

</ul>
</div>
<div class="l-colspan--r">
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/trusted-publishing/profile-gem-list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 36 additions & 0 deletions trusted-publishing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
layout: default
title: Trusted Publishing
url: /trusted-publishing
---

Trusted Publishing is a term for using OpenID Connect (OIDC) to exchange short-lived identity tokens between a trusted third-party service and RubyGems.org.
This allows obtaining short-lived API tokens in an automated environment (such as CI) without having to store long-lived API tokens or username/password credentials.

For a quickstart guide, see:

- [Adding a trusted publisher to an existing gem]()
- [Pushing a new gem with a trusted publisher]()

## How it works

Trusted publishing is a mechanism for uploading gems to PyPi without using long-lived secret credentials.

You don't need to be an OIDC expert to use trusted publishing, but it's helpful to understand the basics of how it works.

1. Certain platforms, such as GitHub Actions, are OIDC _identity providers_, meaning they can issue short-lived identity tokens that third parties can **strongly** verify came from the CI service (as well as the repository, workflow, and commit that triggered the build).
1. Gems on RubyGems.org can be configured to trust particular configurations from particular providers, making that configuration a trusted publisher for that gem.
1. Release automation (such as GitHub Actions) can exchange the identity token for a short-lived API token from RubyGems.org, provided the token matches any trusted publishers that have been configured on RubyGems.org.
1. The API token can be used only to push to the gems that are configured to trust the publisher, and only for a short period of time.

This mechanism has significant security & usability advantages compared to traditional authentication mechanisms:

- **Usability**: trusted publishing does not require manually creating & storing API tokens from RubyGems.org. The only manual step is configuring the trusted publisher on RubyGems.org.
- **Security**: RubyGems.org's normal API tokens are long-lived, meaning an attacker who obtains one can use it indefinitely. Trusted publishing tokens are short-lived, meaning they can only be used for a short period of time.

## Further reading

We highly reccomend checking out the excellent docs written by our friends over at PyPi for some more in-depth information on how Trusted Publishing works:

- [PyPi: Security model and considerations](https://docs.pypi.org/trusted-publishers/security-model/)
- [PyPi: Internals and Technical Details](https://docs.pypi.org/trusted-publishers/internals/)
34 changes: 34 additions & 0 deletions trusted-publishing/adding-a-publisher.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
layout: default
title: Adding a trusted publisher to an existing gem
url: /trusted-publishing/adding-a-publisher
---

Adding a trusted publisher to a gem only requires a single setup step.

On [your profile page](https://rubygems.org/), click the link to any gem you'd like to configure.

![List of gems on a RubyGems.org profile](/images/trusted-publishing/profile-gem-list.png){:class="t-img"}

If you're a gem owner, you'll see a link to "Trusted publishers" on the right side of the page. Click that link.

![Links shown on the sidebar of a gem page when the user is an owner](/images/trusted-publishing/gem-owner-sidebar-links.png){:class="t-img t-img--small"}

This will take you to the gem's trusted publishers page.

![Gem's trusted publisher page with a create button](/images/trusted-publishing/rubygem-trusted-publisher-create.png){:class="t-img"}

Click the "Create" button, which will take you to the publisher configuration page.

![Gem trusted publisher creation form](/images/trusted-publishing/rubygem-trusted-publisher-form.png){:class="t-img"}

To enable trusted publishing for this gem, you'll need to tell RubyGems.org how to trust it.
GitHub Actions (the only currently supported trusted publisher) requires the repository owner's name, repository name, and the name of the workflow that will be pushing the gem.

Once you click "Create Rubygem trusted publisher", your publisher will be registered and will appear in the list of trusted publishers for this gem.

![List of configured gem trusted publishers](/images/trusted-publishing/rubygem-trusted-publishers-index.png){:class="t-img"}

Now, the `push.yml` workflow on `indirect/indirect-trusted-publishing` will be able to generate short-lived API tokens from RubyGems.org that are able to push to this gem.

A publisher can be registered for multiple gems, and a gem can have multiple publishers.
36 changes: 36 additions & 0 deletions trusted-publishing/pushing-a-new-gem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
layout: default
title: Pushing a new gem with a trusted publisher
url: /trusted-publishing/pushing-a-new-gem
---

Trusted publishers are not just for existing gems, they can also be used to push new gems!

This helps reduce the friction for setting up fully automated publishing workflows for new gems,
since the same workflow will work for the first released version of a gem as well as all future versions.

To set up a trusted publisher for a new gem, you'll need to set up a "pending" trusted publisher
under your RubyGems.org profile.

The process is the same as for [adding a trusted publisher to an existing gem](/trusted-publishing/adding-a-publisher),
except that you'll also need to specify a gem name.

To configure a pending trusted publisher, go to your [pending trusted publisher page](https://rubygems.org/profile/oidc/pending_trusted_publishers)

![User's pending trusted publisher page with a create button](/images/trusted-publishing/pending-trusted-publisher-create.png){:class="t-img"}

Click the "Create" button, which will take you to the publisher configuration page.

![Pending trusted publisher creation form](/images/trusted-publishing/pending-trusted-publisher-form.png){:class="t-img"}

For example, if you have a repository at `https://github.com/rubygems/sample-gem` with a release workflow at `push_gem.yml` and an environment named `release` that you would like to push to RubyGems.org as the `sample-gem` gem, you would enter the following values:

![Pending trusted publisher creation form with values filled in](/images/trusted-publishing/pending-trusted-publisher-form-filled.png){:class="t-img"}

Once you click "Create Pending trusted publisher", your publisher will be registered and will appear in the list of pending publishers for your account.

![List of configured pending trusted publishers](/images/trusted-publishing/pending-trusted-publishers-index.png){:class="t-img"}

From this point, the "pending" publisher will act like a "normal" publisher.
After its first successful push, it will be converted to a "normal" trusted publisher for the new gem,
and you will be added as the owner of the gem.
37 changes: 37 additions & 0 deletions trusted-publishing/releasing-gems.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
layout: default
title: Releasing gems with a trusted publisher
url: /trusted-publishing/releasing-gems
---

Once you have a trusted publisher configured, you can use RubyGems' [`configure-rubygems-credentials`](https://github.com/rubygems/configure-rubygems-credentials) GitHub Action to set up your workflow to push gems to RubyGems.org.

This looks almost exactly the same as normal, except that you don't need any explicit usernames, passwords, or API tokens: GitHub's OIDC identity provider will take care of everything for you:

```yaml
jobs:
push:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
bundler-cache: true
ruby-version: 3.2.2
- name: Configure RubyGems credentials
uses: rubygems/configure-rubygems-credentials@segiddins/support-trusted-publishers
- name: Set remote URL
run: |
git config --global user.email "$(git log -1 --pretty=format:'%ae')"
git config --global user.name "$(git log -1 --pretty=format:'%an')"
git remote set-url origin "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY"
- name: Release
run: bundle exec rake release
```
Note the `id-token: write`` permission: you **must** provide this permission at either the job level (strongly recommended) or workflow level (discouraged). Without it, the publishing action won't have sufficient permissions to identify itself to RubyGems.org.

0 comments on commit 1df2895

Please sign in to comment.