Repository navigation
ci(release): scope and lock the signing keychain - #3066
Conversation
The signing jobs read KC_PW as a repository secret, passed it to security on the command line, and left the keychain unlocked when they ended. Both jobs now set `environment: release`, which holds the secret and admits only main and release/* branches. The password goes to security on standard input, with pipefail set so a refused unlock still fails the step. The last step of each job locks the keychain on any outcome. The docs add the release branch step for the signing runner group.
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
|
Approved: no findings at Nothing to do. Clobber: none seen. |
There was a problem hiding this comment.
Review of 803dbe7. I found no blockers. The workflow change is small and correct as far as I can check without a Mac.
I checked these live settings read-only:
- The
releaseenvironment exists. Its branch policies aremainandrelease/*. KC_PWis set on that environment.- The only caller,
nightly.yml, runs onmain, so it is admitted. - The runner doc says there is one runner per Mac, so the new lock step cannot race another job on the same keychain.
Should-fix
1. The repository-level KC_PW still exists, so the environment does not restrict the secret yet. (release.yml:618, :702, :944; docs/internal/release-pipeline.md:62)
gh api repos/gominimal/minimal/actions/secretsstill listsKC_PWbeside the new environment secret.- Any job on any branch that leaves out
environment:still getssecrets.KC_PW. The runner group still limits who can reach the keychain on the Mac. - So the title and the doc line "The
releaseenvironment holdsKC_PW" are not true yet. - The PR body also says
KC_PWis still only a repository secret until it is set on the environment. That is out of date, because the environment secret now exists. - Fix: after the first dry run from
mainpasses, rungh secret delete KC_PW --repo gominimal/minimal, then update the PR body.
2. The doc's runner-group PATCH can reset the group's access settings. (docs/internal/release-pipeline.md:69-70)
-
The jq builds only
{name, selected_workflows}and PATCHes that. -
The "Update a self-hosted runner group" endpoint documents
restricted_to_workflowsandallows_public_repositorieswithDefault: false. If omitted fields fall back to those defaults rather than keeping their current values:restricted_to_workflowsbecomes false, and every workflow on every branch can reach the signing Mac. That undoes the control this PR documents.allows_public_repositoriesbecomes false. This repo is public, so the signing jobs then wait in the queue.
-
This is a hypothesis from the documented defaults. I could not read runner group 3, which needs
admin:org, and I did not run the PATCH. -
Fix: carry the fields through, then have an org owner check the group with a GET:
--jq '{name, visibility, allows_public_repositories, restricted_to_workflows, selected_workflows: (.selected_workflows + ["…@refs/heads/release/X.Y.Z"])}'
Nits
set -o pipefaildoes nothing here. (release.yml:625,:710,:949)- The last command in each pipeline is
security, sobash -ealready fails the step when the unlock is refused. - I checked with
bash -e -c 'printf "%s\n" pw | sh -c "cat >/dev/null; exit 51"; echo reached', which exits 51 without printingreached. - Drop it, or keep it and correct the reason in the PR body.
- The last command in each pipeline is
- The stdin unlock goes through
getpass(). (release.yml:626,:711,:950)- Without
-p,security unlock-keychainreads withgetpass(). On macOS that cuts the password at 128 characters. - It also reads
/dev/ttyinstead of stdin when the process has a controlling terminal. - Confirm that
KC_PWis 128 characters or fewer. A runner started with./run.shfrom Terminal, instead of the LaunchAgent, would wait on the tty. - I have not tested this on macOS.
- Without
docs/internal/release-pipeline.md:66names the wrong permission. It says the PATCH "needs an organization owner". The API asks for an org admin, or the "runners and runner groups" permission, which is whatmacos-runner-setup.md:145already says.docs/internal/release-pipeline.md:472gives no branch for the dry run. It says to test with adry_runrun. After this PR, a dry run from a feature branch fails the two signing jobs on the environment's branch policy. Add "frommainor arelease/branch".- The tracking issue is wrong. "Related: #2812" is closed and is about Secure Enclave keys for BEP. This hardening has no tracking issue.
Not checked
actionlint,zizmorand Vale are not installed here.- Runner group 3's live config, which needs
admin:org. - Anything that needs a real Mac:
- the stdin unlock
lock-keychainwhen the earlier steps never ran- whether a locked keychain that is still on the search list makes a later job on the signing Mac show a GUI unlock prompt
The first dry run from main after merge is the first real proof.
This PR edits .github/workflows/, which is frozen and owner-gated. @mitodrummer signs off on that separately.
There was a problem hiding this comment.
Sign-off from @mitodrummer, the owner, on the .github/workflows/release.yml change, which is frozen and CODEOWNER-gated. Before or right after merge, see the two should-fixes in the review above: delete the repo-level KC_PW once the first dry run from main passes, and carry the access fields through the runner-group PATCH in the doc.
Backports the release.yml part of #3066. Both signing jobs set `environment: release`, the unlock reads the password from standard input with pipefail set, and each job ends by locking the keychain. The one conflict is the build job timeout. This branch keeps its own 60 minutes and its comment, because it does not build bep.
Change
build-release-macos-arm64andsign-macos-artifactssetenvironment: release.KC_PWon standard input, not as an argument.pipefailkeeps a refused unlock a step failure, because the defaultbash -eshell does not set it.if: always()step.release/X.Y.Zbranch in thesigningrunner group.Before and after merge
releaseenvironment exists and admitsmainandrelease/*.KC_PWis still a repository secret, which these jobs can read until it is set on the environment. So this PR does not depend on that move.Verification
actionlintandzizmorpass onrelease.yml.just lint-proseis clean on both docs.",\, a space and$:printf '%s\n' "$PW" | security unlock-keychain "$K"exits 0 for the right password and 51 for a wrong one.Not verified: no release has run this workflow. The environment rule blocks a dry run from this branch, so the first proof is a dry run from
mainafter merge.Related: #2812.
Summary by CodeRabbit