Repository navigation
b1_04_sigstore_verification_
In the previous chapter, we saw how the Validation Engine (Evaluator) acts like a judge, applying rules from our policy to the evidence (data about our container images). But a crucial question remains: how can the judge trust the evidence?
Imagine someone gives the judge a document claiming a container image passed security scans. How does the judge know this document is real and hasn't been forged or altered? This is where Sigstore Verification comes in.
In the world of software, especially with container images flying around, it's vital to ensure they haven't been tampered with and that any claims made about them (like "this image was built by our official CI/CD pipeline" or "this image has no critical vulnerabilities") are authentic.
Use Case: You want to run ec-cli to check if your quay.io/my-org/backend:v1.2.0 image complies with your company's policy. The policy requires a security scan attestation signed by your official build system. How does ec-cli confirm that the attestation it finds really came from the build system and hasn't been faked?
Sigstore Verification provides the mechanism to check the digital "seals" (signatures) on container images and their associated attestations. It's like a digital notary public, verifying the authenticity and integrity of these software credentials.
ec-cli leverages the tools and concepts from the Sigstore project to perform these checks. Think of Sigstore as a toolbox for signing and verifying software:
-
Digital Signatures: Imagine a unique, unforgeable wax seal on a letter. A digital signature works similarly. It's created using a secret private key and can be verified by anyone using the corresponding public key. If the signature is valid, it proves:
- Authenticity: Who signed the artifact (the owner of the private key).
- Integrity: The artifact hasn't been changed since it was signed.
-
cosign: This is the primary command-line tool (and underlying library used byec-cli) for signing and verifying container images and other artifacts (like attestations). It handles the mechanics of attaching signatures and checking them. -
Fulcio(for Keyless Signing): Traditionally, managing cryptographic keys is complex. Sigstore offers a "keyless" approach. Fulcio is like a temporary certificate office. It can issue short-lived signing certificates based on an identity you already have, like your GitHub account or Google Cloud service account (using OpenID Connect - OIDC). This means you don't need to manage long-term keys yourself for signing. -
Rekor(Transparency Log): This is like a public, tamper-proof ledger or bulletin board. When something is signed (especially using the keyless method), a record of that signing event can be published to Rekor. When verifying,cosign(and thusec-cli) can check Rekor to see if the signature was publicly recorded. This adds another layer of security, making it harder for attackers to hide malicious actions.
ec-cli doesn't perform the cryptographic operations directly. Instead, it uses the libraries provided by cosign to handle the verification heavy lifting. The instructions for how to verify are given in the Policy Definition & Sources.
Remember the policy configuration file from Chapter 2? It contains clues for ec-cli on how to verify signatures:
# my-policy-config.yaml (Simplified)
---
# Option 1: Using a specific Public Key
publicKey: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----
rekorUrl: "https://rekor.sigstore.dev" # Check the public Rekor log
---
# Option 2: Using Keyless Verification (Identity)
identity:
subject: "build-robot@my-company.com" # Expected signer identity
issuer: "https://accounts.google.com" # Where the identity comes from
rekorUrl: "https://rekor.sigstore.dev" # Check the public Rekor log
---
# Option 3: Keyless but ignoring Rekor check
identity:
subjectRegexp: ".*@my-company-ci.iam.gserviceaccount.com" # Regex allowed
issuer: "https://token.actions.githubusercontent.com"
# If ignoreRekor is true, Rekor check is skipped
ignoreRekor: true
---Explanation:
-
publicKey: If provided,ec-cliwill usecosignto check if signatures were made by the corresponding private key. -
identity: IfpublicKeyis not provided,ec-cliexpects keyless signatures. It checks if the short-lived certificate used for signing matches the expectedsubject(e.g., email, service account) andissuer(e.g., Google, GitHub Actions). You can use exact matches (subject,issuer) or regular expressions (subjectRegexp,issuerRegexp). -
rekorUrl: Tellsec-cliwhich Rekor instance to query to see if the signature event was logged. -
ignoreRekor: If set totrue,ec-cliwill skip the Rekor check.
The Flow:
When ec validate image runs:
- It identifies an image from the Application Snapshot.
- It finds associated signatures and attestations in the OCI registry (more on this in the next chapter).
- It reads the verification rules (
publicKeyoridentity,rekorUrl,ignoreRekor) from the Policy Definition & Sources. - It calls the
cosignlibraries to perform Sigstore verification. - If verification passes, the signatures/attestations are considered trustworthy and can be used as input for the Validation Engine (Evaluator).
- If verification fails,
ec-cliflags this as an error. The associated attestations might be ignored or cause the overall validation to fail.
Let's trace how ec-cli might verify an attestation's signature using the keyless method.
sequenceDiagram
participant User
participant ec_cli as ec validate image
participant Policy as Policy Config
participant OCIRegistry as OCI Registry
participant CosignLib as Cosign Library
participant RekorClient as Rekor Client
User->>ec_cli: Runs `ec validate image --images ... --policy policy.yaml`
ec_cli->>Policy: Load policy.yaml (reads identity, rekorUrl)
Policy-->>ec_cli: Identity: subject='ci@org', issuer='github', rekorUrl='...'
ec_cli->>OCIRegistry: Find attestations for image 'img@sha256:abc'
OCIRegistry-->>ec_cli: Found Attestation A (with signature S)
ec_cli->>CosignLib: Verify Attestation A / Signature S with identity & rekorUrl
Note over CosignLib: Checks signature math is correct
Note over CosignLib: Checks certificate matches identity (subject/issuer)
CosignLib->>RekorClient: Is signature S recorded in Rekor? (if !ignoreRekor)
RekorClient-->>CosignLib: Yes, found entry
CosignLib-->>ec_cli: Verification SUCCESSFUL
Note over ec_cli: Attestation A is trusted, pass to Evaluator
Code Connection:
-
Loading Policy Settings:
ec-cliparses the policy configuration to determine the verification method.File:
internal/policy/policy.go(SimplifiedcheckOptsfunction logic)// Simplified logic for creating cosign verification options func checkOpts(ctx context.Context, p *policy) (*cosign.CheckOpts, error) { opts := cosign.CheckOpts{} var err error if p.PublicKey != "" { // If publicKey is set, prepare for key-based verification log.Debug("Using public key verification") opts.SigVerifier, err = signatureVerifier(ctx, p) // Gets the verifier from the key // ... handle error ... } else { // Otherwise, prepare for keyless verification log.Debug("Using keyless verification") opts.Identities = []cosign.Identity{p.Identity()} // Use subject/issuer from policy // Fetch root certificates needed for keyless opts.RootCerts, _ = fulcio.GetRoots() opts.IntermediateCerts, _ = fulcio.GetIntermediates() // ... handle errors ... } opts.IgnoreTlog = p.ignoreRekor // Check if Rekor should be ignored if !opts.IgnoreTlog && p.RekorUrl != "" { // If checking Rekor, create a client opts.RekorClient, err = rekor.NewClient(p.RekorUrl) // ... handle error ... opts.RekorPubKeys, _ = cosign.GetRekorPubs(ctx) // Get Rekor's public keys // ... handle error ... } return &opts, nil }
Explanation: This function reads the
policyobject (which holds data parsed from the policy YAML) and prepares acosign.CheckOptsstructure. This structure tells thecosignlibrary exactly how to verify (use this key, or check this identity, check/ignore Rekor, use this Rekor URL). -
Triggering Verification: The main validation logic uses the prepared
CheckOpts.File:
internal/image/validate.go(SimplifiedValidateImagefunction)func ValidateImage(ctx context.Context, comp app.SnapshotComponent, /*...*/ p policy.Policy, /*...*/) (*output.Output, error) { // ... setup, create application snapshot image 'a' ... // *** Trigger Signature Verification *** // 'a.ValidateImageSignature' and 'a.ValidateAttestationSignature' // will internally use the CheckOpts derived from the policy 'p' // to call cosign library functions. out.SetImageSignatureCheckFromError(a.ValidateImageSignature(ctx)) out.SetAttestationSignatureCheckFromError(a.ValidateAttestationSignature(ctx)) // If AttestationSignatureCheck failed, often return early if !out.AttestationSignatureCheck.Passed { return out, nil } // ... continue with syntax checks and evaluation ... return out, nil }
Explanation: The
ValidateImagefunction calls methods likeValidateAttestationSignatureon the image objecta. These methods use thecosign.CheckOpts(configured based on the policy) to invoke the actual verification routines from thecosignlibrary. -
Using Verification Results: The results (success/failure, verified signatures/attestations) are stored in the
output.Outputobject. Attestations that pass verification are then used by the Validation Engine (Evaluator). -
(Advanced) Rego Integration: Policies written in Rego can also directly invoke Sigstore verification using built-in functions.
File:
internal/rego/sigstore/sigstore.go(Conceptual call)# Example Rego rule snippet import rego.v1 # Get verification options from the policy config (simplified) verification_options := { "certificate_identity": input.policy.identity.subject, "certificate_oidc_issuer": input.policy.identity.issuer, "rekor_url": input.policy.rekorUrl, "ignore_rekor": input.policy.ignoreRekor or false, } # Call the built-in function to verify attestations for the image verify_result := ec.sigstore.verify_attestation(input.imageRef, verification_options) # Deny if verification failed or no valid attestations were found deny contains msg if { not verify_result.success msg := sprintf("Sigstore attestation verification failed for %s: %s", [input.imageRef, concat(", ", verify_result.errors)]) }
Explanation: Rego rules can use functions like
ec.sigstore.verify_attestation, passing the image reference and verification options derived from the policy. This allows complex policy logic to directly incorporate signature checks. The Go code (internal/rego/sigstore/sigstore.go) implements these Rego functions by calling the same underlyingcosignlibraries.
You've learned about Sigstore Verification – ec-cli's mechanism for ensuring trust in container images and attestations. It acts like a digital notary, using tools like cosign, Fulcio, and Rekor to check digital signatures. We saw how the Policy Definition & Sources control whether to use public keys or keyless identity verification, and whether to consult the Rekor transparency log. This verification step is crucial for providing trustworthy evidence to the Validation Engine (Evaluator).
Now that we understand how ec-cli verifies the authenticity of artifacts, let's look closer at how it actually finds and handles these artifacts (images, signatures, attestations) in container registries.
Next up: Image & OCI Artifact Handling.
Generated by AI Codebase Knowledge Builder