{"record":{"id":"15f5489f262626bb","repo":"hashicorp/packer","slug":"verify-fulcio-certificate-chain-w","errorCode":null,"errorMessage":"verify Fulcio certificate chain: %w","messagePattern":"verify Fulcio certificate chain: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/attestation/sign_keyless.go","lineNumber":55,"sourceCode":"var newKeylessBundle = sigstoregosign.Bundle\n\nvar newKeylessRekor = func(baseURL string) sigstoregosign.Transparency {\n\treturn sigstoregosign.NewRekor(&sigstoregosign.RekorOptions{BaseURL: baseURL})\n}\n\nvar loadKeylessTrustedMaterial = func(cfg BackendConfig) (sigstoreroot.TrustedMaterial, error) {\n\ttrustedRootPath := strings.TrimSpace(cfg.TrustedRootPath)\n\tif trustedRootPath == \"\" {\n\t\treturn sigstoreroot.FetchTrustedRoot()\n\t}\n\n\treturn sigstoreroot.NewTrustedRootFromPath(trustedRootPath)\n}\n\nvar verifyKeylessCertificate = func(certificate *x509.Certificate, trustedMaterial sigstoreroot.TrustedMaterial, expectedIdentity, expectedOIDCIssuer, trustedRootPath string) error {\n\tchains, err := sigstoreverify.VerifyLeafCertificate(time.Now().UTC(), certificate, trustedMaterial)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"verify Fulcio certificate chain: %w\", err)\n\t}\n\n\t// When using the public Sigstore root (no custom trusted root configured),\n\t// require a valid SCT so certificates issued outside a public CT log are rejected.\n\tif strings.TrimSpace(trustedRootPath) == \"\" {\n\t\tif err := sigstoreverify.VerifySignedCertificateTimestamp(chains, 1, trustedMaterial); err != nil {\n\t\t\treturn fmt.Errorf(\"verify Fulcio certificate SCT: %w\", err)\n\t\t}\n\t}\n\n\tsummary, err := fulciocertificate.SummarizeCertificate(certificate)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"summarize Fulcio certificate: %w\", err)\n\t}\n\n\tidentity, err := sigstoreverify.NewShortCertificateIdentity(expectedOIDCIssuer, \"\", expectedIdentity, \"\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"build keyless identity policy: %w\", err)","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/hashicorp/packer/blob/eb36e3c3e48a036f3e8cc94087636ee72e1303c9/internal/attestation/sign_keyless.go#L37-L73","documentation":"verifyKeylessCertificate wraps sigstore-go's VerifyLeafCertificate, which validates that the Fulcio-issued short-lived certificate in a keyless attestation chains to a trusted Fulcio root within the configured TrustedMaterial. Failure means the leaf certificate's chain, validity window, or issuing root does not match the trusted root (the embedded public Sigstore root by default, or a custom trusted_root.json).","triggerScenarios":"keylessVerifier.Verify is invoked (via newKeylessVerifier/newKeylessVerifierForEnvelope) and sigstoreverify.VerifyLeafCertificate(time.Now(), cert, trustedMaterial) rejects the certificate: untrusted root, expired/not-yet-valid cert, wrong chain, or the trusted root omits the Fulcio CA that issued the cert.","commonSituations":"Verifying an old attestation whose Fulcio cert has expired (typically valid ~10 minutes) — chain trust relies on the recorded Rekor entry/trusted root; using a custom trusted_root.json that lacks the CA which issued the certificate; a stale embedded trusted root that predates a Fulcio CA rotation; system clock skew making the cert appear not-yet-valid or expired.","solutions":["Update to a current trusted root: clear trusted_root_path so FetchTrustedRoot() pulls the latest public Sigstore root, or re-download trusted_root.json ('sigstore fetch-trusted-root' / TUF client).","If using a custom trusted_root_path, ensure it contains the Fulcio CA (and intermediates) that issued the signer's certificate.","Check clock skew on the verifying machine (NTP); certificates are short-lived and 'now' must fall inside the validity window or the timestamp/chain checks fail.","Confirm the attestation bundle is intact and was produced against the same Sigstore ecosystem (public Fulcio vs. a private/enterprise instance — roots must match).","Read the wrapped inner error from sigstore-go; it distinguishes expired cert, unknown issuer, and chain-building failures, which point to different fixes."],"exampleFix":"// before: stale custom trusted root missing current Fulcio CA\n// trusted_root_path = \"old_trusted_root.json\"\n// after: refresh or omit to use the fetched public root\n// trusted_root_path = \"\"  // fetches current Sigstore trusted root via TUF","handlingStrategy":"try-catch","validationCode":"// pre-check: refresh trusted root and confirm clock sanity before verifying\nfunc precheckKeylessEnv(trustedRootPath string) error {\n\tif strings.TrimSpace(trustedRootPath) == \"\" {\n\t\tif _, err := sigstoreroot.FetchTrustedRoot(); err != nil {\n\t\t\treturn fmt.Errorf(\"cannot fetch current Sigstore trusted root: %w\", err)\n\t\t}\n\t} else if _, err := sigstoreroot.NewTrustedRootFromPath(trustedRootPath); err != nil {\n\t\treturn fmt.Errorf(\"cannot load trusted root %s: %w\", trustedRootPath, err)\n\t}\n\tif sk := time.Until(time.Now().UTC().Truncate(time.Hour)); sk > time.Hour || sk < -time.Hour {\n\t\treturn fmt.Errorf(\"system clock appears skewed; sync NTP before verifying\")\n\t}\n\treturn nil\n}","typeGuard":"func isChainError(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"verify Fulcio certificate chain:\")\n}","tryCatchPattern":"err := keylessVerifier.Verify(ctx, payloadType, payload, sig)\nif err != nil {\n\tif strings.Contains(err.Error(), \"verify Fulcio certificate chain\") {\n\t\t// refresh trusted root once, then surface actionable guidance\n\t\treturn fmt.Errorf(\"keyless verification failed: Fulcio cert does not chain to configured trusted root; refresh trusted_root or re-sign the artifact: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Do not pin a stale trusted_root.json; refresh it regularly (TUF) or omit the path to auto-fetch the public root.","Keep verifying hosts NTP-synced — Fulcio certs live only minutes and chain checks are time-sensitive.","Match ecosystems: verify private-Fulcio attestations with that deployment's trusted root, public ones with the public root.","Log the wrapped inner error from sigstore-go; it names the exact chain failure (expired, unknown issuer, no chain)."],"tags":["sigstore","x509","certificate-chain","keyless"],"backgroundTag":"certificate-chain-verification-failed","analyzedSha":"eb36e3c3e48a036f3e8cc94087636ee72e1303c9","analyzedAt":"2026-09-05T13:20:43.127Z","contentChangedAt":"2026-09-05T13:20:43.127Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}