Skip to content

enhance(actions): add OIDC workload identity support - #39052

Open
0xMax42 wants to merge 14 commits into
go-gitea:mainfrom
0xMax42:feat/actions-oidc
Open

enhance(actions): add OIDC workload identity support#39052
0xMax42 wants to merge 14 commits into
go-gitea:mainfrom
0xMax42:feat/actions-oidc

Conversation

@0xMax42

@0xMax42 0xMax42 commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Add OpenID Connect workload identity support for Gitea Actions.

Workflows can explicitly request id-token: write and 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:

Bildschirmfoto_20260822_190337

After:

Bildschirmfoto_20260822_190410

Documentation

https://gitea.com/gitea/docs/pulls/526


Assisted-by: ChatGPT:gpt-5.6-sol

@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Aug 22, 2026
@0xMax42

0xMax42 commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

Manual validation note

In addition to the automated test suite, which is currently passing, I have
manually tested the current implementation on a Gitea instance.

The following scenarios were verified:

  • regular workflow with id-token: write → OIDC runtime context is available
    and a token can be minted successfully
  • regular workflow with id-token: none → OIDC runtime context is absent
  • regular workflow with id-token: read → OIDC runtime context is absent
  • repository-level maximum permission disabling OIDC → runtime context is
    absent even when the workflow requests id-token: write
  • re-enabling OIDC at repository level → token issuance works again
  • cross-repository reusable workflow with id-token: write → token issuance
    succeeds and root/reusable workflow provenance is reported separately and
    correctly
  • reusable workflow where the caller has id-token: none while the called
    workflow requests id-token: write → OIDC runtime context is absent, i.e.
    the called workflow cannot elevate the caller's permission

The positive end-to-end test also verified the issued token using only the
public OIDC discovery document and JWKS, including signature, issuer, audience,
lifetime, repository/ref/SHA claims, and workflow provenance.

This is mainly a note for tracking the current Draft validation state; I still
intend to perform my own complete code/security review before marking the PR
ready for review.

@silverwind

silverwind commented Aug 22, 2026

Copy link
Copy Markdown
Member

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.

@0xMax42 0xMax42 changed the title feat(actions): add OIDC workload identity support enhance(actions): add OIDC workload identity support Aug 22, 2026
@0xMax42

0xMax42 commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

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.

@silverwind

Copy link
Copy Markdown
Member

Thanks, yes that's a more managable size now :)

Comment thread services/actions/oidc.go
Comment on lines +93 to +98
if err := task.Job.LoadRun(ctx); err != nil {
return false, err
}
if task.Job.Run.WorkflowPath == "" {
return false, nil
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread services/actions/auth.go
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 {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +65 to +72

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)
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@silverwind silverwind added the topic/gitea-actions related to the actions of Gitea label Aug 22, 2026
Comment on lines +63 to +71
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())
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@0xMax42

0xMax42 commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

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 ES384 or ES512 is configured. This appears to make those algorithms fail when using an automatically generated key.

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.

@0xMax42

0xMax42 commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

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.

@0xMax42

0xMax42 commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

Also verified that the Gitea Actions runner masks ACTIONS_ID_TOKEN_REQUEST_TOKEN in job logs as expected, including when environment variables are printed directly.

@0xMax42
0xMax42 force-pushed the feat/actions-oidc branch from 47a494c to de5952f Compare August 23, 2026 17:52
@0xMax42
0xMax42 marked this pull request as ready for review August 23, 2026 17:56
@github-actions github-actions Bot added the type/enhancement An improvement of existing functionality label Aug 23, 2026
lunny and others added 13 commits August 23, 2026 21:54
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
@0xMax42
0xMax42 force-pushed the feat/actions-oidc branch from 8d8ce04 to a54eaa8 Compare August 23, 2026 19:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. topic/gitea-actions related to the actions of Gitea type/enhancement An improvement of existing functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants