enhance(actions): add OIDC workload identity support - #39052
Conversation
Manual validation noteIn addition to the automated test suite, which is currently passing, I have The following scenarios were verified:
The positive end-to-end test also verified the issued token using only the This is mainly a note for tracking the current Draft validation state; I still |
|
Make sure your agent has read AGENTS.md and followed it exactly. Based on this long PR description, I see that it didn't read it. Please re-review the whole work under the AGENTS.md constraints. |
Sorry! I’ve now amended the pull request description in line with the guidelines in AGENTS.md, and I’ve added the screenshots I’d already prepared. I’ll check the entire change once more against the guidelines in AGENTS.md before removing the pull request from draft status. |
|
Thanks, yes that's a more managable size now :) |
| if err := task.Job.LoadRun(ctx); err != nil { | ||
| return false, err | ||
| } | ||
| if task.Job.Run.WorkflowPath == "" { | ||
| return false, nil | ||
| } |
There was a problem hiding this comment.
For migrated schedules, WorkflowPath can legitimately be empty. In that case OIDC is intentionally kept unavailable rather than issuing a token with incomplete workflow provenance.
Would it make sense to log this condition when preparing the runner context? Otherwise, after upgrading, an administrator could see id-token: write but no OIDC runtime variables without an obvious indication why.
| func TokenToTaskID(token string) (int64, error) { | ||
| parsedToken, err := jwt.ParseWithClaims(token, &actionsClaims{}, func(t *jwt.Token) (any, error) { | ||
| if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { | ||
| if t.Method != jwt.SigningMethodHS256 { |
There was a problem hiding this comment.
This narrows Actions token verification to HS256, matching the algorithm Gitea actually uses to issue these tokens. It is defense-in-depth for the OIDC bearer credential path, so I’m happy to drop it if you’d prefer to keep this PR narrower.
|
|
||
| func TestTokenToTaskIDRejectsOtherHMACAlgorithms(t *testing.T) { | ||
| token := jwt.NewWithClaims(jwt.SigningMethodHS384, actionsClaims{TaskID: 23}) | ||
| signed, err := token.SignedString(setting.GetGeneralTokenSigningSecret()) | ||
| assert.NoError(t, err) | ||
| _, err = TokenToTaskID(signed) | ||
| assert.Error(t, err) | ||
| } |
There was a problem hiding this comment.
This test only documents the HS256 restriction above by verifying that an otherwise valid HS384 token is rejected. It can be removed together with that hardening if it is considered out of scope.
| if parentPerms := parentJob.TokenPermissions; parentPerms != nil { | ||
| effectivePerms = repo_model.ClampActionsTokenPermissions(effectivePerms, *parentPerms) | ||
| } else if parentJob.ParentJobID == 0 { | ||
| if repoActionsCfg.OverrideOwnerConfig { | ||
| effectivePerms = repo_model.ClampActionsTokenPermissions(effectivePerms, repoActionsCfg.GetDefaultTokenPermissions()) | ||
| } else { | ||
| effectivePerms = repo_model.ClampActionsTokenPermissions(effectivePerms, ownerActionsCfg.GetDefaultTokenPermissions()) | ||
| } | ||
| } |
There was a problem hiding this comment.
This intentionally clamps the full ActionsTokenPermissions, not only IDTokenAccessMode. The reusable caller chain is part of the canonical effective-permission calculation, so a called workflow cannot exceed its caller for either GITEA_TOKEN or OIDC. Happy to split or narrow this if you'd prefer to keep the PR strictly OIDC-scoped.
|
While reviewing/testing this, I noticed an unrelated existing issue in the OAuth2 JWT signing-key setup: auto-generated ECDSA keys always use P-256, even when I don't think this belongs in this PR. Would you prefer a separate issue for it? I can open one later and look at it independently. |
|
I've now completed my own review of the implementation and tests. The remaining piece is documentation, which I'll add separately. In the meantime, feel free to take a look already — feedback and comments are very welcome. |
|
Also verified that the Gitea Actions runner masks |
47a494c to
de5952f
Compare
Derive token permissions and identity claims from canonical server state, persist authoritative workflow paths, and cover capability access and signing behavior. Assisted-by: OpenCode:gpt-5.6-sol
Legacy schedules can create runs without an authoritative workflow path. Keep OIDC unavailable for these runs, as before OIDC support was introduced, until the schedule is rebuilt. Assisted-by: OpenCode:gpt-5.6-sol
Fail OIDC token issuance when reusable workflow provenance cannot be resolved or does not include the resolved workflow commit. Assisted-by: OpenCode:gpt-5.6-sol
Treat the unsupported id-token read mode as none while preserving write-all and explicit id-token write behavior. Assisted-by: OpenCode:gpt-5.6-sol
Avoid evaluating OIDC eligibility while the provider is unavailable so disabled OIDC cannot interfere with normal task context generation. Assisted-by: OpenCode:gpt-5.6-sol
Cover workflow path persistence for scheduled runs and scoped workflow dispatches. Assisted-by: OpenCode:gpt-5.6-terra
Verify that cyclic reusable workflow parent chains are rejected while computing effective token permissions. Assisted-by: OpenCode:gpt-5.6-terra
Avoid resolving OIDC provenance and effective permissions for jobs that did not explicitly request id-token write access. Assisted-by: OpenCode:gpt-5.6-luna
Verify that reusable workflow callers restrict both repository unit permissions and OIDC token permissions. Assisted-by: OpenCode:gpt-5.6-luna
Keep invalid authorization and task state failures as 401 responses while surfacing unexpected task lookup failures as logged internal server errors. Assisted-by: OpenCode:gpt-5.6-luna
Reject token issuance when the root workflow source commit or reference is missing, and cover root, reusable, scoped, and signing-key provenance cases. Assisted-by: OpenCode:gpt-5.6-terra
Verify the discovery-to-JWKS chain and require the expected bearer challenge for rejected OIDC token requests. Assisted-by: OpenCode:gpt-5.6-luna
8d8ce04 to
a54eaa8
Compare
Add OpenID Connect workload identity support for Gitea Actions.
Workflows can explicitly request
id-token: writeand obtain short-lived OIDC tokens representing their repository and workflow provenance. This enables external services to authenticate Actions workloads without requiring long-lived credentials stored as secrets.This continues and substantially revises the work from #36988, particularly around effective permissions, reusable workflows, authoritative provenance and token issuance security.
UI
Before:
After:
Documentation
https://gitea.com/gitea/docs/pulls/526
Assisted-by: ChatGPT:gpt-5.6-sol