8000 Feature request: Ability to trust OIDC tokens bypassing the OIDC protocol with a JWKS file in the GitHub repository · Issue #841 · octo-sts/app · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Feature request: Ability to trust OIDC tokens bypassing the OIDC protocol with a JWKS file in the GitHub repository #841

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

Open
matheuscscp opened this issue Apr 15, 2025 · 7 comments · May be fixed by #842
Labels
enhancement New feature or request

Comments

@matheuscscp
Copy link
matheuscscp commented Apr 15, 2025

Hi again 👋

A very cool feature of GCP's Workload Identity Federation is the ability to upload a JWKS file for supporting issuers that are not accessible from the Internet for whatever reasons (compliance, etc.), like a Kubernetes cluster with a private API server/OIDC discovery endpoint (e.g. kind running locally). See the GCP docs for this feature:

https://cloud.google.com/iam/docs/workload-identity-federation#oidc-credential-security

It would be very cool if the trust policy had a field jwks where you can specify a multi-line string containing a JWKS JSON document, i.e. the output of a request to https://<issuer>/openid/v1/jwks, like this:

issuer: https://kubernetes.default.svc.cluster.local
audience: https://kubernetes.default.svc.cluster.local
subject: system:serviceaccount:my-app:my-app
jwks: |
  {
    "keys": [
      {
        "use": "sig",
        "kty": "RSA",
        "kid": "LHVGP8kqzN1MuKRMTsroIcR-7hdicXWdpaquEWcAh9Q",
        "alg": "RS256",
        "n": "s5XuFpodwhj6my_gTUHDKbHmQIx-3Tf40OduMZRWlU6_B_nSdjX01kS1UQSGw_G5eVQARooI-tY1vj3bBwn4dEEFa2TlnNnAJca0hj2Izef8A8Uw-mT0fgGI4Hs3xS84Mn_WXNlKXEiPLiFyOGNr0GQBKZDyTps8JUlvnwuWCv1gkzudUHa8B0i8ITSEUclK9_LqZj4zXUAN0Wj_4DVfI_PQ0IHci9K5Q9bgCV0j1EvTsyrwGyLFwyhktUmNhjREAfgYmxvbIRhPSP4YuO2Et1KM7YmjA75cQ9oE3i-QLrOZDripyMRop5RmWttQCEdEWLQWPzBd7aZ5CLbmZuIlIQ",
        "e": "AQAB"
      }
    ]
  }

permissions:
  contents: read

The trust policy above should allow the Kubernetes ServiceAccount my-app from the namespace my-app to read the contents of the GitHub repository.

It would be easy to implement this through oidc.NewVerifier(), like you are already doing for tests. This would require moving the call to s.lookupInstallAndTrustPolicy() up, above the OIDC token verification part in the func (s *sts) Exchange() method.

I'm filing a PR for this 😁

@matheuscscp matheuscscp linked a pull request Apr 16, 2025 that will close this issue
@cpanato
Copy link
Collaborator
cpanato commented Apr 16, 2025

One dumb question: How will it access the Octo-STS app if it is an air-gapped environment? And if the Octo-STS app is installed in the air gap, how will it access GitHub to get the token?

@cpanato cpanato added the enhancement New feature or request label Apr 16, 2025
@matheuscscp
Copy link
Author

You're right, maybe air-gapped is not exactly the case/the best choice of words, but you sure can have e.g. a Kubernetes cluster that lacks a public API server/OIDC discovery endpoint, e.g. GKE. I'll remove this from the issue description and PR content!

@cpanato
Copy link
Collaborator
cpanato commented Apr 16, 2025

We currently use that in Cloud Run and kubernetes; you need to add the service account and give the permissions. You can see the example of a policy here: https://github.com/wolfi-dev/advisories/blob/main/.github/chainguard/lifecycle-adv-triage.sts.yaml

@matheuscscp
Copy link
Author

Oops, you're right! Last time I checked a long time ago it wasn't possible to reach the discovery endpoint of a GKE cluster, maybe they changed it recently or this is just a feature of GKE Autopilot?

I created a token like this:

