Skip to content

verify: Add trusted_root support for private Sigstore instance verification #768

Description

@trevor-vaughan

🦾 Written with LLM assistance [claude-opus-4-6]
💪 Reviewed by a human before submission


Is your feature request related to a problem? Please describe.

complyctl currently hardcodes the public Sigstore TUF root for keyless verification, making it impossible to verify artifacts signed by a private Sigstore instance (e.g. an organization running its own Fulcio, Rekor, and timestamp authority).

In internal/cache/verify.go:77, NewKeylessVerifier() calls root.FetchTrustedRootWithOptions(opts) at line 83, which fetches the public good instance's trusted root via TUF. There is no mechanism to supply a custom trusted_root.json for a private Sigstore deployment.

The VerificationConfig struct in internal/complytime/config.go:78-87 has fields for Issuer, Identity, and Key, but no TrustedRoot field.

This was discovered during the implementation of #678 - current branch linked while attempting to instrument a fully-contained CI test
environment.

Two acceptance tests in the proposed implementation are currently skipped pending this feature:

  • tests/acceptance/verification_test.go:138TestVerification_Keyless_HappyPath (t.Skip("requires trusted_root support"))
  • tests/acceptance/verification_test.go:184TestVerification_Keyless_WrongIdentity (t.Skip("requires trusted_root support"))

Describe the solution you'd like

  1. Add a trusted_root field to VerificationConfig in internal/complytime/config.go that accepts a file path to a trusted_root.json:

    verification:
      issuer: https://dex.example.com
      identity: signer@example.com
      trusted_root: /path/to/trusted_root.json
  2. Update NewKeylessVerifier() in internal/cache/verify.go to:

    • When trusted_root is provided: load the trusted root from the specified file path using root.NewTrustedRootFromJSON()
    • When trusted_root is absent: continue using root.FetchTrustedRootWithOptions() for the public Sigstore instance (current behavior)
  3. Add Sigstore infrastructure services (dex-idp, fulcio, rekor) to the acceptance test compose stack (tests/acceptance/compose.yaml) under the verification profile, enabling the skipped keyless tests to run against a local Sigstore instance.

  4. Un-skip TestVerification_Keyless_HappyPath and TestVerification_Keyless_WrongIdentity.

Describe alternatives you've considered

  • Environment variable for trusted root path: Simpler but less composable — does not support per-policy-entry verification against different Sigstore instances. The complytime.yaml approach is consistent with the existing per-entry verification: override pattern.

  • Inline trusted root JSON in config: Embedding the full trusted root JSON in complytime.yaml would avoid a file path dependency but would make configs unwieldy and harder to manage.

  • Auto-discovery via .well-known endpoint: Fetching the trusted root from a Sigstore instance's well-known URL would reduce config burden but adds a network dependency and does not work for air-gapped environments.

Additional context

Design history

This was explicitly listed as a non-goal in the acceptance test openspec:

  • openspec/changes/678-policy-complypack-acceptance-tests/design.md:30 — "Implementing trusted_root field (follow-on issue)"

The original sigstore verification design chose the public good instance:

  • openspec/changes/sigstore-verification/design.md:170-178 — Decision 7: "Use sigstore-go's root.NewLiveTrustedRoot() to fetch Sigstore's public good instance trusted root via TUF"

Infrastructure context

The Sigstore compose infrastructure (dex-idp, fulcio, rekor, witness) was previously built for acceptance tests but was removed as dormant when the keyless tests could not run without trusted_root support. Pre-generated test PKI material exists at tests/acceptance/testdata/sigstore/. This feature would re-enable that infrastructure.

Per-entry support

The existing per-entry verification: override pattern (added in the per-entry-verification work) means trusted_root should also be supported at the per-entry level, allowing different policies to be verified against different Sigstore instances:

policies:
  - id: internal-policy
    source: registry.internal/policies/security
    verification:
      issuer: https://dex.internal
      identity: signer@internal
      trusted_root: /path/to/internal-trusted-root.json
  - id: public-policy
    source: ghcr.io/org/policies/compliance
    verification:
      issuer: https://accounts.google.com
      identity: signer@example.com
      # No trusted_root — uses public Sigstore instance

Metadata

Metadata

Labels

enhancementNew feature or requestllm_assistedFiled or drafted with LLM assistance

Type

Fields

Priority

Medium

Effort

Medium

Projects

Status
In progress 📋

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions