kubernetes/kops · critical
verifying PKCS7 signature: %w
Error message
verifying PKCS7 signature: %w
What it means
This wraps p7.Verify() failing after the PKCS#7 structure parsed successfully. Verify checks the message digest and the embedded certificates' signature chain against the trusted root; failure means the signature over the attested payload is cryptographically invalid or the embedded cert does not chain to the expected Azure root. It is returned by parseAndValidatePKCS7Signer to callers in verifyAttestedDocumentWithRootAndFetcher.
Source
Thrown at upup/pkg/fi/cloudup/azure/attest.go:252
if signature == "" {
return nil, nil, fmt.Errorf("empty PKCS7 signature")
}
sigBytes, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
return nil, nil, fmt.Errorf("decoding PKCS7 signature: %w", err)
}
klog.V(4).Infof("Decoded PKCS7 signature (%d bytes)", len(sigBytes))
p7, err := pkcs7.Parse(sigBytes)
if err != nil {
return nil, nil, fmt.Errorf("parsing PKCS7 signature: %w", err)
}
klog.V(8).Infof("Parsed PKCS7 structure with %d embedded certificate(s)", len(p7.Certificates))
// Verify the PKCS7 signature against the embedded leaf certificate.
if err := p7.Verify(); err != nil {
return nil, nil, fmt.Errorf("verifying PKCS7 signature: %w", err)
}
klog.V(4).Infof("PKCS7 self-signature verified")
signer := p7.GetOnlySigner()
if signer == nil {
return nil, nil, fmt.Errorf("PKCS7 signer certificate not found")
}
klog.V(8).Infof("PKCS7 signer certificate: subject=%q issuer=%q SANs=%v", signer.Subject, signer.Issuer, signer.DNSNames)
if err := validateAzureMetadataSignerSAN(signer); err != nil {
return nil, nil, fmt.Errorf("validating PKCS7 signer SAN: %w", err)
}
klog.V(4).Infof("PKCS7 signer SAN validated as Azure metadata endpoint")
return p7, signer, nil
}
// nonceForBody derives the IMDS attestation nonce from the request body; the shared
// azuremetadata implementation keeps the authenticator and verifier sides identical.View on GitHub (pinned to 4c8573c808)
Solutions
- Check VM system clock (chrony/ntpd) for skew; expired signer certs are the most common cause
- Confirm the trusted root and any intermediates supplied to verifyAttestedDocumentWithRootAndFetcher match the current Azure IMDS signing hierarchy
- Re-fetch the attested document directly from IMDS (169.254.169.254) bypassing proxies to rule out tampering/truncation
- Compare signature verification on a known-good VM to isolate whether the certificate chain or the payload is at fault
Example fix
// before
rootPool := x509.NewCertPool() // missing Azure intermediates
if err := p7.Verify(); err != nil { return err } // "verifying PKCS7 signature: x509: certificate signed by unknown authority"
// after
rootPool := x509.NewCertPool()
rootPool.AddCert(azureRoot)
for _, c := range azureIntermediates {
rootPool.AddCert(c)
}
if err := p7.Verify(); err != nil { return err } Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check: ensure the trust root/intermediates are loaded before calling verifyAttestedDocumentWithRootAndFetcher
if rootPool == nil {
return fmt.Errorf("azure attestation root not initialized")
} Type guard
func certChainLoaded(pool *x509.CertPool, roots ...*x509.Certificate) bool {
if pool == nil {
return false
}
for _, r := range roots {
if r == nil {
return false
}
}
return true
} Try / catch
if err := p7.Verify(); err != nil {
var certErr x509.CertificateInvalidError
if errors.As(err, &certErr) && errors.Is(certErr.Err, x509.Expired) {
klog.Warningf("PKCS7 signer cert expired at %v; check VM clock skew (now=%v)", certErr.NotAfter, time.Now().UTC())
}
return nil, nil, fmt.Errorf("verifying PKCS7 signature: %w", err)
} Prevention
- Keep VM clocks synced (chrony/ntpd) — expired-cert failures are usually clock skew
- Pin and refresh the Azure IMDS root/intermediate certificates as part of routine upgrades
- Fetch attested documents only from 169.254.169.254 inside the VM, never via proxies
- Treat verification failures as security-relevant; log full x509 chain details before failing
When it happens
Trigger: p7.Verify() is invoked on a parsed PKCS7 blob whose SignedData digest does not match the message content, whose signer cert is expired, or whose certificate chain does not validate against the root passed to verifyAttestedDocumentWithRootAndFetcher.
Common situations: Clock skew making the embedded certificate appear expired/not-yet-valid; the attested document was tampered with or replayed from another VM; custom root/fetcher wired with the wrong intermediate (missing Azure intermediate certs); MITM proxy re-signing responses.
Related errors
- validating PKCS7 signer SAN: %w
- decoding PKCS7 signature: %w
- parsing PKCS7 signature: %w
- PKCS7 signer certificate not found
- attested vmId %q does not match %s (API vmId %q)
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/a2b0515e7ceca333.
Report an issue: GitHub.