k create token default -n default
eyJhbGciOiJSUzI1NiIsImtpZCI6IkJmSzU0dkZrOFVxaXpnSlZhMUJmUmZObC1DNWMzbVd3UTFvXy1iQS15QW8ifQ.eyJhdWQiOlsiaHR0cHM6Ly9jb250YWluZXIuZ29vZ2xlYXBpcy5jb20vdjEvcHJvamVjdHMva3ViZXJuZXRlcy1iaWZyb3N0L2xvY2F0aW9ucy91cy1jZW50cmFsMS9jbHVzdGVycy9hdXRvcGlsb3QtY2x1c3Rlci0xIl0sImV4cCI6MTc0NDc5NjQ1OSwiaWF0IjoxNzQ0NzkyODU5LCJpc3MiOiJodHRwczovL2NvbnRhaW5lci5nb29nbGVhcGlzLmNvbS92MS9wcm9qZWN0cy9rdWJlcm5ldGVzLWJpZnJvc3QvbG9jYXRpb25zL3VzLWNlbnRyYWwxL2NsdXN0ZXJzL2F1dG9waWxvdC1jbHVzdGVyLTEiLCJqdGkiOiI3NDMyMGFiZS1iMmMxLTQ1Y2ItOTNjOC1lZTA1Y2IwMDA3ZTIiLCJrdWJlcm5ldGVzLmlvIjp7Im5hbWVzcGFjZSI6ImRlZmF1bHQiLCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoiZGVmYXVsdCIsInVpZCI6IjkzMWQ0OTU2LTg0NzYtNGFlOC1iM2I3LTgxNzhlOWYyNTYzOSJ9fSwibmJmIjoxNzQ0NzkyODU5LCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpkZWZhdWx0In0.m9i2dTM0S_LMAb5BgzzagrAdXmCdaJLgm-kcWezfrqm4Ie31Y2kWIH-Jat71cX71K_qnJUDVRDRfPZnXaKlOsEpVw0gxdWxFPGj5wR-vQzN7w_pNBeCw6gMzr1O0dK-JP4cL077A6UQCB2Hiu1a_ycV2bqFKjiFulW9TeodFqWHB4kvyKkoT6X_oT5ATDE_FhCacojXAIzQ3g3I1l9ZHe98B1XmB0TqomNTYUAW3C7Uy2uK7EZi0b-QyLvY0oi23glBpeNn38GXYkbVbZih_s9FdTEVCmqV1wOXy-0XE4omnO86PVCtMWvXQ54MlIb0N4Ed3S9t3FJyfSnKly0UfTw

The iss claim has this:

"iss": "https://container.googleapis.com/v1/projects/kubernetes-bifrost/locations/us-central1/clusters/autopilot-cluster-1"

Then I curled it:

curl https://container.googleapis.com/v1/projects/kubernetes-bifrost/locations/us-central1/clusters/autopilot-cluster-1/.well-known/openid-configuration
{
  "issuer": "https://container.googleapis.com/v1/projects/kubernetes-bifrost/locations/us-central1/clusters/autopilot-cluster-1",
  "jwks_uri": "https://container.googleapis.com/v1/projects/kubernetes-bifrost/locations/us-central1/clusters/autopilot-cluster-1/jwks",
  "response_types_supported": [
    "id_token"
  ],
  "subject_types_supported": [
    "public"
  ],
  "id_token_signing_alg_values_supported": [
    "RS256"
  ],
  "claims_supported": [
    "iss",
    "sub",
    "kubernetes.io"
  ],
  "grant_types": [
    "urn:kubernetes:grant_type:programmatic_authorization"
  ]
}
curl https://container.googleapis.com/v1/projects/kubernetes-bifrost/locations/us-central1/clusters/autopilot-cluster-1/jwks
{
  "keys": [
    {
      "kty": "RSA",
      "alg": "RS256",
      "use": "sig",
      "kid": "BfK54vFk8UqizgJVa1BfRfNl-C5c3mWwQ1o_-bA-yAo",
      "n": "qN1iYk24aSykF_90B2dum3NEAByFwul_iPAABTSHUxCiqwJv4gJXqehqitS5UadOJQxchYX0ZZkzBAr5L66HZTzaWxl3_AdQFZf0HOgTxvRgvXFKlYKsDwfiT3ezfTCc51xiAQDIshew5PjnwE36hL8oaDob61zCzF-g0Z518vJ97oYgQZikWVJy6uPohlNC4V66bTIBQp70ciQ4ggvOyZnBuMVC0PQR35iQkh_BFShFPvtj4FwJUJiBcMgZ3UdOchGX9ZT48nn14wht6cCLawhb8FEbWMy-OH-3M0kZCsqd2sxIkErUna1gP_dsOPpckdUnqJ49uI4RvUY8e4wSaw",
      "e": "AQAB"
    }
  ]
}

