Skip to content

Keyless GCP token for BMC assimilation: WIF-federated scoped SA + GHA auth step (apply-ready) - #5676

Merged
briansrls merged 8 commits into
mainfrom
session/merry-badger-255
Jun 23, 2026
Merged

briansrls merged 8 commits into
mainfrom
session/merry-badger-255

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Jun 23, 2026 •

Copy link
Copy Markdown
Contributor

Goal

Make the .dag effect shell.GCloud.AuthPrintAccessToken() resolve on the self-hosted GitHub Actions runner with no human pasting a token — the keyless path for unattended BMC assimilate jobs. Today a pasted ~1h token is the only path, which blocks any unattended assimilate job.

The .dag effect and its output wire are unchanged. Token acquisition is host/CI config behind the effect. The single token source stays shell.GCloud.AuthPrintAccessToken().access_token (the seam the interpreter consumer binds to — confirmed with @neat-boar-71). WIF only changes how gcloud is authenticated on the runner.

What's here (apply-ready, NOT live-applied)

SA/WIF creation needs operator GCP-admin creds. This PR delivers the exact apply-ready artifacts + the in-substrate modeling:

  • extdeps/github/actions.dag — WorkflowPermissions.id_token + google_auth_action (google-github-actions/auth@v2).
  • workflow_yaml_project.dag — emit the id-token permission.
  • extdeps/cloud/gcp/iam.dag — ground roles/secretmanager.secretVersionAdder in common_roles (was missing; no nickname, DESIGN §3).
  • gunbc/assimilate/bmc_token_federation.dag — single authority for the identity facts, the two resource-level secret bindings (accessor + versionAdder on bmc-srv3-admin only), the WIF pool/provider/principalSet pinned to gunb-ai/gunbc, the keyless auth step, and an emittable smoke workflow.
  • docs/runbooks/bmc-assimilator-wif-setup.md — exact gcloud commands (SA, 2 resource-level grants, WIF pool+provider with repo attribute-condition, SA impersonation binding), the GHA auth step, a scoped-SA-key local fallback, and least-privilege verification commands.

Least privilege

One secret (bmc-srv3-admin), two secretmanager.* roles, nothing project-wide, no admin. Blast radius = one secret.

Proven by execution

dsl/test/claim/bmc_token_federation_witness_test.dag — 6 witnesses, all PASS:

  • least-privilege: exactly 2 bindings, both roles/secretmanager.*, one secret, one project, the SA member
  • no broad/owner/editor/admin role (fail-closed, reds if privilege widens)
  • WIF provider resource + principalSet repo-pinning well-formed
  • emitted workflow carries id-token: write + the auth uses/with + gcloud auth print-access-token
  • emitted workflow carries no credentials_json/key (fail-closed against re-introducing a pasted key)

ci.yml verified byte-identical (id_token is Absent in the floor workflow → emits nothing).

🤖 Generated with Claude Code

briansrls and others added 2 commits June 23, 2026 21:15
…tep (apply-ready)

Make shell.GCloud.AuthPrintAccessToken() resolve on the self-hosted GitHub
Actions runner with no pasted token. The .dag effect is unchanged; this is the
host/CI config behind it plus the apply-ready GCP infra the operator runs.

- extdeps/github/actions.dag: WorkflowPermissions.id_token + google_auth_action
- workflow_yaml_project.dag: emit id-token permission
- extdeps/cloud/gcp/iam.dag: ground roles/secretmanager.secretVersionAdder
- gunbc/assimilate/bmc_token_federation.dag: identity facts, 2 resource-level
  secret bindings (single authority), WIF provider/principalSet, keyless auth
  step + smoke workflow emit
- docs/runbooks/bmc-assimilator-wif-setup.md: exact apply-ready gcloud/WIF/GHA
  artifacts + scoped-key local fallback + least-privilege verification

Least-privilege (one secret, two secretmanager roles, no project-wide) and
keyless emission proven by execution in
dsl/test/claim/bmc_token_federation_witness_test.dag (6 witnesses, all PASS).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review June 23, 2026 21:24
@gunbai-bot gunbai-bot Bot changed the title GCP token automation for BMC assimilation: scoped service account + Workload Identity Federation (keyless) so the .dag AuthPrintAccessToken() effect resolves on the GitHub Actions runner WITHOUT pasted tokens. Deliverable = (1) a dedicated SA (e.g. bmc-assimilator@gunbai-secrets) with RESOURCE-LEVEL Keyless GCP token for BMC assimilation: WIF-federated scoped SA + GHA auth step (apply-ready) Jun 23, 2026
briansrls and others added 6 commits June 23, 2026 21:27
…onstants + records

Addresses review #5676 (claude-opus-4-7) single-authority findings:
- bindings derive member (from sa_email), secret, project from the data
  constants via bmc_secret_binding() — no re-typed literals
- role names sourced from iam.dag (role_secretmanager_secret_accessor /
  _version_adder), which common_roles now references too — one authority,
  and the new secretVersionAdder row gains a consumer
- WIF resource-path + principalSet projection moved to reusable helpers in
  extdeps/cloud/gcp/gcp.dag (wif_provider_resource / wif_principal_set_for_repo),
  derived from the WifProvider/WifPool records; removed the ad-hoc concat
  ladders and duplicate scalar id/issuer data rows

7 witnesses still PASS; allowlist discriminating-red unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
extdeps.ctrl.jobserver (from #5663) declared its external-authority anchor as
`ctrl_jobserver_authority`, but the host-fed clean-tree scanner
(extdeps_shape_transport_policy_project::read_external_authority_anchor_from_items)
requires the exact name `extdeps_external_authority_anchor` — so the module read
as anchor-Absent and corpus_live_clean_tree_holds / extdeps_external_authority_gate
have been RED on main since #5663, masked by the self-hosted runner CI backlog
(main runs never completed). Surfaced on this PR's floor run.

One-line rename to the convention every other extdeps module follows; anchor var
was unreferenced. corpus_live_clean_tree_holds + corpus_live_anchored_modules_clean
now PASS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@briansrls
briansrls merged commit 2c4f755 into main Jun 23, 2026
2 checks passed
@briansrls
briansrls deleted the session/merry-badger-255 branch June 23, 2026 23:58
gunbai-bot Bot pushed a commit that referenced this pull request Jun 24, 2026
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

Successfully merging this pull request may close these issues.

1 participant