Add cosign trusted root creation to ProvenancePublisher
What does this MR do and why?
Adds ProvenancePublisher#cosign_create_trusted_root(ca_certificate:, dir:), a private method next to cosign_import_key_pair and cosign_create_signing_config that builds a cosign trusted root from the project's CA certificate.
The verification approach was agreed in https://gitlab.com/gitlab-org/gitlab/-/work_items/630121 (Option B): the leaf certificate goes in the bundle, and cosign gets the project CA as a trusted root via --trusted-root. Without --trusted-root, cosign attest-blob --certificate leaf.pem signs but skips the certificate chain check, so the trusted root has to be passed.
Not called yet, and the whole flow is behind the slsa_provenance_statement feature flag (https://gitlab.com/gitlab-org/gitlab/-/work_items/547866). https://gitlab.com/gitlab-org/gitlab/-/work_items/603913 will wire this in, alongside the offline signing config from !261751 (merged). Related: !261090 (merged) (with_local_ca_signing_material).
No database, API, UI or docs changes. Only the public CA certificate is used, never SigningCertificate#private_key.
Tests: new describe block in spec/lib/supply_chain/provenance_publisher_spec.rb with 8 examples, covering the exact cosign command, the CA file contents and 0600 mode, that dir is validated before cosign runs, a nil dir, a comma in dir, a PEM string and a nil certificate, a comma in the issuer, and a cosign failure. Full file: 32 examples, 0 failures.
Manual testing: tested end to end in a GDK box with real cosign v3.1.3 and v3.1.1, signing and verifying with the Option B commands from https://gitlab.com/gitlab-org/gitlab/-/work_items/630121. All checks passed on both versions.
References
- Closes https://gitlab.com/gitlab-org/gitlab/-/work_items/633459
- Stacked on: !261474 (merged)
- Sibling MR: !261751 (merged) (https://gitlab.com/gitlab-org/gitlab/-/work_items/633460)
- Related: !261090 (merged)
- Wiring issue: https://gitlab.com/gitlab-org/gitlab/-/work_items/603913
- No cosign wrapper classes, review decision: https://gitlab.com/gitlab-org/gitlab/-/work_items/603914#note_3995581547
- Verification approach decision: 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, backend only.
How to set up and validate locally
With cosign installed (brew install cosign):
-
In a Rails console, build a CA and a leaf certificate, import the leaf key pair, then create the trusted root:
project = Project.first driver = SupplyChain::PostgresCaDriver.new(project) ca_certificate = driver.ca_certificate leaf = driver.generate_leaf_certificate(ci_config_ref_uri: "#{project.full_path}//.gitlab-ci.yml@refs/heads/main") # allocate skips the build and JWT setup, which these two methods don't use publisher = SupplyChain::ProvenancePublisher.allocate dir = Dir.mktmpdir File.write(File.join(dir, 'leaf.pem'), leaf.certificate.to_pem) imported = publisher.send(:cosign_import_key_pair, key: leaf.key, dir: dir) # writes cosign.key publisher.send(:cosign_create_trusted_root, ca_certificate: ca_certificate, dir: dir) # => "<dir>/trusted-root.json" puts dir, imported[:password], Gitlab.config.gitlab.url puts leaf.certificate.extensions.find { |e| e.oid == 'subjectAltName' }.value # the SAN, after "URI:" -
From a shell in
dir, withCOSIGN_PASSWORDset to the password from step 1, create a test artifact, a predicate and an offline signing config, then sign:echo "hello" > artifact.txt echo '{"buildDefinition":{"buildType":"manual-test"}}' > predicate.json cosign signing-config create --out signing-config.json cosign attest-blob --key cosign.key --certificate leaf.pem --trusted-root trusted-root.json \ --signing-config signing-config.json --predicate predicate.json --type slsaprovenance1 \ --bundle good.sigstore.json --yes artifact.txt -
Verify the bundle with the same trusted root:
cosign verify-blob-attestation --bundle good.sigstore.json --trusted-root trusted-root.json \ --certificate-identity <SAN> --certificate-oidc-issuer <ci_server_url> \ --type slsaprovenance1 --insecure-ignore-tlog --insecure-ignore-sct artifact.txt -
Run the specs:
bin/rspec spec/lib/supply_chain/provenance_publisher_spec.rb
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.