ci: zero-trust GitHub Actions CI/CD template - #1
Conversation
- Read-only default permissions (per-job opt-in writes) - Reusable _build.yml (OS x Python x Go matrix) and _security.yml - OIDC-only deploy.yml (AWS/GCP/Azure stanzas + Tailscale WIF) - Release pipeline: GHCR push, cosign keyless signing, SLSA provenance - Nightly scheduled security sweep - CodeQL extended+quality, gitleaks, Trivy fs, Syft SBOM, actionlint, zizmor - step-security/harden-runner on every job, egress audit enabled - All third-party actions pinned to immutable commit SHAs - Dependabot weekly grouped updates (actions/pip/go/docker) - CODEOWNERS, SECURITY.md, PR template, issue templates - persist-credentials: false on every checkout
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4c8be2d8e5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| permissions: | ||
| contents: read | ||
| security-events: write # upload SARIF to code scanning | ||
| actions: read | ||
| packages: read |
There was a problem hiding this comment.
Grant attestation permissions for CI security runs
The security caller job here does not grant id-token: write or attestations: write, but _security.yml includes an SBOM attestation step (actions/attest-sbom) for non-PR events. In reusable workflows, called jobs cannot elevate token scopes beyond what the caller grants, so CI runs on push/tags will hit permission errors in the SBOM job and fail the required-check gate.
Useful? React with 👍 / 👎.
| preflight: | ||
| name: Preflight | ||
| uses: ./.github/workflows/_build.yml | ||
| permissions: |
There was a problem hiding this comment.
Validate the requested ref in deploy preflight
For workflow_dispatch, the workflow accepts a ref to deploy, but preflight only calls _build.yml with defaults and does not test that requested ref. The deploy job later checks out inputs.ref, so a manually dispatched run can deploy a commit/tag that was never validated by preflight in that same run.
Useful? React with 👍 / 👎.
| persist-credentials: false | ||
| - name: actionlint | ||
| run: | | ||
| bash <(curl -fsSL https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) 1.7.7 |
There was a problem hiding this comment.
Pin actionlint installer to an immutable source
This step executes a script fetched from .../main/... at runtime, which is a mutable branch head. That breaks reproducibility and creates a supply-chain risk because upstream changes can alter CI behavior without any change in this repository. Use a pinned tag/commit URL (or a pinned action) and verify integrity.
Useful? React with 👍 / 👎.
What
Adds a complete zero-trust CI/CD stack for
tailscale-guard:.github/workflows/ci.yml.github/workflows/_build.yml.github/workflows/_security.yml.github/workflows/deploy.yml.github/workflows/release.yml.github/workflows/scheduled-security.ymlmain..github/actions/setup-toolchain/.github/dependabot.yml.github/CODEOWNERS,SECURITY.md, PR + issue templatesZero-trust defaults
permissions: read-allat workflow root; every job opts in to the narrowest write scope it needs.GITHUB_TOKENread-only (matches the repo-level setting you should flip under Settings → Actions → General).# vX.Y.Ztrailing comment; first-partyactions/*andgithub/codeql-action/*on major tag (dependabot-managed).persist-credentials: falseon everyactions/checkout— nothing written to.git/config.step-security/harden-runneron every job (egress-policy: audit); the deploy job has an explicit allowed-endpoints list you can flip toblockonce stable.AssumeRoleWithWebIdentity, GCP Workload Identity Federation, Azure federated credential, and Tailscale Workload Identity Federation (no OAuth client secret needed).Required one-time setup
See
.github/workflows/README.mdfor the complete runbook. Key steps:stagingandproduction(required reviewers on prod).main: requireRequired checks, signed commits, linear history, code owners.deploy.ymland set the repo Variables.TS_OAUTH_CLIENT_ID+TS_AUDIENCEsecrets andTS_DEPLOY_TARGETvariable.CODECOV_TOKENsecret if private.Validation
actionlint 1.7.7— passes with zero findings across all 6 workflows.python -m yamlparse — OK on all YAML files.Threat-model coverage
OWASP Top 10 CI/CD · SLSA v1.0 build L3 · NIST SSDF (SP 800-218) · MITRE ATT&CK T1195 (supply-chain compromise) · CWE-829 (inclusion from untrusted source).