Allow LOCAL_CA SLSA attestations for private projects
What does this MR do and why?
This MR lets private and internal projects generate SLSA provenance attestations when they are signed with LOCAL_CA. SIGSTORE stays limited to public projects, as ADR 007 requires, because Sigstore records every attestation in the public Rekor transparency log.
SupplyChain.attest_method_allowed?(build)checks whether a build may be attested with the method it resolves to: the project is public, or the method isLOCAL_CA. The other conditions (feature flag,buildstage,ATTEST_*variables) stay inSupplyChain.publish_provenance_for_build?.SupplyChain.publish_provenance_for_build?already letLOCAL_CAbuilds of private projects through, soCi::Slsa::PublishProvenanceWorkerwas enqueued for them, butCi::Slsa::PublishProvenanceServicethen rejected every project that was not public. Both now call the shared rule, so the two checks cannot drift apart. The service keeps its own check because a project's visibility can change between the job finishing and the worker running.- Internal projects follow the same rule as private projects. They are not visible to everyone, so their attestations must not reach Rekor either.
- When the service rejects a build, the message is now
SIGSTORE attestations are only enabled for public projects. - The private
SIGSTOREexample inspec/lib/supply_chain_spec.rbran in the factory's defaultteststage, so it passed on the stage check alone, even with the visibility check removed. It now runs in thebuildstage.
Who this affects. Everything stays behind the slsa_provenance_statement feature flag (default off, https://gitlab.com/gitlab-org/gitlab/-/work_items/547866). Public projects see no change. A private or internal project reaches the new path only when the flag is enabled for it, its job runs in the build stage with ATTEST_BUILD_ARTIFACTS or ATTEST_CONTAINER_IMAGES, and its attestation method resolves to LOCAL_CA, which is the default for these projects. On GitLab.com the flag is currently enabled only for public projects, so nothing changes there until it is enabled for a private one. On GitLab Self-Managed it applies only where an administrator enables the flag.
Merge order. This MR must merge after !264318 (merged), which signs LOCAL_CA attestations with the project CA. Without it, LOCAL_CA builds are still signed keylessly through Sigstore, so a private project would get a permanent public Rekor entry. The two MRs share no files, so this one is based on master and marked as blocked by 264318. If 264318 is reverted, this MR must be reverted too.
The commits can be reviewed one at a time. The first fixes the weak spec, the second adds attest_method_allowed?, and the third uses it in the gate without changing behaviour. The fourth is the only change in behaviour: the service accepts private and internal projects that use LOCAL_CA.
There are no database, API or UI changes. No changelog entry, as the feature flag is off by default.
Tests: spec/lib/supply_chain_spec.rb covers the new rule for public, internal and private projects with SIGSTORE, LOCAL_CA and no ATTEST_METHOD, on and off GitLab.com. spec/services/ci/slsa/publish_provenance_service_spec.rb checks that private and internal projects are rejected with SIGSTORE without calling any publisher, and publish with LOCAL_CA, explicit or by default. Both changed files have full line and branch coverage, and each new example fails when the line it covers is reverted.
References
- Related to https://gitlab.com/gitlab-org/gitlab/-/work_items/603913
- Depends on !264318 (merged)
- Earlier MRs in this stack: !263845 (merged), !263571 (merged), !263213 (merged), !261474 (merged), !261090 (merged), !261751 (merged), !262054 (merged)
- Verification approach: https://gitlab.com/gitlab-org/gitlab/-/work_items/630121
- Epic: https://gitlab.com/groups/gitlab-org/-/work_items/22313
- ADR 007: https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/slsa_level_3/decisions/007_attestations_private_dedicated_self_hosted/
Screenshots or screen recordings
Not applicable, as this is a backend-only change.
How to set up and validate locally
This requires cosign on the Sidekiq host, a runner, and the changes from !264318 (merged).
-
Enable the feature flag for a private project:
project = Project.find_by_full_path('root/slsa-private-test') Feature.enable(:slsa_provenance_statement, project) -
Add these jobs to the project's
.gitlab-ci.ymland run a pipeline:stages: - build build-local-ca: stage: build image: alpine:latest variables: ATTEST_BUILD_ARTIFACTS: "true" script: - echo "local CA, job $CI_JOB_ID" > local-ca.txt artifacts: paths: - local-ca.txt build-sigstore: stage: build image: alpine:latest variables: ATTEST_BUILD_ARTIFACTS: "true" ATTEST_METHOD: "SIGSTORE" script: - echo "sigstore, job $CI_JOB_ID" > sigstore.txt artifacts: paths: - sigstore.txt -
After the jobs finish, check the attestations in a Rails console:
jobs = project.ci_pipelines.order(:id).last.builds.pluck(:name, :id).to_h SupplyChain::Attestation.where(build_id: jobs['build-local-ca']).pluck(:status, :signing_method, :signing_certificate_id) # => [["success", "local_ca", <id of the project CA>]] SupplyChain::Attestation.where(build_id: jobs['build-sigstore']).count # => 0 -
log/application_json.loghas aNot publishing attestations for buildentry for thebuild-sigstorejob withpublic_requirement_met: false.
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.