Skip to content
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

More clarity on id-token: write #329

Open
simonw opened this issue Apr 1, 2024 · 13 comments
Open

More clarity on id-token: write #329

simonw opened this issue Apr 1, 2024 · 13 comments

Comments

@simonw
Copy link

simonw commented Apr 1, 2024

The README includes this comment:

id-token: write # to verify the deployment originates from an appropriate source

Could we get more information on this?

It looks pretty scary. id-token: write implies that there is some deep and mysterious extra permission being granted to this workflow, potentially involving the ability to make writes and involving a token which might have permission to do other privileged things, maybe even at the account or organization level.

It's really hard to figure out the security impact of this configuration from existing GitHub documentation.

@simonw
Copy link
Author

simonw commented Apr 1, 2024

Related issue full of confused people:

@simonw
Copy link
Author

simonw commented Apr 1, 2024

Another issue about how confusing this is, closed without resolution:

@Lewiscowles1986
Copy link

This is my fault.

As I understand it, any contributor with write access could write to a repo a script which uses that token for nefarious purposes.

It's also very hard to lock-down once you've enabled the github OIDC as an AuthN provider; with a third-party provider.

The AuthZ you configure on the trusted (acting as) user is set, in the external system; so if I were to have a repo and manage it's infrastructure as code with Amazon AWS; then there are not great ways (I think environment is one) to restrict other workflows from say deleting an RDS. Recently a co-worker on a private repo, accidentally deleted a cognito user pool because they forgot to rebase.

Not the type of change or risk I would like to invite for the discussed use-case of deploying github pages sites. The convenience seems to be at the cost of needing to make sure nobody points the barrel at anything you value.

I Believe even less teams will use this responsibly than for novelty purposes; so I'd mentioned publicly to Simon that I am deeply distrustful of public repos using this as a mechanism.

I would like to seek clarification on the claims and grants that id-token: write conveys to the running action.

  • Is it locked to the repo?
  • What guarantees does GitHub provide regarding the lifetime of a token, and it's revocation?
  • Is it expected that the JTI cannot be used to prevent close-timing attacks against accounts with this
  • Is the third party expected to as well as verifying the key, check iat, nbf, perform any kind of handshake with github to validate that the jti is not revoked?

In the same issue Simon just linked, there is a link to some docs which state that none and read are essentially the same thing. Seemingly like a boolean database column that is allowed to be null, two of the represented states are treated as not having access. But what it means to "have access" is also unclear, as if I must use this token type for pushing to github-pages via a github action; I'd like to make sure I understand what else it is capable of internally to GitHub. Can it for example write to another repo; can it be used to enumerate users of a repo, which might be leaked elsewhere to help a more skillful attack?

@simonw
Copy link
Author

simonw commented Apr 1, 2024

Another closed issue about this (apparently I'm collecting them now):

@simonw
Copy link
Author

simonw commented Apr 1, 2024

I filed an issue here with my own attempt at explaining this feature:

@Lewiscowles1986
Copy link

There is even a pages: write. So here is the weird thing. What the heck do I need a token for?

When I use contents: write I don't need an id-token alongside it to commit to a repo, so why does pages: write need id-token: write?

@yoannchaudet
Copy link
Collaborator

👋 I cannot talk for the questions about OIDC itself (though they are good ones and I see you have opened issues against the right repos already to get the confusions addressed).

I can talk about GitHub Pages' specific use case for an OIDC token.

First, you need to be aware of GitHub's automatic token authentication. This is the default GitHub API token (we usually refer as GITHUB_TOKEN) a workflow is given that allows it to do certain operations. It's an opaque token that only GitHub understands.

The id-token: write permission allows the GITHUB_TOKEN to make an API call to generate an OIDC token (which is an extra security token, a standard one). The OIDC token is not dangerous in itself. It is minted and signed by GitHub and includes "claims" (more on that later). What's potentially dangerous is what a third-party may be configured to do with said OIDC tokens.

To start a Pages deployment today, the actions/deploy-pages Action makes an API call. Like all authenticated GitHub APIs, it needs a GitHub API token (e.g. GITHUB_TOKEN) and it requires pages: write permission because we consider a deployment a change to a Pages site.

In the context of building and deploying a Pages site with Actions, a regular token like GITHUB_TOKEN is not sufficient. While the token is opaque it does not include specific "context" Pages needs. If you set the source branch to say gh-pages, how would you prevent a deployment from targeting another branch? There are various ways to handle this but for Pages we decided to leverage OIDC tokens. Claims they come with includes: repo name, environment, branch, etc. A lot of context. The OIDC token is used to make extra security checks (ensure a deployment is being requested via GitHub Actions, that it targets a valid environment and a valid branch when applicable).

In a perfect world, the Pages deploy API would not require an extra token. Unfortunately GitHub's API tokens predates the OIDC specification!

@Lewiscowles1986
Copy link

Thanks for the explainer @yoannchaudet I'll be interested to see what the other permissions of id-token are or if it's just "hey, it's me GitHub". There must be lots more information in there, otherwise AWS etc would be wide open from any github user

@yoannchaudet
Copy link
Collaborator

@Lewiscowles1986 The OIDC token is actually a big "hey it's me GitHub" (I like that image and I think you were spot on). It has no "permissions" associated on its own but it is minted in the context of an Action job and comes with a bunch of context. It's for third-party to decide if a token is allowed to do anything (say do something in AWS or Azure) – it's a trust relationship between GitHub and a third-party. GitHub Pages does use the token (like a third-party) to gate deployment but not alone, it has to be paired with a regular GitHub API token.

They actually document further what the claims look like here which I find useful: https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#understanding-the-oidc-token.

Anyhow, I agree that we can better elaborate about Pages' use of OIDC tokens in this repository. I'll track it on our side (Pages team).

@Lewiscowles1986
Copy link

Lewiscowles1986 commented Apr 2, 2024

Ah I think I see something here that I will get updated.

Looks like job_workflow_ref paired with environment and runner_environment do satisfy things from the situation I was thinking of.

actor also seems like a nice bit of auditing data when paired with actor_id as the usernames are unique, hard to spoof, and even if someone managed a rename, I think their actor_id might be a primary key that an attacker cannot change.

@simonw so from this I think I might be able to lock down a token at the integration level so that third-party actions are unable to use the id-token to escalate from within a step; which next to user-stupidity is a primary concern. What if an actions provider used the fact that my job had an id-token: write to exploit an org.

This definitely isn't immediately obvious; but I also wonder if it's too dry and boring for most users.

@bakoontz2
Copy link

It would also be nice if it was clearly documented that setting id-token:write reverts all other default permission scopes to "none." This is rather inconvenient, as there is no way to say "set id-token to write, but retain all previous permissions" (which could possibly have been set at the workflow level). As it is, using id-token:write requires one to explicitly supply permissions to scopes that were previously set to "read" or "write."

@terryf82
Copy link

It would also be nice if it was clearly documented that setting id-token:write reverts all other default permission scopes to "none." This is rather inconvenient, as there is no way to say "set id-token to write, but retain all previous permissions" (which could possibly have been set at the workflow level). As it is, using id-token:write requires one to explicitly supply permissions to scopes that were previously set to "read" or "write."

If I'm reading these docs correctly, explicitly setting any permission will revoke access to almost everything that you don't set:

When the permissions key is used, all unspecified permissions are set to no access, with the exception of the metadata scope, which always gets read access.

@Lewiscowles1986
Copy link

@terryf82

When the permissions key is used, all unspecified permissions are set to no access, with the exception of the metadata scope, which always gets read access.

Should be in a callout box, everywhere folks are documenting just changing some flags.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants