Skip to content

Onboarding a GitHub repo to buildkit-operator

Point a repo's image build at the hot daemons in ~25 lines of workflow. Credentials are set once at the org level, so onboarding a new repo is just dropping in the workflow below.

1. Org-level credentials (once per org)

Set the three mTLS secrets + two vars on the SocialGouv org. The /route credential is not a shared secret: the Action mints a GitHub OIDC id_token natively (the workflow grants permissions: id-token: write), buildd verifies it and derives repo/untrusted from the verified claim — so there is no BUILDKIT_OPERATOR_TOKEN to distribute. --visibility selected --repos <r1,r2,…> opts repos in one at a time (tighter); use --visibility all to let every org repo build.

Needs the admin:org scope: gh auth refresh -h github.com -s admin:org first.

ORG=SocialGouv; REPOS=buildkit-operator-example   # comma-separated as you onboard more
C=deploy/cert/.certs/client                        # the mTLS client material

gh secret   set BUILDKIT_OPERATOR_CA    --org $ORG --visibility selected --repos $REPOS < $C/ca.pem
gh secret   set BUILDKIT_OPERATOR_CERT  --org $ORG --visibility selected --repos $REPOS < $C/cert.pem
gh secret   set BUILDKIT_OPERATOR_KEY   --org $ORG --visibility selected --repos $REPOS < $C/key.pem
gh variable set BUILDKIT_OPERATOR_BUILDD_URL --org $ORG --visibility selected --repos $REPOS --body "http://135.125.57.125:8080"
gh variable set BUILDKIT_OPERATOR_GATEWAY_IP  --org $ORG --visibility selected --repos $REPOS --body "57.128.55.172"

A buildd with no oidc.providers can still take a shared bearer (BUILDKIT_OPERATOR_TOKEN) or the break-glass admin token, passed via the Action's token / admin-token input. Prefer OIDC in every case: a shared bearer proves who is calling but not which project they may build, so any holder can build as — and poison the cache of — any repo. The reference deployment accepts OIDC only.

To onboard another repo later, just add it to --repos on each (re-running gh secret set is fine), or flip to --visibility all.

2. The workflow (per repo)

Drop this in as .github/workflows/build.yml and change only IMAGE. It references the org secrets/vars, so there is nothing repo-specific to configure. Signing runs in this repo's workflow, so the cosign signature attests this repo's identity (per-repo provenance).

name: build
on:
  push:
  workflow_dispatch:
permissions:
  contents: read
  packages: write   # push to GHCR
  id-token: write   # mints the OIDC id_token: cosign keyless AND the /route credential
env:
  IMAGE: ghcr.io/socialgouv/<repo>
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      # buildx forwards this registry auth to the remote daemon, which does the --push.
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: socialgouv/buildkit-operator@v1
        with:
          buildd-url: ${{ vars.BUILDKIT_OPERATOR_BUILDD_URL }}
          gateway-ip: ${{ vars.BUILDKIT_OPERATOR_GATEWAY_IP }}   # until wildcard DNS for the gateway
          ca:    ${{ secrets.BUILDKIT_OPERATOR_CA }}
          cert:  ${{ secrets.BUILDKIT_OPERATOR_CERT }}
          key:   ${{ secrets.BUILDKIT_OPERATOR_KEY }}
          # /route auth is the auto-minted OIDC id_token (id-token: write above; default audience
          # buildkit-operator, override with oidc-audience). No shared token. For a non-OIDC buildd,
          # pass token: ${{ secrets.BUILDKIT_OPERATOR_TOKEN }} or admin-token: instead.
          tags: ${{ env.IMAGE }}:${{ github.sha }}
          push: true
          provenance: mode=max   # SLSA provenance + …
          sbom: true             # … SBOM, generated by the daemon
          sign: true             # cosign keyless-sign the pushed image
      # Optional proof: the signature verifies against THIS workflow's OIDC identity.
      - uses: sigstore/cosign-installer@v3
      - name: verify
        run: |
          set -eux
          cosign verify "${IMAGE}:${{ github.sha }}" \
            --certificate-identity-regexp "(?i)^${{ github.server_url }}/${{ github.repository }}/" \
            --certificate-oidc-issuer https://token.actions.githubusercontent.com >/dev/null
          echo "✅ cosign signature verified"

Monorepo? Add name: <component> to give each image its own daemon + cache. Fork-PR isolation? Add untrusted: true (ephemeral daemon, no cache write-back). All inputs: ci-integration.md.

Notes

  • mTLS client material — the ca/cert/key are the daemon's CA + a client cert from deploy/cert/create-certs.sh (the client/ dir). They authenticate the runner to the daemon through the gateway; the daemon's cert covers *.<gateway-host>.
  • Reusable workflow? A workflow_call reusable workflow would shrink the caller to ~10 lines, but cosign sign from a reusable workflow attributes the signature to the reusable workflow's identity, not the consumer repo's — so the per-repo template above is preferred when signature provenance matters.
  • DNSgateway-ip is the escape hatch while the L4 gateway has no wildcard DNS record; drop it once *.<gateway-host> resolves.