Repository navigation
feat(repo-settings): release environment for the ccf-release-bot secrets - #78
Merged
Merged
Conversation
RELEASE_BOT_PRIVATE_KEY was an org secret visible to the workflows repo and every mock, so any workflow on any branch of those repos (or a same-repo PR's) could read the key of an app installed on every org repo. The in-workflow "Check the token scope" steps only bound workflows that run them. REPO_ADMIN_PRIVATE_KEY had the same exposure in the workflows repo, guarded only by steps inside repo-settings.yml. - repo-settings manages an environment `release` on every manifest repo: custom deployment policies (the default branch and the v*.*.* / *-v*.*.* release tags, which only the bot creates; other policies are deleted) and the secrets RELEASE_BOT_APP_ID and RELEASE_BOT_PRIVATE_KEY, written when missing (or all with --rotate-secrets) after the policies are in place, encrypted with the environment's public key (nacl sealed box). A secret is never written to an environment whose policy steps failed. - Every job that mints a ccf-release-bot token names the environment: release-please, cut-prerelease and release-finished (the caller's), and this repo's train, renovate, ccf-bump sync/merge, attention digest and vuln summary (main only). The workflow_call secrets are optional, so callers keep `secrets: inherit` and the environment secret wins. - repo-settings.yml runs in a `repo-admin` environment (main only, required reviewers; set up by hand) holding the ccf-repo-admin key and the release-bot values it copies, which only an apply receives. Its token adds Actions read and Environments read/write, so ccf-repo-admin can keep fixed permissions instead of being raised to write for each apply. - internal/ciworkflows: every job that reads an app private key names the environment that holds it. - README: the environments, the app's permissions and the cut-over steps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 59 minutes. View limit detailsLimit details: You’ve used the included review currently available. Review configuration: ⚙️ Run configuration
📒 Files selected for processing (34)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
A called workflow whose jobs all skip reports skipped, not success, so the required job failed on every PR that isn't a release-please PR (where release-checks skips). Skipped is accepted for release-checks only; any other result but success still fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gusfcarvalho
changed the base branch from
ra/sec-repo-settings-hardening
to
main
October 8, 2026 21:26
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #77 (base branch
ra/sec-repo-settings-hardening). From the security review of workflows v1.2.0 and the mock repos.Merging this moves nothing by itself. The secrets move when an admin runs
repo-settingswithapply, following the README cut-over steps (below).Why
RELEASE_BOT_PRIVATE_KEYis an org secret visible toworkflowsand all 10 mocks, with no environment, so any workflow on any branch of those repos can read it, and so can a same-repo PR's. That is the key of an app installed on every org repo, which is also theccf-reviewbypass actor. At go-live,secrets: inheritwould spread it to every product repo.REPO_ADMIN_PRIVATE_KEYhas the same exposure inworkflows. Only steps insiderepo-settings.ymlguard it.Environment secrets fix this. An environment secret only reaches a job that names the environment and runs on a ref the environment allows. A workflow pushed to a branch, or a PR's, is refused before it starts.
What
repo-settingsmanages areleaseenvironment on every manifest repo:v*.*.*and*-v*.*.*. Only the bot can create those tags (ccf-release-tags, from the base PR). Any other policy is deleted.RELEASE_BOT_APP_IDandRELEASE_BOT_PRIVATE_KEYare written when missing, or all of them with--rotate-secrets(new workflow inputrotate-secrets). Values are encrypted with the environment's public key (nacl/boxsealed box,golang.org/x/cryptois now a direct dependency) and never printed.Jobs that mint a ccf-release-bot token now name the environment (
environment: release):release-please,cut-prereleaseandrelease-finished.train,renovate,ccf-bump-sync,ccf-bump-merge,attention-digestandvuln-summary.RELEASE_BOT_*workflow_callsecrets are nowrequired: false, so callers keepsecrets: inheritand need no change. An environment secret wins over an inherited one.repo-settings.yml:repo-adminenvironment, which you create by hand once:mainonly, admins as required reviewers.REPO_ADMIN_*and theRELEASE_BOT_*values to copy; only an apply gets the latter.permission-actions: readandpermission-environments: read|write.ccf-repo-admincan now keep fixed permissions (Administration RW, Environments RW, Actions R), so a future "onboard a repo" dispatch can run end to end.Elsewhere:
internal/ciworkflows: newTestAppKeysOnlyInEnvironments. Every job that reads an app private key must name the environment holding it. I checked that the test fails when one job drops itsenvironment:.Tests
environment_test.go(reposettings):TestGitHubEnvironment: every environment, policy and secret call. The secret is decrypted with the test keypair to check the sealed box. A 403 gets a permission hint.cmd/repo-settings: the end-to-end apply writes the environment, its 3 policies and its 2 secrets, after the other settings.Gate:
go vet ./...,go test ./...,gofmt -l .and actionlint pass. It also merges cleanly with the other three security PRs, and they pass together.Cut-over (README, "Repo settings")
ccf-repo-adminAdministration RW, Environments RW and Actions R. Createworkflows'repo-adminenvironment with the 4 secrets, using a newly generated release-bot private key.releaseenvironment exists, GitHub creates an empty one on first use and the job still gets the org secret, so nothing breaks in between.repo-settingsonrepos.mock.yamland check a mock release end to end, then onrepos.yaml.RELEASE_BOT_*andREPO_ADMIN_*, then delete the old private keys from both apps.Notes
SLACK_BOT_TOKENstays an org secret.notify-failure.ymlneeds it on PR runs.mainnow. A dispatch from another branch is refused.workflows'releaseenvironment each run. It's harmless, but noisy.🤖 Generated with Claude Code