Brilliant!

However, there's plenty of Kubernetes clusters with private API server/OIDC discovery endpoints out there 😁 I worked on a company where we tried to use the API server binary flags --service-account-issuer and --service-account-jwks-uri to customize these URLs in the issued tokens for bare metal clusters with private API servers, and even though it works, we caused a major outage for existing tokens. This idea that GCP implemented for uploading JWKS files is brilliant, and solved the problem at the time.

@mattmoor
Copy link
Member

Yes, the issuer endpoints for GKE, EKS, and AKS (with their v2 identity stuff) are all publicly accessible (and have been for years), I added support to sigstore/fulcio for signing with these a while back, and we have long supported federation with these at Chainguard.

We added support for something similar (in the Chainguard issuer) several years ago to support KinD clusters, which may have outbound networking, but aren't themselves accessible for OIDC discovery (minikube is probably the same story).

I believe that all we would need to do to technically to support this is to add an 8000 optional field to trust policies that allows the repository to specify the JWKS it is willing to accept, and I know the underlying library we use for validation supports accepting a static JWKS to use for validation.

That said, I want to highlight my favorite Jurassic park meme: Your scientists were so preoccupied with whether they COULD do it, then didn't stop to think whether they SHOULD do it.

There are a lot of gotchas needed to "hold this right." Folks would need to not only check in the initial JWKS into their repository, but they would need to update them BEFORE the issuer starts to issue tokens with rotated keys, or STS interactions will fail until the JWKS are rotated in the trust policy. Generally issuers can handle this by providing cache-control headers on the discovery endpoints, but my guess is that 99% of folks using this (or the GCP feature) simply aren't rotating their issuer keys at all 😱

I'm not entirely opposed to this, but...

  1. We should clear on the motivating use case here (e.g. are folks actually looking to use this?),
  2. Perhaps we should enable this functionality under a flag for folks interested in self-hosting instances of the app themselves,
  3. I'd be inclined to NOT enable this in the public good instance without a really good use case and "design-partner user" who wants this in the public good instance.

@matheuscscp
Copy link
Author
matheuscscp commented Apr 19, 2025

Hi @mattmoor, thanks for the feedback here!

Yes, the issuer endpoints for GKE, EKS, and AKS (with their v2 identity stuff) are all publicly accessible (and have been for years)

Then I really can't explain myself 😛

There are a lot of gotchas needed to "hold this right." Folks would need to not only check in the initial JWKS into their repository, but they would need to update them BEFORE the issuer starts to issue tokens with rotated keys, or STS interactions will fail until the JWKS are rotated in the trust policy. Generally issuers can handle this by providing cache-control headers on the discovery endpoints, but my guess is that 99% of folks using this (or the GCP feature) simply aren't rotating their issuer keys at all 😱

Yes, you are exactly right about this, that's a concern I have too... But I really don't know another solution to this problem, happy to brainstorm ideas 🤷 Looks like the Chainguard solution you linked to is exactly the same

I believe that all we would need to do to technically to support this is to add an optional field to trust policies that allows the repository to specify the JWKS it is willing to accept, and I know the underlying library we use for validation supports accepting a static JWKS to use for validation.

Yes, that's what I did in this PR for implementing the feature requested here: #842

You have a good point about having a real use case. As I mentioned in another issue in this repo, I'd be very interested in implementing Octo STS in CNCF Flux (which sure has lots of non-cloud users), we are implementing this RFC there right now for the three major cloud providers. GitHub unfortunately does not support GHCR access through GitHub Apps, though, which limits our interest in actually implementing this... So I really just filed this issue+PR with the intention to simply contribute a cool feature, to be honest. Let's maybe wait for this issue to get upvoted, then?

@mattmoor
Copy link
Member

Gotcha, yeah the Flux use case could be interesting if there's a mutual user interested in that.

I still need to poke at the packages stuff, I could have sworn I played with that (successfully), but it's been a long year 😅

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

Successfully merging a pull request may close this issue.

3 participants
0